diff --git a/adf_core_python/core/agent/__init__.py b/adf_core_python/core/agent/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/action/__init__.py b/adf_core_python/core/agent/action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/action/ambulance/__init__.py b/adf_core_python/core/agent/action/ambulance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/action/common/__init__.py b/adf_core_python/core/agent/action/common/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/action/fire/__init__.py b/adf_core_python/core/agent/action/fire/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/action/police/__init__.py b/adf_core_python/core/agent/action/police/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/agent.py b/adf_core_python/core/agent/agent.py index 297f42a4..4d41918f 100644 --- a/adf_core_python/core/agent/agent.py +++ b/adf_core_python/core/agent/agent.py @@ -1,27 +1,348 @@ import sys -from typing import Any, NoReturn +from abc import abstractmethod +from typing import Any, Callable, NoReturn -from rcrs_core.agents.agent import Agent as RCRSAgent +from bitarray import bitarray +from rcrs_core.commands.AKClear import AKClear +from rcrs_core.commands.AKClearArea import AKClearArea +from rcrs_core.commands.AKLoad import AKLoad +from rcrs_core.commands.AKMove import AKMove +from rcrs_core.commands.AKRescue import AKRescue +from rcrs_core.commands.AKRest import AKRest +from rcrs_core.commands.AKSay import AKSay +from rcrs_core.commands.AKSpeak import AKSpeak +from rcrs_core.commands.AKSubscribe import AKSubscribe +from rcrs_core.commands.AKTell import AKTell +from rcrs_core.commands.AKUnload import AKUnload +from rcrs_core.commands.Command import Command +from rcrs_core.config.config import Config as RCRSConfig +from rcrs_core.connection.URN import Command as CommandURN +from rcrs_core.connection.URN import ComponentCommand as ComponentCommandMessageID +from rcrs_core.connection.URN import ComponentControlMSG as ComponentControlMessageID +from rcrs_core.connection.URN import Entity as EntityURN +from rcrs_core.messages.AKAcknowledge import AKAcknowledge +from rcrs_core.messages.AKConnect import AKConnect +from rcrs_core.messages.controlMessageFactory import ControlMessageFactory +from rcrs_core.messages.KAConnectError import KAConnectError +from rcrs_core.messages.KAConnectOK import KAConnectOK +from rcrs_core.messages.KASense import KASense +from rcrs_core.worldmodel.changeSet import ChangeSet +from rcrs_core.worldmodel.entityID import EntityID from rcrs_core.worldmodel.worldmodel import WorldModel -from adf_core_python.core.logger.logger import get_logger +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team import ( + MessageAmbulanceTeam, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_building import ( + MessageBuilding, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_civilian import ( + MessageCivilian, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade import ( + MessageFireBrigade, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_police_force import ( + MessagePoliceForce, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_road import ( + MessageRoad, +) +from adf_core_python.core.agent.communication.standard.standard_communication_module import ( + StandardCommunicationModule, +) +from adf_core_python.core.agent.config.module_config import ModuleConfig +from adf_core_python.core.agent.develop.develop_data import DevelopData +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import Mode, ScenarioInfo +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData +from adf_core_python.core.component.communication.communication_module import ( + CommunicationModule, +) +from adf_core_python.core.config.config import Config +from adf_core_python.core.launcher.config_key import ConfigKey +from adf_core_python.core.logger.logger import get_agent_logger, get_logger -class Agent(RCRSAgent): - def __init__(self, is_precompute: bool, name: str) -> None: +class Agent: + def __init__( + self, + is_precompute: bool, + name: str, + is_debug: bool, + team_name: str, + data_storage_name: str, + module_config: ModuleConfig, + develop_data: DevelopData, + ) -> None: self.name = name self.connect_request_id = None self.world_model = WorldModel() - self.config = None + self.config: Config self.random = None - self.agent_id = None + self.agent_id: EntityID self.precompute_flag = is_precompute self.logger = get_logger( f"{self.__class__.__module__}.{self.__class__.__qualname__}" ) + self.team_name = team_name + self.is_debug = is_debug + self.is_precompute = is_precompute + + if is_precompute: + # PrecomputeData.remove_date(data_storage_name) + self.mode = Mode.PRECOMPUTATION + + self._module_config = module_config + self._develop_data = develop_data + self._precompute_data = PrecomputeData(data_storage_name) + self._message_manager: MessageManager = MessageManager() + self._communication_module: CommunicationModule = StandardCommunicationModule() + + def get_entity_id(self) -> EntityID: + return self.agent_id + + def set_send_msg(self, connection_send_func: Callable) -> None: + self.send_msg = connection_send_func + + def post_connect(self) -> None: + if self.is_precompute: + self._mode = Mode.PRECOMPUTATION + else: + # if self._precompute_data.is_ready(): + # self._mode = Mode.PRECOMPUTED + # else: + # self._mode = Mode.NON_PRECOMPUTE + self._mode = Mode.NON_PRECOMPUTE + + self.config.set_value(ConfigKey.KEY_DEBUG_FLAG, self.is_debug) + self.config.set_value( + ConfigKey.KEY_DEVELOP_FLAG, self._develop_data.is_develop_mode() + ) + self._ignore_time: int = int( + self.config.get_value("kernel.agents.ignoreuntil", 3) + ) + self._scenario_info: ScenarioInfo = ScenarioInfo(self.config, self._mode) + self._world_info: WorldInfo = WorldInfo(self.world_model) + self._agent_info = AgentInfo(self, self.world_model) + self.logger = get_agent_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}", + self._agent_info, + ) + + self.logger.info(f"config: {self.config}") + + def update_step_info( + self, time: int, change_set: ChangeSet, hear: list[Command] + ) -> None: + self._agent_info.record_think_start_time() + self._agent_info.set_time(time) + + if time == 1: + self._message_manager.register_message_class(0, MessageAmbulanceTeam) + self._message_manager.register_message_class(1, MessageFireBrigade) + self._message_manager.register_message_class(2, MessagePoliceForce) + self._message_manager.register_message_class(3, MessageBuilding) + self._message_manager.register_message_class(4, MessageCivilian) + self._message_manager.register_message_class(5, MessageRoad) + self._message_manager.register_message_class(6, CommandAmbulance) + self._message_manager.register_message_class(7, CommandFire) + self._message_manager.register_message_class(8, CommandPolice) + self._message_manager.register_message_class(9, CommandScout) + self._message_manager.register_message_class(10, MessageReport) + + if time > self._ignore_time: + self._message_manager.subscribe( + self._agent_info, self._world_info, self._scenario_info + ) + if not self._message_manager.get_is_subscribed(): + subscribed_channels = self._message_manager.get_subscribed_channels() + if subscribed_channels: + self.logger.debug( + f"Subscribed channels: {subscribed_channels}", + message_manager=self._message_manager, + ) + self.send_subscribe(time, subscribed_channels) + self._message_manager.set_is_subscribed(True) + + self._agent_info.set_heard_commands(hear) + self._agent_info.set_change_set(change_set) + self._world_info.set_change_set(change_set) + + self._message_manager.refresh() + self._communication_module.receive(self, self._message_manager) + + self.think() + + self.logger.debug( + f"send messages: {self._message_manager.get_send_message_list()}", + message_manager=self._message_manager, + ) + + self._message_manager.coordinate_message( + self._agent_info, self._world_info, self._scenario_info + ) + self._communication_module.send(self, self._message_manager) + + @abstractmethod + def think(self) -> None: + pass + + @abstractmethod + def precompute(self) -> None: + pass + + @abstractmethod + def get_requested_entities(self) -> list[EntityURN]: + pass + + def start_up(self, request_id: int) -> None: + ak_connect = AKConnect() + self.send_msg(ak_connect.write(request_id, self)) + + def message_received(self, msg: Any) -> None: + c_msg = ControlMessageFactory().make_message(msg) + if isinstance(c_msg, KASense): + self.handler_sense(c_msg) + elif isinstance(c_msg, KAConnectOK): + self.handle_connect_ok(c_msg) + elif isinstance(c_msg, KAConnectError): + self.handle_connect_error(c_msg) + def handle_connect_error(self, msg: Any) -> NoReturn: self.logger.error( "Failed to connect agent: %s(request_id: %s)", msg.reason, msg.request_id ) sys.exit(1) + + def handle_connect_ok(self, msg: Any) -> None: + self.agent_id = EntityID(msg.agent_id) + self.world_model.add_entities(msg.world) + config: RCRSConfig = msg.config + self.config = Config() + if config is not None: + for key, value in config.data.items(): + self.config.set_value(key, value) + for key, value in config.int_data.items(): + self.config.set_value(key, value) + for key, value in config.float_data.items(): + self.config.set_value(key, value) + for key, value in config.boolean_data.items(): + self.config.set_value(key, value) + for key, value in config.array_data.items(): + self.config.set_value(key, value) + self.send_acknowledge(msg.request_id) + self.post_connect() + if self.precompute_flag: + print("self.precompute_flag: ", self.precompute_flag) + self.precompute() + + def handler_sense(self, msg: Any) -> None: + _id = EntityID(msg.agent_id) + time = msg.time + change_set = msg.change_set + heard = msg.hear.commands + + if _id != self.get_entity_id(): + self.logger.error("Agent ID mismatch: %s != %s", _id, self.get_entity_id()) + return + + heard_commands: list[Command] = [] + for herad_command in heard: + if herad_command.urn == CommandURN.AK_SPEAK: + heard_commands.append( + AKSpeak( + herad_command.components[ + ComponentControlMessageID.AgentID + ].entityID, + herad_command.components[ + ComponentControlMessageID.Time + ].intValue, + herad_command.components[ + ComponentCommandMessageID.Message + ].rawData, + herad_command.components[ + ComponentCommandMessageID.Channel + ].intValue, + ) + ) + + self.world_model.merge(change_set) + self.update_step_info(time, change_set, heard_commands) + + def send_acknowledge(self, request_id: int) -> None: + ak_ack = AKAcknowledge() + self.send_msg(ak_ack.write(request_id, self.agent_id)) + + def send_clear(self, time: int, target: EntityID) -> None: + cmd = AKClear(self.get_entity_id(), time, target) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_clear_area(self, time: int, x: int = -1, y: int = -1) -> None: + cmd = AKClearArea(self.get_entity_id(), time, x, y) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_load(self, time: int, target: EntityID) -> None: + cmd = AKLoad(self.get_entity_id(), time, target) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_move(self, time: int, path: list[int], x: int = -1, y: int = -1) -> None: + cmd = AKMove(self.get_entity_id(), time, path[:], x, y) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_rescue(self, time: int, target: EntityID) -> None: + cmd = AKRescue(self.get_entity_id(), time, target) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_rest(self, time: int) -> None: + cmd = AKRest(self.get_entity_id(), time) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_say(self, time_step: int, message: str) -> None: + cmd = AKSay(self.get_entity_id(), time_step, message) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_speak(self, time_step: int, message: bitarray, channel: int) -> None: + cmd = AKSpeak(self.get_entity_id(), time_step, bytes(message), channel) # type: ignore + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_subscribe(self, time: int, channels: list[int]) -> None: + cmd = AKSubscribe(self.get_entity_id(), time, channels) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_tell(self, time: int, message: str) -> None: + cmd = AKTell(self.get_entity_id(), time, message) + msg = cmd.prepare_cmd() + self.send_msg(msg) + + def send_unload(self, time: int) -> None: + cmd = AKUnload(self.get_entity_id(), time) + msg = cmd.prepare_cmd() + self.send_msg(msg) diff --git a/adf_core_python/core/agent/communication/message_manager.py b/adf_core_python/core/agent/communication/message_manager.py index 78831364..f318b1ab 100644 --- a/adf_core_python/core/agent/communication/message_manager.py +++ b/adf_core_python/core/agent/communication/message_manager.py @@ -1,4 +1,375 @@ -# TODO: Implement MessageManager class +from __future__ import annotations + +from typing import TYPE_CHECKING + +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo, ScenarioInfoKeys +from adf_core_python.core.agent.info.world_info import WorldInfo + +if TYPE_CHECKING: + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.component.communication.channel_subscriber import ( + ChannelSubscriber, + ) + from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, + ) + from adf_core_python.core.component.communication.message_coordinator import ( + MessageCoordinator, + ) + + class MessageManager: + MAX_MESSAGE_CLASS_COUNT = 32 + def __init__(self) -> None: - pass + """ + Initialize the MessageManager. + + Parameters + ---------- + __standard_message_class_count : int + The count of standard message classes. + __custom_message_class_count : int + The count of custom message classes. + __message_classes : dict[int, CommunicationMessage] + The message classes. + __subscribed_channels : list[int] + The subscribed channels. Default is [1]. + __is_subscribed : bool + The flag to indicate if the agent is subscribed to the channel. + """ + self.__standard_message_class_count = 0b0000_0001 + self.__custom_message_class_count = 0b0001_0000 + self.__message_classes: dict[int, type[CommunicationMessage]] = {} + self.__send_message_list: list[CommunicationMessage] = [] + self.__received_message_list: list[CommunicationMessage] = [] + self.__channel_send_message_list: list[list[CommunicationMessage]] = [] + self.__check_duplicate_cache: set[int] = set() + self.__message_coordinator: MessageCoordinator + self.__channel_subscriber: ChannelSubscriber + self.__heard_agent_help_message_count: int = 0 + self.__subscribed_channels: list[int] = [1] + self.__is_subscribed = False + + def set_subscribed_channels(self, subscribed_channels: list[int]) -> None: + """ + Set the subscribed channels. + + Parameters + ---------- + subscribed_channels : list[int] + The subscribed channels. + + """ + self.__subscribed_channels = subscribed_channels + self.__is_subscribed = False + + def get_subscribed_channels(self) -> list[int]: + """ + Get the subscribed channels. + + Returns + ------- + list[int] + The subscribed channels. + + """ + return self.__subscribed_channels + + def set_is_subscribed(self, is_subscribed: bool) -> None: + """ + Set the flag to indicate if the agent is subscribed to the channel. + + Parameters + ---------- + is_subscribed : bool + The flag to indicate if the agent is subscribed to the channel. + + """ + self.__is_subscribed = is_subscribed + + def get_is_subscribed(self) -> bool: + """ + Get the flag to indicate if the agent is subscribed to the channel. + + Returns + ------- + bool + The flag to indicate if the agent is subscribed to the channel. + + """ + return self.__is_subscribed + + def set_channel_subscriber(self, channel_subscriber: ChannelSubscriber) -> None: + """ + Set the channel subscriber. + + Parameters + ---------- + channel_subscriber : ChannelSubscriber + The channel subscriber. + + """ + self.__channel_subscriber = channel_subscriber + + def set_message_coordinator(self, message_coordinator: MessageCoordinator) -> None: + """ + Set the message coordinator. + + Parameters + ---------- + message_coordinator : MessageCoordinator + The message coordinator. + + """ + self.__message_coordinator = message_coordinator + + def get_channel_subscriber(self) -> ChannelSubscriber: + """ + Get the channel subscriber. + + Returns + ------- + ChannelSubscriber + The channel subscriber. + + """ + return self.__channel_subscriber + + def add_heard_agent_help_message_count(self) -> None: + """ + Add the heard agent help message count. + + """ + self.__heard_agent_help_message_count += 1 + + def get_heard_agent_help_message_count(self) -> int: + """ + Get the heard agent help message count. + + Returns + ------- + int + The heard agent help message count. + + """ + return self.__heard_agent_help_message_count + + def add_received_message(self, message: CommunicationMessage) -> None: + """ + Add the received message. + + Parameters + ---------- + message : CommunicationMessage + The received message. + + """ + self.__received_message_list.append(message) + + def get_received_message_list(self) -> list[CommunicationMessage]: + """ + Get the received message list. + + Returns + ------- + list[CommunicationMessage] + The received message list. + + """ + return self.__received_message_list + + def get_channel_send_message_list(self) -> list[list[CommunicationMessage]]: + """ + Get the channel send message list. + + Returns + ------- + list[list[CommunicationMessage]] + The channel send message list. + + """ + return self.__channel_send_message_list + + def subscribe( + self, agent_info: AgentInfo, world_info: WorldInfo, scenario_info: ScenarioInfo + ) -> None: + """ + Subscribe to the channel. + + Parameters + ---------- + agent_info : AgentInfo + The agent info. + world_info : WorldInfo + The world info. + scenario_info : ScenarioInfo + The scenario info. + + Throws + ------ + ValueError + If the ChannelSubscriber is not set. + + """ + if self.__channel_subscriber is None: + raise ValueError("ChannelSubscriber is not set.") + + if agent_info.get_time() == 1: + self.__subscribed_channels = self.__channel_subscriber.subscribe( + agent_info, world_info, scenario_info + ) + + def register_message_class( + self, index: int, message_class: type[CommunicationMessage] + ) -> None: + """ + Register the message class. + + Parameters + ---------- + message_class : type[CommunicationMessage] + The message class. + + """ + if index >= self.MAX_MESSAGE_CLASS_COUNT: + raise ValueError( + f"Possible index values are 0 to {self.MAX_MESSAGE_CLASS_COUNT-1}" + ) + self.__message_classes[index] = message_class + + def get_message_class(self, index: int) -> type[CommunicationMessage]: + """ + Get the message class. + + Parameters + ---------- + index : int + The index. + + Returns + ------- + type[CommunicationMessage] + The message class. + + """ + return self.__message_classes[index] + + def get_message_class_index(self, message_class: type[CommunicationMessage]) -> int: + """ + Get the message class index. + + Parameters + ---------- + message_class : type[CommunicationMessage] + The message class. + + Returns + ------- + int + The message class index. + + """ + for index, cls in self.__message_classes.items(): + if cls == message_class: + return index + return -1 + + def add_message( + self, message: CommunicationMessage, check_duplicate: bool = True + ) -> None: + """ + Add the message. + + Parameters + ---------- + message : CommunicationMessage + The message. + + """ + check_key = message.__hash__() + # TODO:両方同じコードになっているが、なぜなのか調査する + if check_duplicate and check_key not in self.__check_duplicate_cache: + self.__send_message_list.append(message) + self.__check_duplicate_cache.add(check_key) + else: + self.__send_message_list.append(message) + self.__check_duplicate_cache.add(check_key) + + def get_send_message_list(self) -> list[CommunicationMessage]: + """ + Get the send message list. + + Returns + ------- + list[CommunicationMessage] + The send message list. + + """ + return self.__send_message_list + + def coordinate_message( + self, agent_info: AgentInfo, world_info: WorldInfo, scenario_info: ScenarioInfo + ) -> None: + """ + Coordinate the message. + + Parameters + ---------- + agent_info : AgentInfo + The agent info. + world_info : WorldInfo + The world info. + scenario_info : ScenarioInfo + The scenario info. + + """ + if self.__message_coordinator is None: + raise ValueError("MessageCoordinator is not set.") + + self.__channel_send_message_list = [ + [] + for _ in range( + int( + scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1 + ) + ) + ) + ] + + self.__message_coordinator.coordinate( + agent_info, + world_info, + scenario_info, + self, + self.__send_message_list, + self.__channel_send_message_list, + ) + + def refresh(self) -> None: + """ + Refresh the message manager. + + """ + self.__send_message_list = [] + self.__received_message_list = [] + self.__check_duplicate_cache = set() + self.__heard_agent_help_message_count = 0 + + def __str__(self) -> str: + return ( + f"MessageManager(" + f"standard_message_class_count={self.__standard_message_class_count}, " + f"custom_message_class_count={self.__custom_message_class_count}, " + f"message_classes={self.__message_classes}, " + f"send_message_list={self.__send_message_list}, " + f"received_message_list={self.__received_message_list}, " + f"channel_send_message_list={self.__channel_send_message_list}, " + f"check_duplicate_cache={self.__check_duplicate_cache}, " + f"message_coordinator={self.__message_coordinator}, " + f"channel_subscriber={self.__channel_subscriber}, " + f"heard_agent_help_message_count={self.__heard_agent_help_message_count}, " + f"subscribed_channels={self.__subscribed_channels}, " + f"is_subscribed={self.__is_subscribed})" + ) diff --git a/adf_core_python/core/agent/communication/standard/__init__.py b/adf_core_python/core/agent/communication/standard/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/communication/standard/bundle/__init__.py b/adf_core_python/core/agent/communication/standard/bundle/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/communication/standard/bundle/centralized/__init__.py b/adf_core_python/core/agent/communication/standard/bundle/centralized/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/communication/standard/bundle/centralized/command_ambulance.py b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_ambulance.py new file mode 100644 index 00000000..a5d69843 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_ambulance.py @@ -0,0 +1,135 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandAmbulance(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_RESCUE: int = 2 + ACTION_LOAD: int = 3 + ACTION_UNLOAD: int = 4 + ACTION_AUTONOMY: int = 5 + + SIZE_AMBULANCE_TEAM_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + execute_action: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._execute_action: Optional[int] = execute_action + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_execute_action(self) -> Optional[int]: + return self._execute_action + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_AMBULANCE_TEAM_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._execute_action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandAmbulance: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + execute_action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + execute_action if execute_action is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._execute_action, + ) + ) + + def __str__(self) -> str: + return f"CommandAmbulance(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, action={self._execute_action})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/centralized/command_fire.py b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_fire.py new file mode 100644 index 00000000..cf611315 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_fire.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandFire(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_EXTINGUISH: int = 2 + ACTION_REFILL: int = 3 + ACTION_RESCUE: int = 4 + ACTION_AUTONOMY: int = 5 + + SIZE_FIRE_BRIGADE_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + execute_action: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._execute_action: Optional[int] = execute_action + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_execute_action(self) -> Optional[int]: + return self._execute_action + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_FIRE_BRIGADE_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._execute_action, self.SIZE_ACTION) + + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandFire: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + execute_action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + execute_action if execute_action is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._execute_action, + ) + ) + + def __str__(self) -> str: + return f"CommandFire(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, action={self._execute_action})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/centralized/command_police.py b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_police.py new file mode 100644 index 00000000..64b8e349 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_police.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandPolice(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_CLEAR: int = 2 + ACTION_AUTONOMY: int = 3 + + SIZE_POLICE_FORCE_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + execute_action: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._execute_action: Optional[int] = execute_action + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_execute_action(self) -> Optional[int]: + return self._execute_action + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_POLICE_FORCE_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._execute_action, self.SIZE_ACTION) + + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandPolice: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + execute_action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + execute_action if execute_action is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._execute_action, + ) + ) + + def __str__(self) -> str: + return f"CommandPolice(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, action={self._execute_action})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/centralized/command_scout.py b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_scout.py new file mode 100644 index 00000000..138a1ddb --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/centralized/command_scout.py @@ -0,0 +1,129 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class CommandScout(StandardMessage): + SIZE_AGENT_ENTITY_ID: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_SCOUT_RANGE: int = 32 + + def __init__( + self, + is_wireless_message: bool, + command_executor_agent_entity_id: EntityID, + sender_entity_id: EntityID, + scout_range: int, + priority: StandardMessagePriority, + command_target_entity_id: Optional[EntityID] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._command_executor_agent_entity_id: Optional[EntityID] = ( + command_executor_agent_entity_id + ) + self._command_target_entity_id: Optional[EntityID] = command_target_entity_id + self._is_bloadcast: bool = command_target_entity_id is None + self._scout_range: Optional[int] = scout_range + + def get_command_executor_agent_entity_id(self) -> Optional[EntityID]: + return self._command_executor_agent_entity_id + + def get_command_target_entity_id(self) -> Optional[EntityID]: + return self._command_target_entity_id + + def get_scout_range(self) -> Optional[int]: + return self._scout_range + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + raw_command_executor_agent_entity_id = ( + self._command_executor_agent_entity_id.get_value() + if self._command_executor_agent_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, + raw_command_executor_agent_entity_id, + self.SIZE_AGENT_ENTITY_ID, + ) + raw_command_target_entity_id = ( + self._command_target_entity_id.get_value() + if self._command_target_entity_id is not None + else None + ) + write_with_exist_flag( + bit_array, raw_command_target_entity_id, self.SIZE_TARGET_ENTITY_ID + ) + write_with_exist_flag(bit_array, self._scout_range, self.SIZE_SCOUT_RANGE) + + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> CommandScout: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + raw_command_executor_agent_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_AGENT_ENTITY_ID + ) + command_executor_agent_id = ( + EntityID(raw_command_executor_agent_entity_id) + if raw_command_executor_agent_entity_id is not None + else None + ) + raw_command_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + command_target_id = ( + EntityID(raw_command_target_entity_id) + if raw_command_target_entity_id is not None + else None + ) + scout_range = read_with_exist_flag(bit_array, cls.SIZE_SCOUT_RANGE) + return cls( + is_wireless_message, + command_executor_agent_id or EntityID(-1), + sender_entity_id, + scout_range if scout_range is not None else -1, + std_message.get_priority(), + command_target_id, + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._command_executor_agent_entity_id, + self._command_target_entity_id, + self._scout_range, + ) + ) + + def __str__(self) -> str: + return f"CommandScout(executor={self._command_executor_agent_entity_id}, target={self._command_target_entity_id}, scout_range={self._scout_range})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/centralized/message_report.py b/adf_core_python/core/agent/communication/standard/bundle/centralized/message_report.py new file mode 100644 index 00000000..55f89074 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/centralized/message_report.py @@ -0,0 +1,88 @@ +from __future__ import annotations + +from bitarray import bitarray +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageReport(StandardMessage): + SIZE_DONE: int = 1 + SIZE_BLOADCAST: int = 1 + + def __init__( + self, + is_wireless_message: bool, + is_done: bool, + is_bloadcast: bool, + sender_entity_id: EntityID, + priority: StandardMessagePriority, + ): + super().__init__(is_wireless_message, priority, sender_entity_id) + self._is_done: bool = is_done + self._is_bloadcast: bool = is_bloadcast + + def is_done(self) -> bool: + return self._is_done + + def is_broadcast(self) -> bool: + return self._is_bloadcast + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._is_done, + self.SIZE_DONE, + ) + write_with_exist_flag( + bit_array, + self._is_bloadcast, + self.SIZE_BLOADCAST, + ) + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> MessageReport: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + is_done = read_with_exist_flag(bit_array, cls.SIZE_DONE) == 1 + is_bloadcast = read_with_exist_flag(bit_array, cls.SIZE_BLOADCAST) == 1 + return cls( + is_wireless_message, + is_done, + is_bloadcast, + std_message.get_sender_entity_id(), + std_message.get_priority(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._is_done, + self._is_bloadcast, + ) + ) + + def __str__(self) -> str: + return f"CommandReport(done={self._is_done}, broadcast={self._is_bloadcast})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/__init__.py b/adf_core_python/core/agent/communication/standard/bundle/information/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/message_ambulance_team.py b/adf_core_python/core/agent/communication/standard/bundle/information/message_ambulance_team.py new file mode 100644 index 00000000..6c73a959 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/information/message_ambulance_team.py @@ -0,0 +1,186 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.entities.ambulanceTeam import AmbulanceTeamEntity +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageAmbulanceTeam(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_RESCUE: int = 2 + ACTION_LOAD: int = 3 + ACTION_UNLOAD: int = 4 + + SIZE_AMBULANCE_TEAM_ENTITY_ID: int = 32 + SIZE_AMBULANCE_TEAM_HP: int = 14 + SIZE_AMBULANCE_TEAM_BURIEDNESS: int = 13 + SIZE_AMBULANCE_TEAM_DAMAGE: int = 14 + SIZE_AMBULANCE_TEAM_POSITION: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + ambulance_team: AmbulanceTeamEntity, + action: int, + target_entity_id: EntityID, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._ambulance_team_entity_id: Optional[EntityID] = ambulance_team.get_id() + self._ambulance_team_hp: Optional[int] = ambulance_team.get_hp() or None + self._ambulance_team_buriedness: Optional[int] = ( + ambulance_team.get_buriedness() or None + ) + self._ambulance_team_damage: Optional[int] = ambulance_team.get_damage() or None + self._ambulance_team_position: Optional[EntityID] = ( + ambulance_team.get_position() or None + ) + self._target_entity_id: Optional[EntityID] = target_entity_id + self._action: Optional[int] = action + + def get_ambulance_team_entity_id(self) -> Optional[EntityID]: + return self._ambulance_team_entity_id + + def get_ambulance_team_hp(self) -> Optional[int]: + return self._ambulance_team_hp + + def get_ambulance_team_buriedness(self) -> Optional[int]: + return self._ambulance_team_buriedness + + def get_ambulance_team_damage(self) -> Optional[int]: + return self._ambulance_team_damage + + def get_ambulance_team_position(self) -> Optional[EntityID]: + return self._ambulance_team_position + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._target_entity_id + + def get_action(self) -> Optional[int]: + return self._action + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._ambulance_team_entity_id.get_value() + if self._ambulance_team_entity_id + else None, + self.SIZE_AMBULANCE_TEAM_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, self._ambulance_team_hp, self.SIZE_AMBULANCE_TEAM_HP + ) + write_with_exist_flag( + bit_array, + self._ambulance_team_buriedness, + self.SIZE_AMBULANCE_TEAM_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, self._ambulance_team_damage, self.SIZE_AMBULANCE_TEAM_DAMAGE + ) + write_with_exist_flag( + bit_array, + self._ambulance_team_position.get_value() + if self._ambulance_team_position + else None, + self.SIZE_AMBULANCE_TEAM_POSITION, + ) + write_with_exist_flag( + bit_array, + self._target_entity_id.get_value() if self._target_entity_id else None, + self.SIZE_TARGET_ENTITY_ID, + ) + write_with_exist_flag(bit_array, self._action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> MessageAmbulanceTeam: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + ambulance_team_id = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_ENTITY_ID + ) + ambulance_team_hp = read_with_exist_flag(bit_array, cls.SIZE_AMBULANCE_TEAM_HP) + ambulance_team_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_BURIEDNESS + ) + ambulance_team_damage = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_DAMAGE + ) + raw_ambulance_team_position = read_with_exist_flag( + bit_array, cls.SIZE_AMBULANCE_TEAM_POSITION + ) + ambulance_team_position = ( + EntityID(raw_ambulance_team_position) + if raw_ambulance_team_position is not None + else None + ) + raw_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + target_entity_id = ( + EntityID(raw_target_entity_id) + if raw_target_entity_id is not None + else EntityID(-1) + ) + action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + ambulance_team = AmbulanceTeamEntity(ambulance_team_id or -1) + ambulance_team.set_hp(ambulance_team_hp) + ambulance_team.set_buriedness(ambulance_team_buriedness) + ambulance_team.set_damage(ambulance_team_damage) + ambulance_team.set_position(ambulance_team_position) + return MessageAmbulanceTeam( + False, + ambulance_team, + action if action is not None else -1, + target_entity_id, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._ambulance_team_entity_id, + self._ambulance_team_hp, + self._ambulance_team_buriedness, + self._ambulance_team_damage, + self._ambulance_team_position, + self._target_entity_id, + self._action, + ) + ) + + def __str__(self) -> str: + return f"MessageAmbulanceTeam(ambulance_team_entity_id={self._ambulance_team_entity_id}, ambulance_team_hp={self._ambulance_team_hp}, ambulance_team_buriedness={self._ambulance_team_buriedness}, ambulance_team_damage={self._ambulance_team_damage}, ambulance_team_position={self._ambulance_team_position}, target_entity_id={self._target_entity_id}, action={self._action}, ttl={self._ttl})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/message_building.py b/adf_core_python/core/agent/communication/standard/bundle/information/message_building.py new file mode 100644 index 00000000..be9f911a --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/information/message_building.py @@ -0,0 +1,122 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.entities.building import Building +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageBuilding(StandardMessage): + SIZE_BUILDING_ENTITY_ID: int = 32 + SIZE_BUILDING_BROKENNESS: int = 32 + SIZE_BUILDING_FIREYNESS: int = 32 + SIZE_BUILDING_TEMPERATURE: int = 32 + + def __init__( + self, + is_wireless_message: bool, + building: Building, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._building_entity_id: Optional[EntityID] = building.get_id() + self._building_brokenness: Optional[int] = building.get_brokenness() or None + self._building_fireyness: Optional[int] = building.get_fieryness() or None + self._building_temperature: Optional[int] = building.get_temperature() or None + + def get_building_entity_id(self) -> Optional[EntityID]: + return self._building_entity_id + + def get_building_brokenness(self) -> Optional[int]: + return self._building_brokenness + + def get_building_fireyness(self) -> Optional[int]: + return self._building_fireyness + + def get_building_temperature(self) -> Optional[int]: + return self._building_temperature + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._building_entity_id.get_value() if self._building_entity_id else None, + self.SIZE_BUILDING_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._building_brokenness, + self.SIZE_BUILDING_BROKENNESS, + ) + write_with_exist_flag( + bit_array, + self._building_fireyness, + self.SIZE_BUILDING_FIREYNESS, + ) + write_with_exist_flag( + bit_array, + self._building_temperature, + self.SIZE_BUILDING_TEMPERATURE, + ) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageBuilding: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + building_id = read_with_exist_flag(bit_array, cls.SIZE_BUILDING_ENTITY_ID) + building_brokenness = read_with_exist_flag( + bit_array, cls.SIZE_BUILDING_BROKENNESS + ) + building_fireyness = read_with_exist_flag( + bit_array, cls.SIZE_BUILDING_FIREYNESS + ) + building_temperature = read_with_exist_flag( + bit_array, cls.SIZE_BUILDING_TEMPERATURE + ) + building = Building(building_id or -1) + building.set_brokenness(building_brokenness) + building.set_fieryness(building_fireyness) + building.set_temperature(building_temperature) + return MessageBuilding( + False, + building, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._building_entity_id, + self._building_brokenness, + self._building_fireyness, + self._building_temperature, + ) + ) + + def __str__(self) -> str: + return f"MessageBuilding(building_entity_id={self._building_entity_id}, building_brokenness={self._building_brokenness}, building_fireyness={self._building_fireyness}, building_temperature={self._building_temperature})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/message_civilian.py b/adf_core_python/core/agent/communication/standard/bundle/information/message_civilian.py new file mode 100644 index 00000000..a5a62b42 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/information/message_civilian.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.entities.civilian import Civilian +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageCivilian(StandardMessage): + SIZE_CIVILIAN_ENTITY_ID: int = 32 + SIZE_CIVILIAN_HP: int = 14 + SIZE_CIVILIAN_BURIEDNESS: int = 13 + SIZE_CIVILIAN_DAMAGE: int = 14 + SIZE_CIVILIAN_POSITION: int = 32 + + def __init__( + self, + is_wireless_message: bool, + civilian: Civilian, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._civilian_entity_id: Optional[EntityID] = civilian.get_id() + self._civilian_hp: Optional[int] = civilian.get_hp() or None + self._civilian_buriedness: Optional[int] = civilian.get_buriedness() or None + self._civilian_damage: Optional[int] = civilian.get_damage() or None + self._civilian_position: Optional[EntityID] = civilian.get_position() or None + + def get_civilian_entity_id(self) -> Optional[EntityID]: + return self._civilian_entity_id + + def get_civilian_hp(self) -> Optional[int]: + return self._civilian_hp + + def get_civilian_buriedness(self) -> Optional[int]: + return self._civilian_buriedness + + def get_civilian_damage(self) -> Optional[int]: + return self._civilian_damage + + def get_civilian_position(self) -> Optional[EntityID]: + return self._civilian_position + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._civilian_entity_id.get_value() if self._civilian_entity_id else None, + self.SIZE_CIVILIAN_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._civilian_hp, + self.SIZE_CIVILIAN_HP, + ) + write_with_exist_flag( + bit_array, + self._civilian_buriedness, + self.SIZE_CIVILIAN_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, + self._civilian_damage, + self.SIZE_CIVILIAN_DAMAGE, + ) + write_with_exist_flag( + bit_array, + self._civilian_position.get_value() if self._civilian_position else None, + self.SIZE_CIVILIAN_POSITION, + ) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageCivilian: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + civilian_id = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_ENTITY_ID) + civilian_hp = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_HP) + civilian_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_CIVILIAN_BURIEDNESS + ) + civilian_damage = read_with_exist_flag(bit_array, cls.SIZE_CIVILIAN_DAMAGE) + raw_civilian_position = read_with_exist_flag( + bit_array, cls.SIZE_CIVILIAN_POSITION + ) + civilian_position = ( + EntityID(raw_civilian_position) if raw_civilian_position else None + ) + civilian = Civilian(civilian_id or -1) + civilian.set_hp(civilian_hp) + civilian.set_buriedness(civilian_buriedness) + civilian.set_damage(civilian_damage) + civilian.set_position(civilian_position) + return MessageCivilian( + False, + civilian, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._civilian_entity_id, + self._civilian_hp, + self._civilian_buriedness, + self._civilian_damage, + self._civilian_position, + ) + ) + + def __str__(self) -> str: + return f"MessageCivilian(civilian_entity_id={self._civilian_entity_id}, civilian_hp={self._civilian_hp}, civilian_buriedness={self._civilian_buriedness}, civilian_damage={self._civilian_damage}, civilian_position={self._civilian_position})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/message_fire_brigade.py b/adf_core_python/core/agent/communication/standard/bundle/information/message_fire_brigade.py new file mode 100644 index 00000000..a7c4cee1 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/information/message_fire_brigade.py @@ -0,0 +1,200 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.entities.fireBrigade import FireBrigadeEntity +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageFireBrigade(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_EXTINGUISH: int = 2 + ACTION_REFILL: int = 3 + ACTION_RESCUE: int = 4 + + SIZE_FIRE_BRIGADE_ENTITY_ID: int = 32 + SIZE_FIRE_BRIGADE_HP: int = 14 + SIZE_FIRE_BRIGADE_BURIEDNESS: int = 13 + SIZE_FIRE_BRIGADE_DAMAGE: int = 14 + SIZE_FIRE_BRIGADE_POSITION: int = 32 + SIZE_FIRE_BRIGADE_WATER: int = 14 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + fire_brigade: FireBrigadeEntity, + action: int, + target_entity_id: EntityID, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._fire_brigade_entity_id: Optional[EntityID] = fire_brigade.get_id() + self._fire_brigade_hp: Optional[int] = fire_brigade.get_hp() or None + self._fire_brigade_buriedness: Optional[int] = ( + fire_brigade.get_buriedness() or None + ) + self._fire_brigade_damage: Optional[int] = fire_brigade.get_damage() or None + self._fire_brigade_position: Optional[EntityID] = ( + fire_brigade.get_position() or None + ) + self._fire_brigade_water: Optional[int] = fire_brigade.get_water() or None + self._target_entity_id: Optional[EntityID] = target_entity_id + self._action: Optional[int] = action + + def get_fire_brigade_entity_id(self) -> Optional[EntityID]: + return self._fire_brigade_entity_id + + def get_fire_brigade_hp(self) -> Optional[int]: + return self._fire_brigade_hp + + def get_fire_brigade_buriedness(self) -> Optional[int]: + return self._fire_brigade_buriedness + + def get_fire_brigade_damage(self) -> Optional[int]: + return self._fire_brigade_damage + + def get_fire_brigade_position(self) -> Optional[EntityID]: + return self._fire_brigade_position + + def get_fire_brigade_water(self) -> Optional[int]: + return self._fire_brigade_water + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._target_entity_id + + def get_action(self) -> Optional[int]: + return self._action + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._fire_brigade_entity_id.get_value() + if self._fire_brigade_entity_id + else None, + self.SIZE_FIRE_BRIGADE_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_hp, + self.SIZE_FIRE_BRIGADE_HP, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_buriedness, + self.SIZE_FIRE_BRIGADE_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_damage, + self.SIZE_FIRE_BRIGADE_DAMAGE, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_position.get_value() + if self._fire_brigade_position + else None, + self.SIZE_FIRE_BRIGADE_POSITION, + ) + write_with_exist_flag( + bit_array, + self._fire_brigade_water, + self.SIZE_FIRE_BRIGADE_WATER, + ) + write_with_exist_flag( + bit_array, + self._target_entity_id.get_value() if self._target_entity_id else None, + self.SIZE_TARGET_ENTITY_ID, + ) + write_with_exist_flag(bit_array, self._action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageFireBrigade: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + fire_brigade_id = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_ENTITY_ID + ) + fire_brigade_hp = read_with_exist_flag(bit_array, cls.SIZE_FIRE_BRIGADE_HP) + fire_brigade_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_BURIEDNESS + ) + fire_brigade_damage = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_DAMAGE + ) + raw_fire_brigade_position = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_POSITION + ) + fire_brigade_position = ( + EntityID(raw_fire_brigade_position) if raw_fire_brigade_position else None + ) + fire_brigade_water = read_with_exist_flag( + bit_array, cls.SIZE_FIRE_BRIGADE_WATER + ) + raw_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + target_entity_id = ( + EntityID(raw_target_entity_id) if raw_target_entity_id else EntityID(-1) + ) + action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + fire_brigade = FireBrigadeEntity( + fire_brigade_id or -1, + ) + fire_brigade.set_hp(fire_brigade_hp) + fire_brigade.set_buriedness(fire_brigade_buriedness) + fire_brigade.set_damage(fire_brigade_damage) + fire_brigade.set_position(fire_brigade_position) + fire_brigade.set_water(fire_brigade_water) + return MessageFireBrigade( + False, + fire_brigade, + action if action is not None else -1, + target_entity_id, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._fire_brigade_entity_id, + self._fire_brigade_hp, + self._fire_brigade_buriedness, + self._fire_brigade_damage, + self._fire_brigade_position, + self._fire_brigade_water, + self._target_entity_id, + self._action, + ) + ) + + def __str__(self) -> str: + return f"MessageFireBrigade(fire_brigade_entity_id={self._fire_brigade_entity_id}, fire_brigade_hp={self._fire_brigade_hp}, fire_brigade_buriedness={self._fire_brigade_buriedness}, fire_brigade_damage={self._fire_brigade_damage}, fire_brigade_position={self._fire_brigade_position}, fire_brigade_water={self._fire_brigade_water}, target_entity_id={self._target_entity_id}, action={self._action})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/message_police_force.py b/adf_core_python/core/agent/communication/standard/bundle/information/message_police_force.py new file mode 100644 index 00000000..45116f3e --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/information/message_police_force.py @@ -0,0 +1,181 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.entities.policeForce import PoliceForceEntity +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessagePoliceForce(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_CLEAR: int = 2 + + SIZE_POLICE_FORCE_ENTITY_ID: int = 32 + SIZE_POLICE_FORCE_HP: int = 14 + SIZE_POLICE_FORCE_BURIEDNESS: int = 13 + SIZE_POLICE_FORCE_DAMAGE: int = 14 + SIZE_POLICE_FORCE_POSITION: int = 32 + SIZE_TARGET_ENTITY_ID: int = 32 + SIZE_ACTION: int = 4 + + def __init__( + self, + is_wireless_message: bool, + police_force: PoliceForceEntity, + action: int, + target_entity_id: EntityID, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._police_force_entity_id: Optional[EntityID] = police_force.get_id() + self._police_force_hp: Optional[int] = police_force.get_hp() or None + self._police_force_buriedness: Optional[int] = ( + police_force.get_buriedness() or None + ) + self._police_force_damage: Optional[int] = police_force.get_damage() or None + self._police_force_position: Optional[EntityID] = ( + police_force.get_position() or None + ) + self._target_entity_id: Optional[EntityID] = target_entity_id + self._action: Optional[int] = action + + def get_police_force_entity_id(self) -> Optional[EntityID]: + return self._police_force_entity_id + + def get_police_force_hp(self) -> Optional[int]: + return self._police_force_hp + + def get_police_force_buriedness(self) -> Optional[int]: + return self._police_force_buriedness + + def get_police_force_damage(self) -> Optional[int]: + return self._police_force_damage + + def get_police_force_position(self) -> Optional[EntityID]: + return self._police_force_position + + def get_target_entity_id(self) -> Optional[EntityID]: + return self._target_entity_id + + def get_action(self) -> Optional[int]: + return self._action + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._police_force_entity_id.get_value() + if self._police_force_entity_id + else None, + self.SIZE_POLICE_FORCE_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._police_force_hp, + self.SIZE_POLICE_FORCE_HP, + ) + write_with_exist_flag( + bit_array, + self._police_force_buriedness, + self.SIZE_POLICE_FORCE_BURIEDNESS, + ) + write_with_exist_flag( + bit_array, + self._police_force_damage, + self.SIZE_POLICE_FORCE_DAMAGE, + ) + write_with_exist_flag( + bit_array, + self._police_force_position.get_value() + if self._police_force_position + else None, + self.SIZE_POLICE_FORCE_POSITION, + ) + write_with_exist_flag( + bit_array, + self._target_entity_id.get_value() if self._target_entity_id else None, + self.SIZE_TARGET_ENTITY_ID, + ) + write_with_exist_flag(bit_array, self._action, self.SIZE_ACTION) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessagePoliceForce: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + police_force_id = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_ENTITY_ID + ) + police_force_hp = read_with_exist_flag(bit_array, cls.SIZE_POLICE_FORCE_HP) + police_force_buriedness = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_BURIEDNESS + ) + police_force_damage = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_DAMAGE + ) + raw_police_force_position = read_with_exist_flag( + bit_array, cls.SIZE_POLICE_FORCE_POSITION + ) + police_force_position = ( + EntityID(raw_police_force_position) if raw_police_force_position else None + ) + raw_target_entity_id = read_with_exist_flag( + bit_array, cls.SIZE_TARGET_ENTITY_ID + ) + target_entity_id = ( + EntityID(raw_target_entity_id) if raw_target_entity_id else EntityID(-1) + ) + action = read_with_exist_flag(bit_array, cls.SIZE_ACTION) + police_force = PoliceForceEntity(police_force_id or -1) + police_force.set_hp(police_force_hp) + police_force.set_buriedness(police_force_buriedness) + police_force.set_damage(police_force_damage) + police_force.set_position(police_force_position) + return MessagePoliceForce( + False, + police_force, + action if action is not None else -1, + target_entity_id, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._police_force_entity_id, + self._police_force_hp, + self._police_force_buriedness, + self._police_force_damage, + self._police_force_position, + self._target_entity_id, + self._action, + ) + ) + + def __str__(self) -> str: + return f"MessagePoliceForce(police_force_entity_id={self._police_force_entity_id}, police_force_hp={self._police_force_hp}, police_force_buriedness={self._police_force_buriedness}, police_force_damage={self._police_force_damage}, police_force_position={self._police_force_position}, target_entity_id={self._target_entity_id}, action={self._action})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/information/message_road.py b/adf_core_python/core/agent/communication/standard/bundle/information/message_road.py new file mode 100644 index 00000000..474421c4 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/information/message_road.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.entities.blockade import Blockade +from rcrs_core.entities.road import Road +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) + + +class MessageRoad(StandardMessage): + ACTION_REST: int = 0 + ACTION_MOVE: int = 1 + ACTION_CLEAR: int = 2 + + SIZE_ROAD_ENTITY_ID: int = 32 + SIZE_ROAD_BLOCKADE_ENTITY_ID: int = 32 + SIZE_ROAD_BLOCKADE_REPAIR_COST: int = 32 + SIZE_ROAD_BLOCKADE_X: int = 32 + SIZE_ROAD_BLOCKADE_Y: int = 32 + SIZE_PASSABLE: int = 1 + + def __init__( + self, + is_wireless_message: bool, + road: Road, + is_send_blockade_location: bool, + is_passable: Optional[bool], + blockade: Optional[Blockade], + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message, priority, sender_entity_id, ttl) + self._road_entity_id: Optional[EntityID] = road.get_id() + self._road_blockade_entity_id: Optional[EntityID] = None + self._road_blockade_repair_cost: Optional[int] = None + self._road_blockade_x: Optional[int] = None + self._road_blockade_y: Optional[int] = None + + if blockade: + self._road_blockade_entity_id = blockade.get_id() + self._road_blockade_repair_cost = blockade.get_repaire_cost() + if is_send_blockade_location: + self._road_blockade_x = blockade.get_x() or None + self._road_blockade_y = blockade.get_y() or None + + self._is_passable: Optional[bool] = is_passable + self._is_send_blockade_location: bool = is_send_blockade_location + + def get_road_entity_id(self) -> Optional[EntityID]: + return self._road_entity_id + + def get_road_blockade_entity_id(self) -> Optional[EntityID]: + return self._road_blockade_entity_id + + def get_road_blockade_repair_cost(self) -> Optional[int]: + return self._road_blockade_repair_cost + + def get_road_blockade_x(self) -> Optional[int]: + return self._road_blockade_x + + def get_road_blockade_y(self) -> Optional[int]: + return self._road_blockade_y + + def get_is_passable(self) -> Optional[bool]: + return self._is_passable + + def get_is_send_blockade_location(self) -> bool: + return self._is_send_blockade_location + + def get_bit_size(self) -> int: + return self.to_bits().__len__() + + def to_bits(self) -> bitarray: + bit_array = super().to_bits() + write_with_exist_flag( + bit_array, + self._road_entity_id.get_value() if self._road_entity_id else None, + self.SIZE_ROAD_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._road_blockade_entity_id.get_value() + if self._road_blockade_entity_id + else None, + self.SIZE_ROAD_BLOCKADE_ENTITY_ID, + ) + write_with_exist_flag( + bit_array, + self._road_blockade_repair_cost + if self._road_blockade_repair_cost + else None, + self.SIZE_ROAD_BLOCKADE_REPAIR_COST, + ) + if self._is_send_blockade_location: + write_with_exist_flag( + bit_array, + self._road_blockade_x if self._road_blockade_x else None, + self.SIZE_ROAD_BLOCKADE_X, + ) + write_with_exist_flag( + bit_array, + self._road_blockade_y if self._road_blockade_y else None, + self.SIZE_ROAD_BLOCKADE_Y, + ) + else: + write_with_exist_flag(bit_array, None, self.SIZE_ROAD_BLOCKADE_X) + write_with_exist_flag(bit_array, None, self.SIZE_ROAD_BLOCKADE_Y) + write_with_exist_flag( + bit_array, + self._is_passable if self._is_passable else None, + self.SIZE_PASSABLE, + ) + return bit_array + + @classmethod + def from_bits( + cls, bit_array: bitarray, is_wireless_message: bool, sender_entity_id: EntityID + ) -> MessageRoad: + std_message = super().from_bits( + bit_array, is_wireless_message, sender_entity_id + ) + road_id = read_with_exist_flag(bit_array, cls.SIZE_ROAD_ENTITY_ID) + road_blockade_id = read_with_exist_flag( + bit_array, cls.SIZE_ROAD_BLOCKADE_ENTITY_ID + ) + road_blockade_repair_cost = read_with_exist_flag( + bit_array, cls.SIZE_ROAD_BLOCKADE_REPAIR_COST + ) + road_blockade_x = read_with_exist_flag(bit_array, cls.SIZE_ROAD_BLOCKADE_X) + road_blockade_y = read_with_exist_flag(bit_array, cls.SIZE_ROAD_BLOCKADE_Y) + is_passable = ( + True if read_with_exist_flag(bit_array, cls.SIZE_PASSABLE) else False + ) + road = Road(road_id or -1) + blockade = Blockade(road_blockade_id or -1) + blockade.set_repaire_cost(road_blockade_repair_cost) + blockade.set_x(road_blockade_x) + blockade.set_y(road_blockade_y) + return MessageRoad( + is_wireless_message, + road, + False, + is_passable, + blockade, + StandardMessagePriority.NORMAL, + sender_entity_id, + std_message.get_ttl(), + ) + + def __hash__(self) -> int: + h = super().__hash__() + return hash( + ( + h, + self._road_entity_id, + self._road_blockade_entity_id, + self._road_blockade_repair_cost, + self._road_blockade_x, + self._road_blockade_y, + self._is_passable, + self._is_send_blockade_location, + ) + ) + + def __str__(self) -> str: + return f"MessageRoad(road_entity_id={self._road_entity_id}, road_blockade_entity_id={self._road_blockade_entity_id}, road_blockade_repair_cost={self._road_blockade_repair_cost}, road_blockade_x={self._road_blockade_x}, road_blockade_y={self._road_blockade_y}, is_passable={self._is_passable}, is_send_blockade_location={self._is_send_blockade_location})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/standard_message.py b/adf_core_python/core/agent/communication/standard/bundle/standard_message.py new file mode 100644 index 00000000..bdec11b6 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/standard_message.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from typing import Optional + +from bitarray import bitarray +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) + + +class StandardMessage(CommunicationMessage): + SIZE_TTL: int = 3 + + def __init__( + self, + is_wireless_message: bool, + priority: StandardMessagePriority, + sender_entity_id: EntityID, + ttl: Optional[int] = None, + ): + super().__init__(is_wireless_message) + self._priority = priority + self._sender_entity_id = sender_entity_id + self._ttl = ttl + + def get_sender_entity_id(self) -> EntityID: + return self._sender_entity_id + + def get_priority(self) -> StandardMessagePriority: + return self._priority + + def get_ttl(self) -> Optional[int]: + return self._ttl + + @classmethod + def from_bits( + cls, + bit_array: bitarray, + is_wireless_message: bool, + sender_entity_id: EntityID, + ) -> StandardMessage: + ttl = read_with_exist_flag(bit_array, cls.SIZE_TTL) + return StandardMessage( + is_wireless_message, + StandardMessagePriority.NORMAL, + sender_entity_id, + ttl, + ) + + def to_bits(self) -> bitarray: + bit_array = bitarray() + write_with_exist_flag(bit_array, self._ttl, self.SIZE_TTL) + return bit_array + + def get_bit_size(self) -> int: + raise NotImplementedError + + def __hash__(self) -> int: + return hash((self._sender_entity_id, self._priority, self._ttl)) + + def __str__(self) -> str: + return f"StandardMessage(sender_entity_id={self._sender_entity_id}, priority={self._priority}, ttl={self._ttl})" diff --git a/adf_core_python/core/agent/communication/standard/bundle/standard_message_priority.py b/adf_core_python/core/agent/communication/standard/bundle/standard_message_priority.py new file mode 100644 index 00000000..9e45154b --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/bundle/standard_message_priority.py @@ -0,0 +1,24 @@ +from enum import Enum + + +class StandardMessagePriority(Enum): + """ + Standard message priorities. + """ + + LOW = 0 + NORMAL = 1 + HIGH = 2 + + def __str__(self) -> str: + return self.name.lower() + + def __lt__(self, other: object) -> bool: + if isinstance(other, StandardMessagePriority): + return self.value < other.value + return NotImplemented + + def __le__(self, other: object) -> bool: + if isinstance(other, StandardMessagePriority): + return self.value <= other.value + return NotImplemented diff --git a/adf_core_python/core/agent/communication/standard/standard_communication_module.py b/adf_core_python/core/agent/communication/standard/standard_communication_module.py new file mode 100644 index 00000000..3fdffb3c --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/standard_communication_module.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from bitarray import bitarray +from rcrs_core.commands.AKSpeak import AKSpeak +from rcrs_core.worldmodel.entityID import EntityID + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.utility.bitarray_with_exits_flag import ( + read_with_exist_flag, + write_with_exist_flag, +) +from adf_core_python.core.component.communication.communication_module import ( + CommunicationModule, +) +from adf_core_python.core.logger.logger import get_logger + +if TYPE_CHECKING: + from adf_core_python.core.agent.agent import Agent + + +class StandardCommunicationModule(CommunicationModule): + ESCAPE_CHAR = bitarray("11111111") + SIZE_ID: int = 5 + SIZE_TTL: int = 3 + + def receive(self, agent: Agent, message_manager: MessageManager) -> None: + heard_commands = agent._agent_info.get_heard_commands() + for command in heard_commands: + if isinstance(command, AKSpeak): + sender_entity_id = command.agent_id + if sender_entity_id == agent.get_entity_id(): + continue + data = command.message + is_wireless_message = command.channel != 0 + + if len(data) == 0: + continue + + if is_wireless_message: + bit_array = bitarray() + bit_array.frombytes(data) + self.add_received_message( + message_manager, + is_wireless_message, + sender_entity_id, + bit_array, + ) + else: + try: + voice_message = data.decode("utf-8") + if voice_message.startswith("Help") or voice_message.startswith( + "Ouch" + ): + message_manager.add_heard_agent_help_message_count() + continue + except UnicodeDecodeError: + pass + + escape_char = self.ESCAPE_CHAR.tobytes()[0] + i = 0 + bit_array = bitarray() + a = bitarray() + a.frombytes(data) + while i < len(data): + if data[i] == escape_char: + if (i + 1) >= len(data): + self.add_received_message( + message_manager, + False, + sender_entity_id, + bit_array, + ) + break + elif data[i + 1] != escape_char: + self.add_received_message( + message_manager, + False, + sender_entity_id, + bit_array, + ) + bit_array.clear() + i += 1 # Skip the next character + continue + i += 1 # Skip the escaped character + bits = bitarray() + bits.frombytes(data[i].to_bytes(1, "big")) + bit_array.extend(bits) + i += 1 + + def send(self, agent: Agent, message_manager: MessageManager) -> None: + voice_message_limit_bytes = agent._scenario_info.get_value( + "comms.channels.0.messages.size", 256 + ) + left_voice_message_limit_bits = voice_message_limit_bytes * 8 + voice_message_bit_array = bitarray() + + send_messages = message_manager.get_channel_send_message_list() + + for channel in range(len(send_messages)): + for message in send_messages[channel]: + message_class_index = message_manager.get_message_class_index( + type(message) + ) + bit_array = bitarray() + + write_with_exist_flag(bit_array, message_class_index, self.SIZE_ID) + + bit_array.extend(message.to_bits()) + + if channel > 0: + agent.send_speak( + agent._agent_info.get_time(), + bit_array, + channel, + ) + else: + # bit_arrayを8bitごとに区切れるようにして、エスケープ処理を行う + if len(bit_array) % 8 != 0: + bit_array.extend([False] * (8 - len(bit_array) % 8)) + message_bit_size = len(bit_array) + if message_bit_size <= left_voice_message_limit_bits: + esceped_message_data = bitarray() + for i in range(len(bit_array) // 8): + if bit_array[(i * 8) : ((i + 1) * 8)] == self.ESCAPE_CHAR: + esceped_message_data.extend(self.ESCAPE_CHAR) + esceped_message_data.extend( + bit_array[(i * 8) : ((i + 1) * 8)] + ) + esceped_message_data.extend(self.ESCAPE_CHAR) + if len(esceped_message_data) <= voice_message_limit_bytes: + left_voice_message_limit_bits -= len(esceped_message_data) + voice_message_bit_array.extend(esceped_message_data) + + if len(voice_message_bit_array) > 0: + agent.send_speak( + agent._agent_info.get_time(), + voice_message_bit_array, + 0, + ) + + def add_received_message( + self, + message_manager: MessageManager, + is_wireless_message: bool, + sender_entity_id: EntityID, + data: bitarray, + ) -> None: + message_class_index = read_with_exist_flag(data, self.SIZE_ID) + if message_class_index is None or message_class_index < 0: + get_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}" + ).warning(f"Invalid message class index: {message_class_index}") + return + + message = message_manager.get_message_class(message_class_index) + if message is None: + get_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}" + ).warning(f"Invalid message class index: {message_class_index}") + return + + if issubclass(message, StandardMessage): + message_instance = message.from_bits( + data, is_wireless_message, sender_entity_id + ) + message_manager.add_received_message(message_instance) + else: + get_logger( + f"{self.__class__.__module__}.{self.__class__.__qualname__}" + ).warning(f"Invalid message class: {message}") + return diff --git a/adf_core_python/core/agent/communication/standard/utility/__init__.py b/adf_core_python/core/agent/communication/standard/utility/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/communication/standard/utility/apply_to_world_info.py b/adf_core_python/core/agent/communication/standard/utility/apply_to_world_info.py new file mode 100644 index 00000000..811c152d --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/utility/apply_to_world_info.py @@ -0,0 +1,335 @@ +from rcrs_core.entities.ambulanceTeam import AmbulanceTeamEntity +from rcrs_core.entities.blockade import Blockade +from rcrs_core.entities.building import Building +from rcrs_core.entities.civilian import Civilian +from rcrs_core.entities.fireBrigade import FireBrigadeEntity +from rcrs_core.entities.policeForce import PoliceForceEntity +from rcrs_core.entities.road import Road + +from adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team import ( + MessageAmbulanceTeam, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_building import ( + MessageBuilding, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_civilian import ( + MessageCivilian, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade import ( + MessageFireBrigade, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_police_force import ( + MessagePoliceForce, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_road import ( + MessageRoad, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.info.world_info import WorldInfo + + +def apply_to_world_info( + world_info: WorldInfo, + standard_message: StandardMessage, +) -> None: + """ + Apply to world info. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + + if isinstance(standard_message, MessageAmbulanceTeam): + _apply_to_world_info_ambulance_team(world_info, standard_message) + elif isinstance(standard_message, MessageFireBrigade): + _apply_to_world_info_fire_brigade(world_info, standard_message) + elif isinstance(standard_message, MessagePoliceForce): + _apply_to_world_info_police_force(world_info, standard_message) + elif isinstance(standard_message, MessageCivilian): + _apply_to_world_info_civilian(world_info, standard_message) + elif isinstance(standard_message, MessageBuilding): + _apply_to_world_info_building(world_info, standard_message) + elif isinstance(standard_message, MessageRoad): + _apply_to_world_info_road(world_info, standard_message) + else: + return + + +def _apply_to_world_info_ambulance_team( + world_info: WorldInfo, + message_ambulance_team: MessageAmbulanceTeam, +) -> None: + """ + Apply to world info for ambulance team. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_ambulance_team.get_ambulance_team_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + ambulance = AmbulanceTeamEntity(entity_id.get_value()) + if (hp := message_ambulance_team.get_ambulance_team_hp()) is not None: + ambulance.set_hp(hp) + if (damege := message_ambulance_team.get_ambulance_team_damage()) is not None: + ambulance.set_damage(damege) + if ( + buriedness := message_ambulance_team.get_ambulance_team_buriedness() + ) is not None: + ambulance.set_buriedness(buriedness) + if ( + position := message_ambulance_team.get_ambulance_team_position() + ) is not None: + ambulance.set_position(position) + world_info.add_entity(ambulance) + else: + if isinstance(entity, AmbulanceTeamEntity): + if (hp := message_ambulance_team.get_ambulance_team_hp()) is not None: + entity.set_hp(hp) + if ( + damege := message_ambulance_team.get_ambulance_team_damage() + ) is not None: + entity.set_damage(damege) + if ( + buriedness := message_ambulance_team.get_ambulance_team_buriedness() + ) is not None: + entity.set_buriedness(buriedness) + if ( + position := message_ambulance_team.get_ambulance_team_position() + ) is not None: + entity.set_position(position) + + +def _apply_to_world_info_fire_brigade( + world_info: WorldInfo, + message_fire_brigade: MessageFireBrigade, +) -> None: + """ + Apply to world info for fire brigade. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_fire_brigade.get_fire_brigade_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + fire_brigade = FireBrigadeEntity(entity_id.get_value()) + if (hp := message_fire_brigade.get_fire_brigade_hp()) is not None: + fire_brigade.set_hp(hp) + if (damage := message_fire_brigade.get_fire_brigade_damage()) is not None: + fire_brigade.set_damage(damage) + if ( + buriedness := message_fire_brigade.get_fire_brigade_buriedness() + ) is not None: + fire_brigade.set_buriedness(buriedness) + if (position := message_fire_brigade.get_fire_brigade_position()) is not None: + fire_brigade.set_position(position) + if (water := message_fire_brigade.get_fire_brigade_water()) is not None: + fire_brigade.set_water(water) + world_info.add_entity(fire_brigade) + else: + if isinstance(entity, FireBrigadeEntity): + if (hp := message_fire_brigade.get_fire_brigade_hp()) is not None: + entity.set_hp(hp) + if (damage := message_fire_brigade.get_fire_brigade_damage()) is not None: + entity.set_damage(damage) + if ( + buriedness := message_fire_brigade.get_fire_brigade_buriedness() + ) is not None: + entity.set_buriedness(buriedness) + if ( + position := message_fire_brigade.get_fire_brigade_position() + ) is not None: + entity.set_position(position) + if (water := message_fire_brigade.get_fire_brigade_water()) is not None: + entity.set_water(water) + + +def _apply_to_world_info_police_force( + world_info: WorldInfo, + message_police_force: MessagePoliceForce, +) -> None: + """ + Apply to world info for police force. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_police_force.get_police_force_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + police_force = PoliceForceEntity(entity_id.get_value()) + if (hp := message_police_force.get_police_force_hp()) is not None: + police_force.set_hp(hp) + if (damage := message_police_force.get_police_force_damage()) is not None: + police_force.set_damage(damage) + if ( + buriedness := message_police_force.get_police_force_buriedness() + ) is not None: + police_force.set_buriedness(buriedness) + if (position := message_police_force.get_police_force_position()) is not None: + police_force.set_position(position) + world_info.add_entity(police_force) + else: + if isinstance(entity, PoliceForceEntity): + if (hp := message_police_force.get_police_force_hp()) is not None: + entity.set_hp(hp) + if (damage := message_police_force.get_police_force_damage()) is not None: + entity.set_damage(damage) + if ( + buriedness := message_police_force.get_police_force_buriedness() + ) is not None: + entity.set_buriedness(buriedness) + if ( + position := message_police_force.get_police_force_position() + ) is not None: + entity.set_position(position) + + +def _apply_to_world_info_civilian( + world_info: WorldInfo, + message_civilian: MessageCivilian, +) -> None: + """ + Apply to world info for civilian. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_civilian.get_civilian_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + civilian = Civilian(entity_id.get_value()) + if (hp := message_civilian.get_civilian_hp()) is not None: + civilian.set_hp(hp) + if (damage := message_civilian.get_civilian_damage()) is not None: + civilian.set_damage(damage) + if (buriedness := message_civilian.get_civilian_buriedness()) is not None: + civilian.set_buriedness(buriedness) + if (position := message_civilian.get_civilian_position()) is not None: + civilian.set_position(position) + world_info.add_entity(civilian) + else: + if isinstance(entity, Civilian): + if (hp := message_civilian.get_civilian_hp()) is not None: + entity.set_hp(hp) + if (damage := message_civilian.get_civilian_damage()) is not None: + entity.set_damage(damage) + if (buriedness := message_civilian.get_civilian_buriedness()) is not None: + entity.set_buriedness(buriedness) + if (position := message_civilian.get_civilian_position()) is not None: + entity.set_position(position) + + +def _apply_to_world_info_building( + world_info: WorldInfo, + message_building: MessageBuilding, +) -> None: + """ + Apply to world info for building. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_building.get_building_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if entity is None: + building = Building(entity_id.get_value()) + if (fieryness := message_building.get_building_fireyness()) is not None: + building.set_fieryness(fieryness) + if (brokenness := message_building.get_building_brokenness()) is not None: + building.set_brokenness(brokenness) + if (temperature := message_building.get_building_temperature()) is not None: + building.set_temperature(temperature) + world_info.add_entity(building) + else: + if isinstance(entity, Building): + if (fieryness := message_building.get_building_fireyness()) is not None: + entity.set_fieryness(fieryness) + if (brokenness := message_building.get_building_brokenness()) is not None: + entity.set_brokenness(brokenness) + if (temperature := message_building.get_building_temperature()) is not None: + entity.set_temperature(temperature) + + +def _apply_to_world_info_road( + world_info: WorldInfo, + message_road: MessageRoad, +) -> None: + """ + Apply to world info for road. + + PARAMETERS + ---------- + world_info: WorldInfo + The world info to apply to. + standard_message: StandardMessage + The standard message to apply to world info. + """ + entity_id = message_road.get_road_entity_id() + if entity_id is None: + return + entity = world_info.get_entity(entity_id) + if not isinstance(entity, Road): + return + + blockade_entity_id = message_road.get_road_blockade_entity_id() + if blockade_entity_id is None: + return + + blockade = world_info.get_entity(blockade_entity_id) + if blockade is None: + road_blockade = Blockade(blockade_entity_id.get_value()) + if (repair_cost := message_road.get_road_blockade_repair_cost()) is not None: + road_blockade.set_repaire_cost(repair_cost) + if (x := message_road.get_road_blockade_x()) is not None: + road_blockade.set_x(x) + if (y := message_road.get_road_blockade_y()) is not None: + road_blockade.set_y(y) + world_info.add_entity(road_blockade) + else: + if isinstance(blockade, Blockade): + if ( + repair_cost := message_road.get_road_blockade_repair_cost() + ) is not None: + blockade.set_repaire_cost(repair_cost) + if (x := message_road.get_road_blockade_x()) is not None: + blockade.set_x(x) + if (y := message_road.get_road_blockade_y()) is not None: + blockade.set_y(y) diff --git a/adf_core_python/core/agent/communication/standard/utility/bitarray_with_exits_flag.py b/adf_core_python/core/agent/communication/standard/utility/bitarray_with_exits_flag.py new file mode 100644 index 00000000..64ab01b3 --- /dev/null +++ b/adf_core_python/core/agent/communication/standard/utility/bitarray_with_exits_flag.py @@ -0,0 +1,72 @@ +from typing import Optional + +from bitarray import bitarray + +IS_EXIST_FLAG = 1 +IS_NOT_EXIST_FLAG = 0 + + +def write_with_exist_flag( + bit_array: bitarray, value: Optional[int], bit_size: int +) -> None: + """ + Write value to bit_array with an exist flag. + If value is None, write IS_NOT_EXIST_FLAG to bit_array. + If value is not None, write IS_EXIST_FLAG to bit_array and then write value to bit_array. + + PARAMETERS + ---------- + bit_array: bitarray + The bitarray to write to. + value: Optional[int] + The value to write. + bit_size: int + The number of bits to use to write value. + + RAISES + ------ + ValueError + If value is too large to fit into bit_size bits. + """ + if value is None: + bit_array.extend([IS_NOT_EXIST_FLAG]) + else: + bit_array.extend([IS_EXIST_FLAG]) + bit_value = bitarray(f"{value:0{bit_size}b}") + if len(bit_value) > bit_size: + raise ValueError(f"Value {value} is too large to fit into {bit_size} bits") + bit_array.extend(bit_value) + + +def read_with_exist_flag(bit_array: bitarray, size: int) -> Optional[int]: + """ + Read value from bit_array with an exist flag. + If the first bit is IS_NOT_EXIST_FLAG, return None. + If the first bit is IS_EXIST_FLAG, read and return value from bit_array. + + PARAMETERS + ---------- + bit_array: bitarray + The bitarray to read from. + size: int + The number of bits to read to get value. + + RETURNS + ------- + Optional[int] + The value read from bit_array. + + RAISES + ------ + ValueError + If the first bit is not IS_EXIST_FLAG or IS_NOT_EXIST_FLAG. + """ + exist_flag = bit_array.pop(0) + if exist_flag == IS_NOT_EXIST_FLAG: + return None + elif exist_flag == IS_EXIST_FLAG: + value = int(bit_array[:size].to01(), 2) + del bit_array[:size] + return value + else: + raise ValueError("Invalid exist flag") diff --git a/adf_core_python/core/agent/info/__init__.py b/adf_core_python/core/agent/info/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/info/agent_info.py b/adf_core_python/core/agent/info/agent_info.py index 79e8cf28..b3e78ee4 100644 --- a/adf_core_python/core/agent/info/agent_info.py +++ b/adf_core_python/core/agent/info/agent_info.py @@ -1,7 +1,9 @@ +from __future__ import annotations + from time import time -from typing import Any +from typing import TYPE_CHECKING, Any -from rcrs_core.agents.agent import Agent +from rcrs_core.commands.Command import Command from rcrs_core.entities.civilian import Civilian from rcrs_core.entities.entity import Entity from rcrs_core.entities.human import Human @@ -11,6 +13,9 @@ from adf_core_python.core.agent.action.action import Action +if TYPE_CHECKING: + from adf_core_python.core.agent.agent import Agent + class AgentInfo: # TODO: Replace Any with the actual type @@ -45,24 +50,24 @@ def get_time(self) -> int: """ return self._time - def set_heard_commands(self, heard_commands: list[Any]) -> None: + def set_heard_commands(self, heard_commands: list[Command]) -> None: """ Set the heard commands Parameters ---------- - heard_commands : list[Any] + heard_commands : list[Command] Heard commands """ self._heard_commands = heard_commands - def get_heard_commands(self) -> list[Any]: + def get_heard_commands(self) -> list[Command]: """ Get the heard commands Returns ------- - list[Any] + list[Command] Heard commands """ return self._heard_commands @@ -77,7 +82,7 @@ def get_entity_id(self) -> EntityID: Entity ID of the agent """ # TODO: Agent class should return EntityID instead of EntityID | None - return self._agent.get_id() # type: ignore + return self._agent.get_entity_id() def get_myself(self) -> Entity: """ diff --git a/adf_core_python/core/agent/info/scenario_info.py b/adf_core_python/core/agent/info/scenario_info.py index fe085bf2..85bf7dab 100644 --- a/adf_core_python/core/agent/info/scenario_info.py +++ b/adf_core_python/core/agent/info/scenario_info.py @@ -1,8 +1,10 @@ from enum import Enum -from typing import Any +from typing import TypeVar from adf_core_python.core.config.config import Config +T = TypeVar("T") + class Mode(Enum): NON_PRECOMPUTE = 0 @@ -10,6 +12,37 @@ class Mode(Enum): PRECOMPUTATION = 2 +class ScenarioInfoKeys: + KERNEL_PERCEPTION = "kernel.perception" + PERCEPTION_LOS_PRECISION_HP = "perception.los.precision.hp" + CLEAR_REPAIR_RAD = "clear.repair.rad" + FIRE_TANK_REFILL_HYDRANT_RATE = "fire.tank.refill_hydrant_rate" + SCENARIO_AGENTS_PF = "scenario.agents.pf" + SCENARIO_AGENTS_FS = "scenario.agents.fs" + VOICE_MESSAGES_SIZE = "comms.channels.0.messages.size" + FIRE_TANK_REFILL_RATE = "fire.tank.refill_rate" + KERNEL_TIMESTEPS = "kernel.timesteps" + FIRE_EXTINGUISH_MAX_SUM = "fire.extinguish.max-sum" + COMMUNICATION_CHANNELS_MAX_PLATOON = "comms.channels.max.platoon" + KERNEL_AGENTS_THINK_TIME = "kernel.agents.think-time" + FIRE_TANK_MAXIMUM = "fire.tank.maximum" + CLEAR_REPAIR_RATE = "clear.repair.rate" + KERNEL_STARTUP_CONNECT_TIME = "kernel.startup.connect-time" + KERNEL_HOST = "kernel.host" + SCENARIO_AGENTS_AT = "scenario.agents.at" + PERCEPTION_LOS_MAX_DISTANCE = "perception.los.max-distance" + SCENARIO_AGENTS_FB = "scenario.agents.fb" + SCENARIO_AGENTS_PO = "scenario.agents.po" + KERNEL_COMMUNICATION_MODEL = "kernel.communication-model" + PERCEPTION_LOS_PRECISION_DAMAGE = "perception.los.precision.damage" + SCENARIO_AGENTS_AC = "scenario.agents.ac" + COMMUNICATION_CHANNELS_MAX_OFFICE = "comms.channels.max.centre" + FIRE_EXTINGUISH_MAX_DISTANCE = "fire.extinguish.max-distance" + KERNEL_AGENTS_IGNOREUNTIL = "kernel.agents.ignoreuntil" + CLEAR_REPAIR_DISTANCE = "clear.repair.distance" + COMMUNICATION_CHANNELS_COUNT = "comms.channels.count" + + class ScenarioInfo: def __init__(self, config: Config, mode: Mode): """ @@ -58,7 +91,7 @@ def get_mode(self) -> Mode: """ return self._mode - def get_value(self, key: str, default: Any) -> Any: + def get_value(self, key: str, default: T) -> T: """ Get the value of the configuration @@ -74,4 +107,11 @@ def get_value(self, key: str, default: Any) -> Any: Any Value of the configuration """ - return self._config.get_value(key, default) + value = self._config.get_value(key, default) + if not isinstance(value, type(default)): + try: + return type(default)(value) # type: ignore + except (ValueError, TypeError): + # 型変換に失敗した場合はそのままデフォルト値を返す + return default + return value diff --git a/adf_core_python/core/agent/info/world_info.py b/adf_core_python/core/agent/info/world_info.py index 223bfb14..5bd66761 100644 --- a/adf_core_python/core/agent/info/world_info.py +++ b/adf_core_python/core/agent/info/world_info.py @@ -171,3 +171,14 @@ def get_bloackades(self, area: Area) -> set[Blockade]: if isinstance(bloackde_entity, Blockade): bloakcades.add(cast(Blockade, bloackde_entity)) return bloakcades + + def add_entity(self, entity: Entity) -> None: + """ + Add the entity + + Parameters + ---------- + entity : Entity + Entity + """ + self._world_model.add_entity(entity) diff --git a/adf_core_python/core/agent/module/module_manager.py b/adf_core_python/core/agent/module/module_manager.py index b15dd8e1..7609e62a 100644 --- a/adf_core_python/core/agent/module/module_manager.py +++ b/adf_core_python/core/agent/module/module_manager.py @@ -4,6 +4,12 @@ from typing import TYPE_CHECKING, Any from adf_core_python.core.component.action.extend_action import ExtendAction +from adf_core_python.core.component.communication.channel_subscriber import ( + ChannelSubscriber, +) +from adf_core_python.core.component.communication.message_coordinator import ( + MessageCoordinator, +) from adf_core_python.core.component.module.abstract_module import AbstractModule if TYPE_CHECKING: @@ -92,6 +98,58 @@ def get_extend_action( raise RuntimeError(f"Action {class_name} is not a subclass of ExtendAction") + def get_channel_subscriber( + self, channel_subscriber_name: str, default_channel_subscriber_name: str + ) -> ChannelSubscriber: + class_name = self._module_config.get_value_or_default( + channel_subscriber_name, default_channel_subscriber_name + ) + + try: + channel_subscriber_class: type = self._load_module(class_name) + except (ImportError, AttributeError) as e: + raise RuntimeError(f"Failed to load channel subscriber {class_name}") from e + + instance = self._channel_subscribers.get(channel_subscriber_name) + if instance is not None: + return instance + + if issubclass(channel_subscriber_class, ChannelSubscriber): + instance = channel_subscriber_class() + self._channel_subscribers[channel_subscriber_name] = instance + return instance + + raise RuntimeError( + f"Channel subscriber {class_name} is not a subclass of ChannelSubscriber" + ) + + def get_message_coordinator( + self, message_coordinator_name: str, default_message_coordinator_name: str + ) -> MessageCoordinator: + class_name = self._module_config.get_value_or_default( + message_coordinator_name, default_message_coordinator_name + ) + + try: + message_coordinator_class: type = self._load_module(class_name) + except (ImportError, AttributeError) as e: + raise RuntimeError( + f"Failed to load message coordinator {class_name}" + ) from e + + instance = self._message_coordinators.get(message_coordinator_name) + if instance is not None: + return instance + + if issubclass(message_coordinator_class, MessageCoordinator): + instance = message_coordinator_class() + self._message_coordinators[message_coordinator_name] = instance + return instance + + raise RuntimeError( + f"Message coordinator {class_name} is not a subclass of MessageCoordinator" + ) + def _load_module(self, class_name: str) -> type: module_name, module_class_name = class_name.rsplit(".", 1) module = importlib.import_module(module_name) diff --git a/adf_core_python/core/agent/platoon/__init__.py b/adf_core_python/core/agent/platoon/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/agent/platoon/platoon.py b/adf_core_python/core/agent/platoon/platoon.py index 6ffcd020..01babaaa 100644 --- a/adf_core_python/core/agent/platoon/platoon.py +++ b/adf_core_python/core/agent/platoon/platoon.py @@ -1,20 +1,11 @@ -# from rcrs_core.agents.agent import Agent -from rcrs_core.commands.Command import Command -from rcrs_core.config.config import Config as RCRSConfig -from rcrs_core.worldmodel.changeSet import ChangeSet - from adf_core_python.core.agent.action.action import Action from adf_core_python.core.agent.agent import Agent -from adf_core_python.core.agent.communication.message_manager import MessageManager from adf_core_python.core.agent.config.module_config import ModuleConfig from adf_core_python.core.agent.develop.develop_data import DevelopData -from adf_core_python.core.agent.info.agent_info import AgentInfo -from adf_core_python.core.agent.info.scenario_info import Mode, ScenarioInfo -from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.agent.info.scenario_info import Mode from adf_core_python.core.agent.module.module_manager import ModuleManager from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData from adf_core_python.core.component.tactics.tactics_agent import TacticsAgent -from adf_core_python.core.config.config import Config from adf_core_python.core.logger.logger import get_agent_logger @@ -29,7 +20,15 @@ def __init__( module_config: ModuleConfig, develop_data: DevelopData, ) -> None: - super().__init__(is_precompute, self.__class__.__qualname__) + super().__init__( + is_precompute, + self.__class__.__qualname__, + is_debug, + team_name, + data_storage_name, + module_config, + develop_data, + ) self._tactics_agent = tactics_agent self._team_name = team_name self._is_precompute = is_precompute @@ -39,39 +38,14 @@ def __init__( self._develop_data = develop_data def post_connect(self) -> None: - self._agent_info: AgentInfo = AgentInfo(self, self.world_model) - self._world_info: WorldInfo = WorldInfo(self.world_model) - self._precompute_data: PrecomputeData = PrecomputeData(self._data_storage_name) + super().post_connect() + self.precompute_data: PrecomputeData = PrecomputeData(self._data_storage_name) self._logger = get_agent_logger( f"{self.__class__.__module__}.{self.__class__.__qualname__}", self._agent_info, ) - if self._is_precompute: - self._mode = Mode.PRECOMPUTATION - else: - # if self._precompute_data.is_ready(): - # self._mode = Mode.PRECOMPUTED - # else: - # self._mode = Mode.NON_PRECOMPUTE - self._mode = Mode.NON_PRECOMPUTE - - config = Config() - if self.config is not None: - rcrc_config: RCRSConfig = self.config - for key, value in rcrc_config.data.items(): # type: ignore - config.set_value(key, value) - for key, value in rcrc_config.int_data.items(): # type: ignore - config.set_value(key, value) - for key, value in rcrc_config.float_data.items(): # type: ignore - config.set_value(key, value) - for key, value in rcrc_config.boolean_data.items(): # type: ignore - config.set_value(key, value) - for key, value in rcrc_config.array_data.items(): # type: ignore - config.set_value(key, value) - - self._scenario_info: ScenarioInfo = ScenarioInfo(config, self._mode) self._module_manager: ModuleManager = ModuleManager( self._agent_info, self._world_info, @@ -80,17 +54,30 @@ def post_connect(self) -> None: self._develop_data, ) + self._message_manager.set_channel_subscriber( + self._module_manager.get_channel_subscriber( + "MessageManager.PlatoonChannelSubscriber", + "adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber", + ) + ) + self._message_manager.set_message_coordinator( + self._module_manager.get_message_coordinator( + "MessageManager.PlatoonMessageCoordinator", + "adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator", + ) + ) + self._tactics_agent.initialize( self._agent_info, self._world_info, self._scenario_info, self._module_manager, self._precompute_data, - MessageManager(), + self._message_manager, self._develop_data, ) - match self._mode: + match self._scenario_info.get_mode(): case Mode.PRECOMPUTATION: pass case Mode.PRECOMPUTED: @@ -101,25 +88,24 @@ def post_connect(self) -> None: self._world_info, self._scenario_info, self._module_manager, - self._precompute_data, + self.precompute_data, self._develop_data, ) - def think(self, time: int, change_set: ChangeSet, hear: list[Command]) -> None: - self._agent_info.set_change_set(change_set) - self._world_info.set_change_set(change_set) - self._agent_info.set_time(time) - self._agent_info.set_heard_commands(hear) - + def think(self) -> None: action: Action = self._tactics_agent.think( self._agent_info, self._world_info, self._scenario_info, self._module_manager, self._precompute_data, - MessageManager(), + self._message_manager, self._develop_data, ) if action is not None and self.agent_id is not None: - self._agent_info.set_executed_action(time, action) - self.send_msg(action.get_command(self.agent_id, time).prepare_cmd()) + self._agent_info.set_executed_action(self._agent_info.get_time(), action) + self.send_msg( + action.get_command( + self.agent_id, self._agent_info.get_time() + ).prepare_cmd() + ) diff --git a/adf_core_python/core/agent/precompute/__init__.py b/adf_core_python/core/agent/precompute/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/component/action/__init__.py b/adf_core_python/core/component/action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/component/communication/__init__.py b/adf_core_python/core/component/communication/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/component/communication/channel_subscriber.py b/adf_core_python/core/component/communication/channel_subscriber.py new file mode 100644 index 00000000..d2da83f2 --- /dev/null +++ b/adf_core_python/core/component/communication/channel_subscriber.py @@ -0,0 +1,37 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + + +class ChannelSubscriber(ABC): + @abstractmethod + def subscribe( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + ) -> list[int]: + """ + Subscribe to the channel. + + Parameters + ---------- + agent_info : AgentInfo + The agent info. + world_info : WorldInfo + The world info. + scenario_info : ScenarioInfo + The scenario info. + + Returns + ------- + list[int] + The list of subscribed channels. + """ + pass diff --git a/adf_core_python/core/component/communication/communication_message.py b/adf_core_python/core/component/communication/communication_message.py index 2909fcf2..60972ff4 100644 --- a/adf_core_python/core/component/communication/communication_message.py +++ b/adf_core_python/core/component/communication/communication_message.py @@ -1,5 +1,9 @@ +from __future__ import annotations + from abc import ABC, abstractmethod +from bitarray import bitarray + class CommunicationMessage(ABC): def __init__(self, is_wireless_message: bool) -> None: @@ -9,11 +13,13 @@ def is_wireless_message(self) -> bool: return self._is_wireless_message @abstractmethod - def get_byte_size(self) -> int: + def get_bit_size(self) -> int: raise NotImplementedError @abstractmethod - def to_bytes(self) -> bytes: + def to_bits(self) -> bitarray: raise NotImplementedError - # TODO: Implement the toBitOutputStream and getCheckKey methods + @abstractmethod + def __hash__(self) -> int: + raise NotImplementedError diff --git a/adf_core_python/core/component/communication/communication_module.py b/adf_core_python/core/component/communication/communication_module.py new file mode 100644 index 00000000..2e87e31c --- /dev/null +++ b/adf_core_python/core/component/communication/communication_module.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from adf_core_python.core.agent.agent import Agent + from adf_core_python.core.agent.communication.message_manager import MessageManager + + +class CommunicationModule(ABC): + @abstractmethod + def receive(self, agent: Agent, message_manager: MessageManager) -> None: + pass + + @abstractmethod + def send(self, agent: Agent, message_manager: MessageManager) -> None: + pass diff --git a/adf_core_python/core/component/communication/message_coordinator.py b/adf_core_python/core/component/communication/message_coordinator.py new file mode 100644 index 00000000..9d8cb1be --- /dev/null +++ b/adf_core_python/core/component/communication/message_coordinator.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from adf_core_python.core.agent.communication.message_manager import MessageManager + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, + ) + + +class MessageCoordinator(ABC): + @abstractmethod + def coordinate( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + message_manager: MessageManager, + send_message_list: list[CommunicationMessage], + channel_send_message_list: list[list[CommunicationMessage]], + ) -> None: + pass diff --git a/adf_core_python/core/component/module/__init__.py b/adf_core_python/core/component/module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/component/module/algorithm/__init__.py b/adf_core_python/core/component/module/algorithm/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/component/module/complex/__init__.py b/adf_core_python/core/component/module/complex/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/component/tactics/__init__.py b/adf_core_python/core/component/tactics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/config/__init__.py b/adf_core_python/core/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/launcher/__init__.py b/adf_core_python/core/launcher/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/launcher/connect/__init__.py b/adf_core_python/core/launcher/connect/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/core/launcher/connect/component_launcher.py b/adf_core_python/core/launcher/connect/component_launcher.py index 027bb85f..56a142ce 100644 --- a/adf_core_python/core/launcher/connect/component_launcher.py +++ b/adf_core_python/core/launcher/connect/component_launcher.py @@ -1,8 +1,8 @@ import socket -from rcrs_core.agents.agent import Agent from structlog import BoundLogger +from adf_core_python.core.agent.agent import Agent from adf_core_python.core.launcher.connect.connection import Connection @@ -17,7 +17,7 @@ def make_connection(self) -> Connection: return Connection(self.host, self.port) def connect(self, agent: Agent, _request_id: int) -> None: - self.logger.bind(agent_id=agent.get_id()) + # self.logger.bind(agent_id=agent.get_id()) self.logger.info( f"{agent.__class__.__name__} connecting to {self.host}:{self.port} request_id: {_request_id}" @@ -33,8 +33,10 @@ def connect(self, agent: Agent, _request_id: int) -> None: except socket.timeout: self.logger.warning(f"Connection to {self.host}:{self.port} timed out") return - except socket.error: - self.logger.error(f"Failed to connect to {self.host}:{self.port}") + except socket.error as e: + self.logger.exception( + f"Failed to connect to {self.host}:{self.port}", exception=str(e) + ) return connection.message_received(agent.message_received) @@ -44,7 +46,10 @@ def connect(self, agent: Agent, _request_id: int) -> None: try: connection.parse_message_from_kernel() except Exception as e: - self.logger.error(f"Failed to connect agent: {self.host}:{self.port} {e}") + self.logger.exception( + f"Failed to connect agent: {self.host}:{self.port} {e}", + exception=str(e), + ) def generate_request_id(self) -> int: self.request_id += 1 diff --git a/adf_core_python/core/launcher/connect/connector_ambulance_centre.py b/adf_core_python/core/launcher/connect/connector_ambulance_centre.py index 01949183..8004fb9d 100644 --- a/adf_core_python/core/launcher/connect/connector_ambulance_centre.py +++ b/adf_core_python/core/launcher/connect/connector_ambulance_centre.py @@ -52,6 +52,7 @@ def connect( ), ) + request_id: int = component_launcher.generate_request_id() # TODO: component_launcher.generate_request_ID can cause race condition thread = threading.Thread( target=component_launcher.connect, @@ -59,8 +60,9 @@ def connect( AmbulanceCenterAgent( config.get_value(ConfigKey.KEY_PRECOMPUTE, False), ), # type: ignore - component_launcher.generate_request_id(), + request_id, ), + name=f"AmbulanceCentreAgent-{request_id}", ) threads.append(thread) diff --git a/adf_core_python/core/launcher/connect/connector_ambulance_team.py b/adf_core_python/core/launcher/connect/connector_ambulance_team.py index 15da390c..350d6bd8 100644 --- a/adf_core_python/core/launcher/connect/connector_ambulance_team.py +++ b/adf_core_python/core/launcher/connect/connector_ambulance_team.py @@ -53,6 +53,7 @@ def connect( ), ) + request_id: int = component_launcher.generate_request_id() thread = threading.Thread( target=component_launcher.connect, args=( @@ -65,8 +66,9 @@ def connect( module_config, develop_data, ), - component_launcher.generate_request_id(), + request_id, ), + name=f"AmbulanceTeam-{request_id}", ) threads.append(thread) diff --git a/adf_core_python/core/launcher/connect/connector_fire_brigade.py b/adf_core_python/core/launcher/connect/connector_fire_brigade.py index fab7095e..9fc24aa7 100644 --- a/adf_core_python/core/launcher/connect/connector_fire_brigade.py +++ b/adf_core_python/core/launcher/connect/connector_fire_brigade.py @@ -51,6 +51,7 @@ def connect( ), ) + request_id: int = component_launcher.generate_request_id() thread = threading.Thread( target=component_launcher.connect, args=( @@ -63,8 +64,9 @@ def connect( module_config, develop_data, ), - component_launcher.generate_request_id(), + request_id, ), + name=f"FireBrigadeAgent-{request_id}", ) threads.append(thread) diff --git a/adf_core_python/core/launcher/connect/connector_fire_station.py b/adf_core_python/core/launcher/connect/connector_fire_station.py index 49bd190a..cd5632e4 100644 --- a/adf_core_python/core/launcher/connect/connector_fire_station.py +++ b/adf_core_python/core/launcher/connect/connector_fire_station.py @@ -52,15 +52,16 @@ def connect( ), ) - # TODO: component_launcher.generate_request_ID can cause race condition + request_id: int = component_launcher.generate_request_id() thread = threading.Thread( target=component_launcher.connect, args=( FireStationAgent( config.get_value(ConfigKey.KEY_PRECOMPUTE, False), ), # type: ignore - component_launcher.generate_request_id(), + request_id, ), + name=f"FireStationAgent-{request_id}", ) threads.append(thread) diff --git a/adf_core_python/core/launcher/connect/connector_police_force.py b/adf_core_python/core/launcher/connect/connector_police_force.py index bbf93596..2b3a905d 100644 --- a/adf_core_python/core/launcher/connect/connector_police_force.py +++ b/adf_core_python/core/launcher/connect/connector_police_force.py @@ -51,6 +51,7 @@ def connect( ), ) + request_id: int = component_launcher.generate_request_id() thread = threading.Thread( target=component_launcher.connect, args=( @@ -63,8 +64,9 @@ def connect( module_config, develop_data, ), - component_launcher.generate_request_id(), + request_id, ), + name=f"PoliceForceAgent-{request_id}", ) threads.append(thread) diff --git a/adf_core_python/core/launcher/connect/connector_police_office.py b/adf_core_python/core/launcher/connect/connector_police_office.py index 32e90607..b20da2e4 100644 --- a/adf_core_python/core/launcher/connect/connector_police_office.py +++ b/adf_core_python/core/launcher/connect/connector_police_office.py @@ -52,15 +52,16 @@ def connect( ), ) - # TODO: component_launcher.generate_request_ID can cause race condition + request_id: int = component_launcher.generate_request_id() thread = threading.Thread( target=component_launcher.connect, args=( PoliceOfficeAgent( config.get_value(ConfigKey.KEY_PRECOMPUTE, False), ), # type: ignore - component_launcher.generate_request_id(), + request_id, ), + name=f"PoliceOfficeAgent-{request_id}", ) threads.append(thread) diff --git a/adf_core_python/core/logger/__init__.py b/adf_core_python/core/logger/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/implement/__init__.py b/adf_core_python/implement/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/implement/action/__init__.py b/adf_core_python/implement/action/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/implement/action/default_extend_action_rescue.py b/adf_core_python/implement/action/default_extend_action_rescue.py index 845ad188..67f4ff36 100644 --- a/adf_core_python/implement/action/default_extend_action_rescue.py +++ b/adf_core_python/implement/action/default_extend_action_rescue.py @@ -1,4 +1,4 @@ -from typing import cast, Optional +from typing import Optional, cast from rcrs_core.entities.area import Area from rcrs_core.entities.blockade import Blockade @@ -12,7 +12,11 @@ from adf_core_python.core.agent.communication.message_manager import MessageManager from adf_core_python.core.agent.develop.develop_data import DevelopData from adf_core_python.core.agent.info.agent_info import AgentInfo -from adf_core_python.core.agent.info.scenario_info import ScenarioInfo, Mode +from adf_core_python.core.agent.info.scenario_info import ( + Mode, + ScenarioInfo, + ScenarioInfoKeys, +) from adf_core_python.core.agent.info.world_info import WorldInfo from adf_core_python.core.agent.module.module_manager import ModuleManager from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData @@ -32,8 +36,8 @@ def __init__( super().__init__( agent_info, world_info, scenario_info, module_manager, develop_data ) - self._kernel_time = None - self._target_entity_id = None + self._kernel_time: int = -1 + self._target_entity_id: Optional[EntityID] = None self._threshold_rest = develop_data.get_value( "adf_core_python.implement.action.DefaultExtendActionRescue.rest", 100 ) @@ -57,7 +61,9 @@ def precompute(self, precompute_data: PrecomputeData) -> ExtendAction: if self.get_count_precompute() >= 2: return self self._path_planning.precompute(precompute_data) - self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + self._kernel_time = self.scenario_info.get_value( + ScenarioInfoKeys.KERNEL_TIMESTEPS, -1 + ) return self def resume(self, precompute_data: PrecomputeData) -> ExtendAction: @@ -65,7 +71,9 @@ def resume(self, precompute_data: PrecomputeData) -> ExtendAction: if self.get_count_resume() >= 2: return self self._path_planning.resume(precompute_data) - self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + self._kernel_time = self.scenario_info.get_value( + ScenarioInfoKeys.KERNEL_TIMESTEPS, -1 + ) return self def prepare(self) -> ExtendAction: @@ -73,7 +81,9 @@ def prepare(self) -> ExtendAction: if self.get_count_prepare() >= 2: return self self._path_planning.prepare() - self._kernel_time = self.scenario_info.get_value("kernel.timesteps", -1) + self._kernel_time = self.scenario_info.get_value( + ScenarioInfoKeys.KERNEL_TIMESTEPS, -1 + ) return self def update_info(self, message_manager: MessageManager) -> ExtendAction: diff --git a/adf_core_python/implement/module/communication/__init__.py b/adf_core_python/implement/module/communication/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/implement/module/communication/default_channel_subscriber.py b/adf_core_python/implement/module/communication/default_channel_subscriber.py new file mode 100644 index 00000000..d195c9a8 --- /dev/null +++ b/adf_core_python/implement/module/communication/default_channel_subscriber.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from rcrs_core.connection.URN import Entity as EntityURN + +from adf_core_python.core.agent.info.scenario_info import ScenarioInfoKeys +from adf_core_python.core.component.communication.channel_subscriber import ( + ChannelSubscriber, +) + +if TYPE_CHECKING: + from adf_core_python.core.agent.info.agent_info import AgentInfo + from adf_core_python.core.agent.info.scenario_info import ScenarioInfo + from adf_core_python.core.agent.info.world_info import WorldInfo + + +class DefaultChannelSubscriber(ChannelSubscriber): + def subscribe( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + ) -> list[int]: + agent = world_info.get_entity(agent_info.get_entity_id()) + if agent is None: + return [] + + agent_type = agent.get_urn() + + number_of_channels: int = ( + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1) + - 1 + ) + + is_platoon: bool = ( + agent_type == EntityURN.FIRE_BRIGADE + or agent_type == EntityURN.POLICE_FORCE + or agent_type == EntityURN.AMBULANCE_TEAM + ) + + max_channel_count: int = ( + scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_PLATOON, 1 + ) + if is_platoon + else scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_OFFICE, 1 + ) + ) + + channels = [ + self.get_channel_number(agent_type, i, number_of_channels) + for i in range(max_channel_count) + ] + return channels + + @staticmethod + def get_channel_number( + agent_type: EntityURN, channel_index: int, number_of_channels: int + ) -> int: + agent_index = 0 + if agent_type == EntityURN.FIRE_BRIGADE or agent_type == EntityURN.FIRE_STATION: + agent_index = 1 + elif ( + agent_type == EntityURN.POLICE_FORCE + or agent_type == EntityURN.POLICE_OFFICE + ): + agent_index = 2 + elif ( + agent_type == EntityURN.AMBULANCE_TEAM + or agent_type == EntityURN.AMBULANCE_CENTRE + ): + agent_index = 3 + + index = (3 * channel_index) + agent_index + if (index % number_of_channels) == 0: + index = number_of_channels + else: + index = index % number_of_channels + return index + + +if __name__ == "__main__": + num_channels = 1 + max_channels = 2 + + for i in range(max_channels): + print( + f"FIREBRIGADE-{i}: {DefaultChannelSubscriber.get_channel_number(EntityURN.FIRE_BRIGADE, i, num_channels)}" + ) + + for i in range(max_channels): + print( + f"POLICE-{i}: {DefaultChannelSubscriber.get_channel_number(EntityURN.POLICE_OFFICE, i, num_channels)}" + ) + + for i in range(max_channels): + print( + f"AMB-{i}: {DefaultChannelSubscriber.get_channel_number(EntityURN.AMBULANCE_CENTRE, i, num_channels)}" + ) diff --git a/adf_core_python/implement/module/communication/default_message_coordinator.py b/adf_core_python/implement/module/communication/default_message_coordinator.py new file mode 100644 index 00000000..92e1555c --- /dev/null +++ b/adf_core_python/implement/module/communication/default_message_coordinator.py @@ -0,0 +1,245 @@ +from typing import Optional + +from rcrs_core.connection.URN import Entity as EntityURN + +from adf_core_python.core.agent.communication.message_manager import MessageManager +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_ambulance import ( + CommandAmbulance, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_fire import ( + CommandFire, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_police import ( + CommandPolice, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.command_scout import ( + CommandScout, +) +from adf_core_python.core.agent.communication.standard.bundle.centralized.message_report import ( + MessageReport, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_ambulance_team import ( + MessageAmbulanceTeam, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_building import ( + MessageBuilding, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_civilian import ( + MessageCivilian, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_fire_brigade import ( + MessageFireBrigade, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_police_force import ( + MessagePoliceForce, +) +from adf_core_python.core.agent.communication.standard.bundle.information.message_road import ( + MessageRoad, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message import ( + StandardMessage, +) +from adf_core_python.core.agent.communication.standard.bundle.standard_message_priority import ( + StandardMessagePriority, +) +from adf_core_python.core.agent.info.agent_info import AgentInfo +from adf_core_python.core.agent.info.scenario_info import ScenarioInfo, ScenarioInfoKeys +from adf_core_python.core.agent.info.world_info import WorldInfo +from adf_core_python.core.component.communication.communication_message import ( + CommunicationMessage, +) +from adf_core_python.core.component.communication.message_coordinator import ( + MessageCoordinator, +) +from adf_core_python.implement.module.communication.default_channel_subscriber import ( + DefaultChannelSubscriber, +) + + +class DefaultMessageCoordinator(MessageCoordinator): + def coordinate( + self, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + message_manager: MessageManager, + send_message_list: list[CommunicationMessage], + channel_send_message_list: list[list[CommunicationMessage]], + ) -> None: + police_messages: list[StandardMessage] = [] + ambulance_messages: list[StandardMessage] = [] + fire_brigade_messages: list[StandardMessage] = [] + voice_messages: list[StandardMessage] = [] + + agent_type = self.get_agent_type(agent_info, world_info) + + for msg in send_message_list: + if isinstance(msg, StandardMessage) and not msg.is_wireless_message(): + voice_messages.append(msg) + else: + if isinstance(msg, MessageBuilding): + fire_brigade_messages.append(msg) + elif isinstance(msg, MessageCivilian): + ambulance_messages.append(msg) + elif isinstance(msg, MessageRoad): + fire_brigade_messages.append(msg) + ambulance_messages.append(msg) + police_messages.append(msg) + elif isinstance(msg, CommandAmbulance): + ambulance_messages.append(msg) + elif isinstance(msg, CommandFire): + fire_brigade_messages.append(msg) + elif isinstance(msg, CommandPolice): + police_messages.append(msg) + elif isinstance(msg, CommandScout): + if agent_type == EntityURN.FIRE_STATION: + fire_brigade_messages.append(msg) + elif agent_type == EntityURN.POLICE_OFFICE: + police_messages.append(msg) + elif agent_type == EntityURN.AMBULANCE_CENTRE: + ambulance_messages.append(msg) + elif isinstance(msg, MessageReport): + if agent_type == EntityURN.FIRE_BRIGADE: + fire_brigade_messages.append(msg) + elif agent_type == EntityURN.POLICE_FORCE: + police_messages.append(msg) + elif agent_type == EntityURN.AMBULANCE_TEAM: + ambulance_messages.append(msg) + elif isinstance(msg, MessageFireBrigade): + fire_brigade_messages.append(msg) + ambulance_messages.append(msg) + police_messages.append(msg) + elif isinstance(msg, MessagePoliceForce): + ambulance_messages.append(msg) + police_messages.append(msg) + elif isinstance(msg, MessageAmbulanceTeam): + ambulance_messages.append(msg) + police_messages.append(msg) + + if int(scenario_info.get_value("comms.channels.count", 1)) > 1: + channel_size = [0] * ( + int(scenario_info.get_value("comms.channels.count", 1)) + ) + self.set_send_messages( + scenario_info, + EntityURN.POLICE_FORCE, + agent_info, + world_info, + police_messages, + channel_send_message_list, + channel_size, + ) + self.set_send_messages( + scenario_info, + EntityURN.AMBULANCE_TEAM, + agent_info, + world_info, + ambulance_messages, + channel_send_message_list, + channel_size, + ) + self.set_send_messages( + scenario_info, + EntityURN.FIRE_BRIGADE, + agent_info, + world_info, + fire_brigade_messages, + channel_send_message_list, + channel_size, + ) + + voice_message_low_list = [] + voice_message_normal_list = [] + voice_message_high_list = [] + + for msg in voice_messages: + if isinstance(msg, StandardMessage): + if msg.get_priority() == StandardMessagePriority.LOW: + voice_message_low_list.append(msg) + elif msg.get_priority() == StandardMessagePriority.NORMAL: + voice_message_normal_list.append(msg) + elif msg.get_priority() == StandardMessagePriority.HIGH: + voice_message_high_list.append(msg) + + channel_send_message_list[0].extend(voice_message_high_list) + channel_send_message_list[0].extend(voice_message_normal_list) + channel_send_message_list[0].extend(voice_message_low_list) + + def get_channels_by_agent_type( + self, + agent_type: EntityURN, + agent_info: AgentInfo, + world_info: WorldInfo, + scenario_info: ScenarioInfo, + ) -> list[int]: + num_channels = ( + scenario_info.get_value(ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1) + - 1 + ) + max_channel_count = int( + # scenario_info.get_comms_channels_max_platoon() + scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_PLATOON, 1 + ) + if self.is_platoon_agent(agent_info, world_info) + else scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_MAX_OFFICE, 1 + ) + ) + channels = [ + DefaultChannelSubscriber.get_channel_number(agent_type, i, num_channels) + for i in range(max_channel_count) + ] + return channels + + def is_platoon_agent(self, agent_info: AgentInfo, world_info: WorldInfo) -> bool: + agent_type = self.get_agent_type(agent_info, world_info) + return agent_type in [ + EntityURN.FIRE_BRIGADE, + EntityURN.POLICE_FORCE, + EntityURN.AMBULANCE_TEAM, + ] + + def get_agent_type( + self, agent_info: AgentInfo, world_info: WorldInfo + ) -> Optional[EntityURN]: + entity = world_info.get_entity(agent_info.get_entity_id()) + if entity is None: + return None + return entity.get_urn() + + def set_send_messages( + self, + scenario_info: ScenarioInfo, + agent_type: EntityURN, + agent_info: AgentInfo, + world_info: WorldInfo, + messages: list[StandardMessage], + channel_send_message_list: list[list[CommunicationMessage]], + channel_size: list[int], + ) -> None: + channels = self.get_channels_by_agent_type( + agent_type, agent_info, world_info, scenario_info + ) + channel_capacities = [ + scenario_info.get_value("comms.channels." + str(channel) + ".bandwidth", 0) + for channel in range( + scenario_info.get_value( + ScenarioInfoKeys.COMMUNICATION_CHANNELS_COUNT, 1 + ) + ) + ] + + sorted_messages = sorted( + messages, key=lambda x: x.get_priority().value, reverse=True + ) + + for message in sorted_messages: + for channel in channels: + if message not in channel_send_message_list[channel] and ( + (channel_size[channel] + message.get_bit_size()) + <= channel_capacities[channel] + ): + channel_size[channel] += message.get_bit_size() + channel_send_message_list[channel].append(message) + break diff --git a/adf_core_python/implement/tactics/__init__.py b/adf_core_python/implement/tactics/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/adf_core_python/implement/tactics/default_tactics_ambulance_team.py b/adf_core_python/implement/tactics/default_tactics_ambulance_team.py index 3154107f..857d724d 100644 --- a/adf_core_python/implement/tactics/default_tactics_ambulance_team.py +++ b/adf_core_python/implement/tactics/default_tactics_ambulance_team.py @@ -119,6 +119,11 @@ def think( agent: AmbulanceTeamEntity = cast(AmbulanceTeamEntity, agent_info.get_myself()) # noqa: F841 entity_id = agent_info.get_entity_id() # noqa: F841 + self._logger.debug( + f"received messages: {[str(message) for message in message_manager.get_received_message_list()]}, help: {message_manager.get_heard_agent_help_message_count()}", + message_manager=message_manager, + ) + target_entity_id = self._human_detector.calculate().get_target_entity_id() self._logger.debug( f"human detector target_entity_id: {target_entity_id}", diff --git a/config/launcher.yaml b/config/launcher.yaml index 3d642721..d69a51b2 100644 --- a/config/launcher.yaml +++ b/config/launcher.yaml @@ -21,11 +21,11 @@ adf: team: platoon: ambulance: - count: 100 + count: 1 fire: - count: 100 + count: 0 police: - count: 100 + count: 0 office: ambulance: count: -1 diff --git a/config/module.yaml b/config/module.yaml index a265bde4..b4fbba11 100644 --- a/config/module.yaml +++ b/config/module.yaml @@ -106,11 +106,10 @@ SampleSearch: # ExtendActionClear: adf_core_python.implement.action.DefaultExtendActionClear # ## MessageManager -# MessageManager: -# PlatoonChannelSubscriber: adf_core_python.implement.module.comm.DefaultChannelSubscriber -# CenterChannelSubscriber: adf_core_python.implement.module.comm.DefaultChannelSubscriber -# PlatoonMessageCoordinator: adf_core_python.implement.module.comm.DefaultMessageCoordinator -# CenterMessageCoordinator: adf_core_python.implement.module.comm.DefaultMessageCoordinator - +MessageManager: + PlatoonChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber + CenterChannelSubscriber: adf_core_python.implement.module.communication.default_channel_subscriber.DefaultChannelSubscriber + PlatoonMessageCoordinator: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator + CenterMessageCoordinator: adf_core_python.implement.module.communication.default_message_coordinator.DefaultMessageCoordinator # ## VisualDebug # VisualDebug: true diff --git a/poetry.lock b/poetry.lock index a96b2720..249bcc8a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,5 +1,151 @@ # This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +[[package]] +name = "bitarray" +version = "3.0.0" +description = "efficient arrays of booleans -- C extension" +optional = false +python-versions = "*" +files = [ + {file = "bitarray-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5ddbf71a97ad1d6252e6e93d2d703b624d0a5b77c153b12f9ea87d83e1250e0c"}, + {file = "bitarray-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0e7f24a0b01e6e6a0191c50b06ca8edfdec1988d9d2b264d669d2487f4f4680"}, + {file = "bitarray-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:150b7b29c36d9f1a24779aea723fdfc73d1c1c161dc0ea14990da27d4e947092"}, + {file = "bitarray-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8330912be6cb8e2fbfe8eb69f82dee139d605730cadf8d50882103af9ac83bb4"}, + {file = "bitarray-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e56ba8be5f17dee0ffa6d6ce85251e062ded2faa3cbd2558659c671e6c3bf96d"}, + {file = "bitarray-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffd94b4803811c738e504a4b499fb2f848b2f7412d71e6b517508217c1d7929d"}, + {file = "bitarray-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0255bd05ec7165e512c115423a5255a3f301417973d20a80fc5bfc3f3640bcb"}, + {file = "bitarray-3.0.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe606e728842389943a939258809dc5db2de831b1d2e0118515059e87f7bbc1a"}, + {file = "bitarray-3.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e89ea59a3ed86a6eb150d016ed28b1bedf892802d0ed32b5659d3199440f3ced"}, + {file = "bitarray-3.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:cf0cc2e91dd38122dec2e6541efa99aafb0a62e118179218181eff720b4b8153"}, + {file = "bitarray-3.0.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d9fe3ee51afeb909b68f97e14c6539ace3f4faa99b21012e610bbe7315c388d"}, + {file = "bitarray-3.0.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:37be5482b9df3105bad00fdf7dc65244e449b130867c3879c9db1db7d72e508b"}, + {file = "bitarray-3.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0027b8f3bb2bba914c79115e96a59b9924aafa1a578223a7c4f0a7242d349842"}, + {file = "bitarray-3.0.0-cp310-cp310-win32.whl", hash = "sha256:628f93e9c2c23930bd1cfe21c634d6c84ec30f45f23e69aefe1fcd262186d7bb"}, + {file = "bitarray-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:0b655c3110e315219e266b2732609fddb0857bc69593de29f3c2ba74b7d3f51a"}, + {file = "bitarray-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:44c3e78b60070389b824d5a654afa1c893df723153c81904088d4922c3cfb6ac"}, + {file = "bitarray-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:545d36332de81e4742a845a80df89530ff193213a50b4cbef937ed5a44c0e5e5"}, + {file = "bitarray-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a9eb510cde3fa78c2e302bece510bf5ed494ec40e6b082dec753d6e22d5d1b1"}, + {file = "bitarray-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e3727ab63dfb6bde00b281934e2212bb7529ea3006c0031a556a84d2268bea5"}, + {file = "bitarray-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2055206ed653bee0b56628f6a4d248d53e5660228d355bbec0014bdfa27050ae"}, + {file = "bitarray-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:147542299f458bdb177f798726e5f7d39ab8491de4182c3c6d9885ed275a3c2b"}, + {file = "bitarray-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3f761184b93092077c7f6b7dad7bd4e671c1620404a76620da7872ceb576a94"}, + {file = "bitarray-3.0.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e008b7b4ce6c7f7a54b250c45c28d4243cc2a3bbfd5298fa7dac92afda229842"}, + {file = "bitarray-3.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dfea514e665af278b2e1d4deb542de1cd4f77413bee83dd15ae16175976ea8d5"}, + {file = "bitarray-3.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:66d6134b7bb737b88f1d16478ad0927c571387f6054f4afa5557825a4c1b78e2"}, + {file = "bitarray-3.0.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3cd565253889940b4ec4768d24f101d9fe111cad4606fdb203ea16f9797cf9ed"}, + {file = "bitarray-3.0.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4800c91a14656789d2e67d9513359e23e8a534c8ee1482bb9b517a4cfc845200"}, + {file = "bitarray-3.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c2945e0390d1329c585c584c6b6d78be017d9c6a1288f9c92006fe907f69cc28"}, + {file = "bitarray-3.0.0-cp311-cp311-win32.whl", hash = "sha256:c23286abba0cb509733c6ce8f4013cd951672c332b2e184dbefbd7331cd234c8"}, + {file = "bitarray-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:ca79f02a98cbda1472449d440592a2fe2ad96fe55515a0447fa8864a38017cf8"}, + {file = "bitarray-3.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:184972c96e1c7e691be60c3792ca1a51dd22b7f25d96ebea502fe3c9b554f25d"}, + {file = "bitarray-3.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:787db8da5e9e29be712f7a6bce153c7bc8697ccc2c38633e347bb9c82475d5c9"}, + {file = "bitarray-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2da91ab3633c66999c2a352f0ca9ae064f553e5fc0eca231d28e7e305b83e942"}, + {file = "bitarray-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7edb83089acbf2c86c8002b96599071931dc4ea5e1513e08306f6f7df879a48b"}, + {file = "bitarray-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:996d1b83eb904589f40974538223eaed1ab0f62be8a5105c280b9bd849e685c4"}, + {file = "bitarray-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4817d73d995bd2b977d9cde6050be8d407791cf1f84c8047fa0bea88c1b815bc"}, + {file = "bitarray-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d47bc4ff9b0e1624d613563c6fa7b80aebe7863c56c3df5ab238bb7134e8755"}, + {file = "bitarray-3.0.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aca0a9cd376beaccd9f504961de83e776dd209c2de5a4c78dc87a78edf61839b"}, + {file = "bitarray-3.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:572a61fba7e3a710a8324771322fba8488d134034d349dcd036a7aef74723a80"}, + {file = "bitarray-3.0.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a817ad70c1aff217530576b4f037dd9b539eb2926603354fcac605d824082ad1"}, + {file = "bitarray-3.0.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:2ac67b658fa5426503e9581a3fb44a26a3b346c1abd17105735f07db572195b3"}, + {file = "bitarray-3.0.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:12f19ede03e685c5c588ab5ed63167999295ffab5e1126c5fe97d12c0718c18f"}, + {file = "bitarray-3.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fcef31b062f756ba7eebcd7890c5d5de84b9d64ee877325257bcc9782288564a"}, + {file = "bitarray-3.0.0-cp312-cp312-win32.whl", hash = "sha256:656db7bdf1d81ec3b57b3cad7ec7276765964bcfd0eb81c5d1331f385298169c"}, + {file = "bitarray-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:f785af6b7cb07a9b1e5db0dea9ef9e3e8bb3d74874a0a61303eab9c16acc1999"}, + {file = "bitarray-3.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7cb885c043000924554fe2124d13084c8fdae03aec52c4086915cd4cb87fe8be"}, + {file = "bitarray-3.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7814c9924a0b30ecd401f02f082d8697fc5a5be3f8d407efa6e34531ff3c306a"}, + {file = "bitarray-3.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bcf524a087b143ba736aebbb054bb399d49e77cf7c04ed24c728e411adc82bfa"}, + {file = "bitarray-3.0.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1d5abf1d6d910599ac16afdd9a0ed3e24f3b46af57f3070cf2792f236f36e0b"}, + {file = "bitarray-3.0.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9929051feeaf8d948cc0b1c9ce57748079a941a1a15c89f6014edf18adaade84"}, + {file = "bitarray-3.0.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96cf0898f8060b2d3ae491762ae871b071212ded97ff9e1e3a5229e9fefe544c"}, + {file = "bitarray-3.0.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab37da66a8736ad5a75a58034180e92c41e864da0152b84e71fcc253a2f69cd4"}, + {file = "bitarray-3.0.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeb79e476d19b91fd6a3439853e4e5ba1b3b475920fa40d62bde719c8af786f"}, + {file = "bitarray-3.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f75fc0198c955d840b836059bd43e0993edbf119923029ca60c4fc017cefa54a"}, + {file = "bitarray-3.0.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f12cc7c7638074918cdcc7491aff897df921b092ffd877227892d2686e98f876"}, + {file = "bitarray-3.0.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dbe1084935b942fab206e609fa1ed3f46ad1f2612fb4833e177e9b2a5e006c96"}, + {file = "bitarray-3.0.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ac06dd72ee1e1b6e312504d06f75220b5894af1fb58f0c20643698f5122aea76"}, + {file = "bitarray-3.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:00f9a88c56e373009ac3c73c55205cfbd9683fbd247e2f9a64bae3da78795252"}, + {file = "bitarray-3.0.0-cp313-cp313-win32.whl", hash = "sha256:9c6e52005e91803eb4e08c0a08a481fb55ddce97f926bae1f6fa61b3396b5b61"}, + {file = "bitarray-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:cb98d5b6eac4b2cf2a5a69f60a9c499844b8bea207059e9fc45c752436e6bb49"}, + {file = "bitarray-3.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:eb27c01b747649afd7e1c342961680893df6d8d81f832a6f04d8c8e03a8a54cc"}, + {file = "bitarray-3.0.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4683bff52f5a0fd523fb5d3138161ef87611e63968e1fcb6cf4b0c6a86970fe0"}, + {file = "bitarray-3.0.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cb7302dbcfcb676f0b66f15891f091d0233c4fc23e1d4b9dc9b9e958156e347f"}, + {file = "bitarray-3.0.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:153d7c416a70951dcfa73487af05d2f49c632e95602f1620cd9a651fa2033695"}, + {file = "bitarray-3.0.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251cd5bd47f542893b2b61860eded54f34920ea47fd5bff038d85e7a2f7ae99b"}, + {file = "bitarray-3.0.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fa4b4d9fa90124b33b251ef74e44e737021f253dc7a9174e1b39f097451f7ca"}, + {file = "bitarray-3.0.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:18abdce7ab5d2104437c39670821cba0b32fdb9b2da9e6d17a4ff295362bd9dc"}, + {file = "bitarray-3.0.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:2855cc01ee370f7e6e3ec97eebe44b1453c83fb35080313145e2c8c3c5243afb"}, + {file = "bitarray-3.0.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:0cecaf2981c9cd2054547f651537b4f4939f9fe225d3fc2b77324b597c124e40"}, + {file = "bitarray-3.0.0-cp36-cp36m-musllinux_1_2_s390x.whl", hash = "sha256:22b00f65193fafb13aa644e16012c8b49e7d5cbb6bb72825105ff89aadaa01e3"}, + {file = "bitarray-3.0.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:20f30373f0af9cb583e4122348cefde93c82865dbcbccc4997108b3d575ece84"}, + {file = "bitarray-3.0.0-cp36-cp36m-win32.whl", hash = "sha256:aef404d5400d95c6ec86664df9924bde667c8865f8e33c9b7bd79823d53b3e5d"}, + {file = "bitarray-3.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:ec5b0f2d13da53e0975ac15ecbe8badb463bdb0bebaa09457f4df3320421915c"}, + {file = "bitarray-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:041c889e69c847b8a96346650e50f728b747ae176889199c49a3f31ae1de0e23"}, + {file = "bitarray-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc83ea003dd75e9ade3291ef0585577dd5524aec0c8c99305c0aaa2a7570d6db"}, + {file = "bitarray-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c33129b49196aa7965ac0f16fcde7b6ad8614b606caf01669a0277cef1afe1d"}, + {file = "bitarray-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ef5c787c8263c082a73219a69eb60a500e157a4ac69d1b8515ad836b0e71fb4"}, + {file = "bitarray-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e15c94d79810c5ab90ddf4d943f71f14332890417be896ca253f21fa3d78d2b1"}, + {file = "bitarray-3.0.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cd021ada988e73d649289cee00428b75564c46d55fbdcb0e3402e504b0ae5ea"}, + {file = "bitarray-3.0.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7f1c24be7519f16a47b7e2ad1a1ef73023d34d8cbe1a3a59b185fc14baabb132"}, + {file = "bitarray-3.0.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:000df24c183011b5d27c23d79970f49b6762e5bb5aacd25da9c3e9695c693222"}, + {file = "bitarray-3.0.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:42bf1b222c698b467097f58b9f59dc850dfa694dde4e08237407a6a103757aa3"}, + {file = "bitarray-3.0.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:648e7ce794928e8d11343b5da8ecc5b910af75a82ea1a4264d5d0a55c3785faa"}, + {file = "bitarray-3.0.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f536fc4d1a683025f9caef0bebeafd60384054579ffe0825bb9bd8c59f8c55b8"}, + {file = "bitarray-3.0.0-cp37-cp37m-win32.whl", hash = "sha256:a754c1464e7b946b1cac7300c582c6fba7d66e535cd1dab76d998ad285ac5a37"}, + {file = "bitarray-3.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e91d46d12781a14ccb8b284566b14933de4e3b29f8bc5e1c17de7a2001ad3b5b"}, + {file = "bitarray-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:904c1d5e3bd24f0c0d37a582d2461312033c91436a6a4f3bdeeceb4bea4a899d"}, + {file = "bitarray-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:47ccf9887bd595d4a0536f2310f0dcf89e17ab83b8befa7dc8727b8017120fda"}, + {file = "bitarray-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:71ad0139c95c9acf4fb62e203b428f9906157b15eecf3f30dc10b55919225896"}, + {file = "bitarray-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e002ac1073ac70e323a7a4bfa9ab95e7e1a85c79160799e265563f342b1557"}, + {file = "bitarray-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acc07211a59e2f245e9a06f28fa374d094fb0e71cf5366eef52abbb826ddc81e"}, + {file = "bitarray-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98a4070ddafabddaee70b2aa7cc6286cf73c37984169ab03af1782da2351059a"}, + {file = "bitarray-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7d09ef06ba57bea646144c29764bf6b870fb3c5558ca098191e07b6a1d40bf7"}, + {file = "bitarray-3.0.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce249ed981f428a8b61538ca82d3875847733d579dd40084ab8246549160f8a4"}, + {file = "bitarray-3.0.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea40e98d751ed4b255db4a88fe8fb743374183f78470b9e9305aab186bf28ede"}, + {file = "bitarray-3.0.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:928b8b6dfcd015e1a81334cfdac02815da2a2407854492a80cf8a3a922b04052"}, + {file = "bitarray-3.0.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:fbb645477595ce2a0fbb678d1cfd08d3b896e5d56196d40fb9e114eeab9382b3"}, + {file = "bitarray-3.0.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:dc1937a0ff2671797d35243db4b596329842480d125a65e9fe964bcffaf16dfc"}, + {file = "bitarray-3.0.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a4f49ac31734fe654a68e2515c0da7f5bbdf2d52755ba09a42ac406f1f08c9d0"}, + {file = "bitarray-3.0.0-cp38-cp38-win32.whl", hash = "sha256:6d2a2ce73f9897268f58857ad6893a1a6680c5a6b28f79d21c7d33285a5ae646"}, + {file = "bitarray-3.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:b1047999f1797c3ea7b7c85261649249c243308dcf3632840d076d18fa72f142"}, + {file = "bitarray-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:39b38a3d45dac39d528c87b700b81dfd5e8dc8e9e1a102503336310ef837c3fd"}, + {file = "bitarray-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0e104f9399144fab6a892d379ba1bb4275e56272eb465059beef52a77b4e5ce6"}, + {file = "bitarray-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0879f839ec8f079fa60c3255966c2e1aa7196699a234d4e5b7898fbc321901b5"}, + {file = "bitarray-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9502c2230d59a4ace2fddfd770dad8e8b414cbd99517e7e56c55c20997c28b8d"}, + {file = "bitarray-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:57d5ef854f8ec434f2ffd9ddcefc25a10848393fe2976e2be2c8c773cf5fef42"}, + {file = "bitarray-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a3c36b2fcfebe15ad1c10a90c1d52a42bebe960adcbce340fef867203028fbe7"}, + {file = "bitarray-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66a33a537e781eac3a352397ce6b07eedf3a8380ef4a804f8844f3f45e335544"}, + {file = "bitarray-3.0.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa54c7e1da8cf4be0aab941ea284ec64033ede5d6de3fd47d75e77cafe986e9d"}, + {file = "bitarray-3.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a667ea05ba1ea81b722682276dbef1d36990f8908cf51e570099fd505a89f931"}, + {file = "bitarray-3.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d756bfeb62ca4fe65d2af7a39249d442c05070c047d03729ad6cd4c2e9b0f0bd"}, + {file = "bitarray-3.0.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c9e9fef0754867d88e948ce8351c9fd7e507d8514e0f242fd67c907b9cdf98b3"}, + {file = "bitarray-3.0.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:67a0b56dd02f2713f6f52cacb3f251afd67c94c5f0748026d307d87a81a8e15c"}, + {file = "bitarray-3.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d8c36ddc1923bcc4c11b9994c54eaae25034812a42400b7b8a86fe6d242166a2"}, + {file = "bitarray-3.0.0-cp39-cp39-win32.whl", hash = "sha256:1414a7102a3c4986f241480544f5c99f5d32258fb9b85c9c04e84e48c490ab35"}, + {file = "bitarray-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:8c9733d2ff9b7838ac04bf1048baea153174753e6a47312be14c83c6a395424b"}, + {file = "bitarray-3.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fef4e3b3f2084b4dae3e5316b44cda72587dcc81f68b4eb2dbda1b8d15261b61"}, + {file = "bitarray-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e9eee03f187cef1e54a4545124109ee0afc84398628b4b32ebb4852b4a66393"}, + {file = "bitarray-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cb5702dd667f4bb10fed056ffdc4ddaae8193a52cd74cb2cdb54e71f4ef2dd1"}, + {file = "bitarray-3.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:666e44b0458bb2894b64264a29f2cc7b5b2cbcc4c5e9cedfe1fdbde37a8e329a"}, + {file = "bitarray-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c756a92cf1c1abf01e56a4cc40cb89f0ff9147f2a0be5b557ec436a23ff464d8"}, + {file = "bitarray-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7e51e7f8289bf6bb631e1ef2a8f5e9ca287985ff518fe666abbdfdb6a848cb26"}, + {file = "bitarray-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fa5d8e4b28388b337face6ce4029be73585651a44866901513df44be9a491ab"}, + {file = "bitarray-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3963b80a68aedcd722a9978d261ae53cb9bb6a8129cc29790f0f10ce5aca287a"}, + {file = "bitarray-3.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b555006a7dea53f6bebc616a4d0249cecbf8f1fadf77860120a2e5dbdc2f167"}, + {file = "bitarray-3.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4ac2027ca650a7302864ed2528220d6cc6921501b383e9917afc7a2424a1e36d"}, + {file = "bitarray-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bf90aba4cff9e72e24ecdefe33bad608f147a23fa5c97790a5bab0e72fe62b6d"}, + {file = "bitarray-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a199e6d7c3bad5ba9d0e4dc00dde70ee7d111c9dfc521247fa646ef59fa57e"}, + {file = "bitarray-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43b6c7c4f4a7b80e86e24a76f4c6b9b67d03229ea16d7d403520616535c32196"}, + {file = "bitarray-3.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34fc13da3518f14825b239374734fce93c1a9299ed7b558c3ec1d659ec7e4c70"}, + {file = "bitarray-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:369b6d457af94af901d632c7e625ca6caf0a7484110fc91c6290ce26bc4f1478"}, + {file = "bitarray-3.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ee040ad3b7dfa05e459713099f16373c1f2a6f68b43cb0575a66718e7a5daef4"}, + {file = "bitarray-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dad7ba2af80f9ec1dd988c3aca7992408ec0d0b4c215b65d353d95ab0070b10"}, + {file = "bitarray-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4839d3b64af51e4b8bb4a602563b98b9faeb34fd6c00ed23d7834e40a9d080fc"}, + {file = "bitarray-3.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f71f24b58e75a889b9915e3197865302467f13e7390efdea5b6afc7424b3a2ea"}, + {file = "bitarray-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:bcf0150ae0bcc4aa97bdfcb231b37bad1a59083c1b5012643b266012bf420e68"}, + {file = "bitarray-3.0.0.tar.gz", hash = "sha256:a2083dc20f0d828a7cdf7a16b20dae56aab0f43dc4f347a3b3039f6577992b03"}, +] + [[package]] name = "colorama" version = "0.4.6" @@ -109,75 +255,77 @@ files = [ [[package]] name = "numpy" -version = "2.1.2" +version = "2.1.3" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "numpy-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:30d53720b726ec36a7f88dc873f0eec8447fbc93d93a8f079dfac2629598d6ee"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8d3ca0a72dd8846eb6f7dfe8f19088060fcb76931ed592d29128e0219652884"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:fc44e3c68ff00fd991b59092a54350e6e4911152682b4782f68070985aa9e648"}, - {file = "numpy-2.1.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7c1c60328bd964b53f8b835df69ae8198659e2b9302ff9ebb7de4e5a5994db3d"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6cdb606a7478f9ad91c6283e238544451e3a95f30fb5467fbf715964341a8a86"}, - {file = "numpy-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d666cb72687559689e9906197e3bec7b736764df6a2e58ee265e360663e9baf7"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6eef7a2dbd0abfb0d9eaf78b73017dbfd0b54051102ff4e6a7b2980d5ac1a03"}, - {file = "numpy-2.1.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:12edb90831ff481f7ef5f6bc6431a9d74dc0e5ff401559a71e5e4611d4f2d466"}, - {file = "numpy-2.1.2-cp310-cp310-win32.whl", hash = "sha256:a65acfdb9c6ebb8368490dbafe83c03c7e277b37e6857f0caeadbbc56e12f4fb"}, - {file = "numpy-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:860ec6e63e2c5c2ee5e9121808145c7bf86c96cca9ad396c0bd3e0f2798ccbe2"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b42a1a511c81cc78cbc4539675713bbcf9d9c3913386243ceff0e9429ca892fe"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:faa88bc527d0f097abdc2c663cddf37c05a1c2f113716601555249805cf573f1"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c82af4b2ddd2ee72d1fc0c6695048d457e00b3582ccde72d8a1c991b808bb20f"}, - {file = "numpy-2.1.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:13602b3174432a35b16c4cfb5de9a12d229727c3dd47a6ce35111f2ebdf66ff4"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ebec5fd716c5a5b3d8dfcc439be82a8407b7b24b230d0ad28a81b61c2f4659a"}, - {file = "numpy-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2b49c3c0804e8ecb05d59af8386ec2f74877f7ca8fd9c1e00be2672e4d399b1"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cbba4b30bf31ddbe97f1c7205ef976909a93a66bb1583e983adbd155ba72ac2"}, - {file = "numpy-2.1.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8e00ea6fc82e8a804433d3e9cedaa1051a1422cb6e443011590c14d2dea59146"}, - {file = "numpy-2.1.2-cp311-cp311-win32.whl", hash = "sha256:5006b13a06e0b38d561fab5ccc37581f23c9511879be7693bd33c7cd15ca227c"}, - {file = "numpy-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:f1eb068ead09f4994dec71c24b2844f1e4e4e013b9629f812f292f04bd1510d9"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d7bf0a4f9f15b32b5ba53147369e94296f5fffb783db5aacc1be15b4bf72f43b"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b1d0fcae4f0949f215d4632be684a539859b295e2d0cb14f78ec231915d644db"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:f751ed0a2f250541e19dfca9f1eafa31a392c71c832b6bb9e113b10d050cb0f1"}, - {file = "numpy-2.1.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:bd33f82e95ba7ad632bc57837ee99dba3d7e006536200c4e9124089e1bf42426"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b8cde4f11f0a975d1fd59373b32e2f5a562ade7cde4f85b7137f3de8fbb29a0"}, - {file = "numpy-2.1.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d95f286b8244b3649b477ac066c6906fbb2905f8ac19b170e2175d3d799f4df"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ab4754d432e3ac42d33a269c8567413bdb541689b02d93788af4131018cbf366"}, - {file = "numpy-2.1.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e585c8ae871fd38ac50598f4763d73ec5497b0de9a0ab4ef5b69f01c6a046142"}, - {file = "numpy-2.1.2-cp312-cp312-win32.whl", hash = "sha256:9c6c754df29ce6a89ed23afb25550d1c2d5fdb9901d9c67a16e0b16eaf7e2550"}, - {file = "numpy-2.1.2-cp312-cp312-win_amd64.whl", hash = "sha256:456e3b11cb79ac9946c822a56346ec80275eaf2950314b249b512896c0d2505e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a84498e0d0a1174f2b3ed769b67b656aa5460c92c9554039e11f20a05650f00d"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4d6ec0d4222e8ffdab1744da2560f07856421b367928026fb540e1945f2eeeaf"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:259ec80d54999cc34cd1eb8ded513cb053c3bf4829152a2e00de2371bd406f5e"}, - {file = "numpy-2.1.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:675c741d4739af2dc20cd6c6a5c4b7355c728167845e3c6b0e824e4e5d36a6c3"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05b2d4e667895cc55e3ff2b56077e4c8a5604361fc21a042845ea3ad67465aa8"}, - {file = "numpy-2.1.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43cca367bf94a14aca50b89e9bc2061683116cfe864e56740e083392f533ce7a"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:76322dcdb16fccf2ac56f99048af32259dcc488d9b7e25b51e5eca5147a3fb98"}, - {file = "numpy-2.1.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:32e16a03138cabe0cb28e1007ee82264296ac0983714094380b408097a418cfe"}, - {file = "numpy-2.1.2-cp313-cp313-win32.whl", hash = "sha256:242b39d00e4944431a3cd2db2f5377e15b5785920421993770cddb89992c3f3a"}, - {file = "numpy-2.1.2-cp313-cp313-win_amd64.whl", hash = "sha256:f2ded8d9b6f68cc26f8425eda5d3877b47343e68ca23d0d0846f4d312ecaa445"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ffef621c14ebb0188a8633348504a35c13680d6da93ab5cb86f4e54b7e922b5"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ad369ed238b1959dfbade9018a740fb9392c5ac4f9b5173f420bd4f37ba1f7a0"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d82075752f40c0ddf57e6e02673a17f6cb0f8eb3f587f63ca1eaab5594da5b17"}, - {file = "numpy-2.1.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1600068c262af1ca9580a527d43dc9d959b0b1d8e56f8a05d830eea39b7c8af6"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a26ae94658d3ba3781d5e103ac07a876b3e9b29db53f68ed7df432fd033358a8"}, - {file = "numpy-2.1.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13311c2db4c5f7609b462bc0f43d3c465424d25c626d95040f073e30f7570e35"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:2abbf905a0b568706391ec6fa15161fad0fb5d8b68d73c461b3c1bab6064dd62"}, - {file = "numpy-2.1.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:ef444c57d664d35cac4e18c298c47d7b504c66b17c2ea91312e979fcfbdfb08a"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bdd407c40483463898b84490770199d5714dcc9dd9b792f6c6caccc523c00952"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:da65fb46d4cbb75cb417cddf6ba5e7582eb7bb0b47db4b99c9fe5787ce5d91f5"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c193d0b0238638e6fc5f10f1b074a6993cb13b0b431f64079a509d63d3aa8b7"}, - {file = "numpy-2.1.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a7d80b2e904faa63068ead63107189164ca443b42dd1930299e0d1cb041cec2e"}, - {file = "numpy-2.1.2.tar.gz", hash = "sha256:13532a088217fa624c99b843eeb54640de23b3414b14aa66d023805eb731066c"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1"}, + {file = "numpy-2.1.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3"}, + {file = "numpy-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c"}, + {file = "numpy-2.1.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4"}, + {file = "numpy-2.1.3-cp310-cp310-win32.whl", hash = "sha256:72dcc4a35a8515d83e76b58fdf8113a5c969ccd505c8a946759b24e3182d1f23"}, + {file = "numpy-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9"}, + {file = "numpy-2.1.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a"}, + {file = "numpy-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee"}, + {file = "numpy-2.1.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0"}, + {file = "numpy-2.1.3-cp311-cp311-win32.whl", hash = "sha256:d9beb777a78c331580705326d2367488d5bc473b49a9bc3036c154832520aca9"}, + {file = "numpy-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8"}, + {file = "numpy-2.1.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512"}, + {file = "numpy-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc"}, + {file = "numpy-2.1.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0"}, + {file = "numpy-2.1.3-cp312-cp312-win32.whl", hash = "sha256:e6988e90fcf617da2b5c78902fe8e668361b43b4fe26dbf2d7b0f8034d4cafb9"}, + {file = "numpy-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57"}, + {file = "numpy-2.1.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43"}, + {file = "numpy-2.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ea4dedd6e394a9c180b33c2c872b92f7ce0f8e7ad93e9585312b0c5a04777a4a"}, + {file = "numpy-2.1.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0df3635b9c8ef48bd3be5f862cf71b0a4716fa0e702155c45067c6b711ddcef"}, + {file = "numpy-2.1.3-cp313-cp313-win32.whl", hash = "sha256:50ca6aba6e163363f132b5c101ba078b8cbd3fa92c7865fd7d4d62d9779ac29f"}, + {file = "numpy-2.1.3-cp313-cp313-win_amd64.whl", hash = "sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:996bb9399059c5b82f76b53ff8bb686069c05acc94656bb259b1d63d04a9506f"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:45966d859916ad02b779706bb43b954281db43e185015df6eb3323120188f9e4"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:baed7e8d7481bfe0874b566850cb0b85243e982388b7b23348c6db2ee2b2ae8e"}, + {file = "numpy-2.1.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f7f672a3388133335589cfca93ed468509cb7b93ba3105fce780d04a6576a0"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7aac50327da5d208db2eec22eb11e491e3fe13d22653dce51b0f4109101b408"}, + {file = "numpy-2.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4394bc0dbd074b7f9b52024832d16e019decebf86caf909d94f6b3f77a8ee3b6"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:50d18c4358a0a8a53f12a8ba9d772ab2d460321e6a93d6064fc22443d189853f"}, + {file = "numpy-2.1.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:14e253bd43fc6b37af4921b10f6add6925878a42a0c5fe83daee390bca80bc17"}, + {file = "numpy-2.1.3-cp313-cp313t-win32.whl", hash = "sha256:08788d27a5fd867a663f6fc753fd7c3ad7e92747efc73c53bca2f19f8bc06f48"}, + {file = "numpy-2.1.3-cp313-cp313t-win_amd64.whl", hash = "sha256:2564fbdf2b99b3f815f2107c1bbc93e2de8ee655a69c261363a1172a79a257d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d"}, + {file = "numpy-2.1.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb"}, + {file = "numpy-2.1.3.tar.gz", hash = "sha256:aa08e04e08aaf974d4458def539dece0d28146d866a39da5639596f4921fd761"}, ] [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -342,7 +490,7 @@ rtree = "*" type = "git" url = "https://github.com/adf-python/rcrs-core-python" reference = "HEAD" -resolved_reference = "b353cd6e82005e20e099554d4f357d9c0cc2e052" +resolved_reference = "c4ec7b2d32badb2f10fc1d03b962b465624a1ed5" [[package]] name = "rtree" @@ -561,13 +709,13 @@ typing = ["mypy (>=1.4)", "rich", "twisted"] [[package]] name = "taskipy" -version = "1.13.0" +version = "1.14.0" description = "tasks runner for python projects" optional = false python-versions = "<4.0,>=3.6" files = [ - {file = "taskipy-1.13.0-py3-none-any.whl", hash = "sha256:56f42b7e508d9aed2c7b6365f8d3dab62dbd0c768c1ab606c819da4fc38421f7"}, - {file = "taskipy-1.13.0.tar.gz", hash = "sha256:2b52f0257958fed151f1340f7de93fcf0848f7a358ad62ba05c31c2ca04f89fe"}, + {file = "taskipy-1.14.0-py3-none-any.whl", hash = "sha256:29040d9a8038170602feb71792bdef5203720ed30f595304aee843625892452b"}, + {file = "taskipy-1.14.0.tar.gz", hash = "sha256:5d9631c29980481d59858f0a100ed3200cf7468ca8c0540ef19388586485532d"}, ] [package.dependencies] @@ -634,4 +782,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "f5041edb49c80e926991fbd658e04175d3c35e3e95fb90bf8762d244de7498f3" +content-hash = "6e8244db0f75c542b50338580672c5e11b1cdfe38b0a62d01b757b31d4eddaf0" diff --git a/pyproject.toml b/pyproject.toml index b768e40b..35dee4b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ pytest = "^8.3.2" types-pyyaml = "^6.0.12.20240808" scikit-learn = "^1.5.2" structlog = "^24.4.0" +bitarray = "^3.0.0" shapely = "^2.0.6" diff --git a/pyrightconfig.json b/pyrightconfig.json index 8a1eb709..8c19fca2 100644 --- a/pyrightconfig.json +++ b/pyrightconfig.json @@ -1,4 +1,4 @@ { "typeCheckingMode": "standard", - "exclude": ["**/site-packages"] + "exclude": [".venv", ".git"] }