async def get_content(self, timeout: float = 0.5) -> \ Tuple[Status, Optional[Content]]: request = api.Message() request.resource_container.content_req = True if not self.send(message=request): return ResourceContainerI.Status.FAILED_TO_SEND_REQUEST, None return await self.wait_content(timeout)
async def time(self, timeout: float = 0.1) -> Optional[int]: """Return current server time""" request = api.Message() request.system_clock.time_req = True if not self.send(message=request): return None return await self.wait_timestamp()
async def get_state(self) -> Optional[State]: """Return current ship's state""" request = api.Message() request.ship.state_req = True if not self.send(message=request): return None return await self.wait_state()
async def open_tunnel(self, port: int) -> Tuple[Status, Optional[int]]: """Open tunnel to the specified 'port'. Return (sessionId, None) on success, otherwise return (None, error)""" request = api.Message() request.commutator.open_tunnel = port self.send(request) # it shouldn't take much time response, _ = await self.wait_message(timeout=0.1) if not response: return CommutatorI.Status.RESPONSE_TIMEOUT, None # Note: once 'open_tunnel_report' is passed through 'sessions_mux', # it creates a new related session object, that can be retreived by # 'get_channel_for_session()' tunnel_id = get_message_field(response, ["commutator", "open_tunnel_report"]) if tunnel_id is None: error = get_message_field(response, ["commutator", "open_tunnel_failed"]) if not error: return CommutatorI.Status.UNEXPECTED_RESPONSE, None self._logger.warning( f"Failed to open tunnel to port #{port}: {error}") return CommutatorI.Status.convert(error), None return CommutatorI.Status.SUCCESS, tunnel_id
async def monitor(self, duration_ms: int) -> Optional[State]: """Start ship's state monitoring. Return current state. After this call you may use 'wait_state()' to receive updates """ request = api.Message() request.ship.monitor = duration_ms if not self.send(message=request): return None return await self.wait_state()
async def start_build(self, blueprint: str, ship_name: str) -> Status: request = api.Message() req_body = request.shipyard.start_build req_body.blueprint_name = blueprint req_body.ship_name = ship_name if not self.send(message=request): return ShipyardI.Status.FAILED_TO_SEND_REQUEST status, _ = await self.wait_building_report() return status
async def get_total_slots(self) -> (bool, int): """Return total number of devices, attached to this commutator""" request = api.Message() request.commutator.total_slots_req = True self.send(request) response, _ = await self.wait_message() if not response: return False, 0 total_slots = get_message_field(response, ["commutator", "total_slots"]) return total_slots is not None, total_slots
async def start_monitoring(self, timeout: float = 0.5) -> Status: request = api.Message() request.passive_scanner.monitor = True if not self.send(message=request): return PassiveScannerI.Status.FAILED_TO_SEND_REQUEST response, _ = await self.wait_message(timeout=timeout) if not response: return PassiveScannerI.Status.RESPONSE_TIMEOUT ack = api.get_message_field(response, ["passive_scanner", "monitor_ack"]) return PassiveScannerI.Status.SUCCESS \ if ack else PassiveScannerI.Status.MONITORING_FAILED
async def wait_for(self, period_us: int, timeout: float) -> Optional[int]: """Wait for the specified 'period' microseconds Return actual server's time""" request = api.Message() request.system_clock.wait_for = period_us if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None return api.get_message_field(response, ["system_clock", "ring"])
async def monitor(self, timeout: float = 0.5 ) -> Tuple[Status, Optional[Content]]: """Start monitoring session. Note: monitoring session can't be closed. The only way to stop monitoring session is close the channel. """ request = api.Message() request.resource_container.monitor = True if not self.send(message=request): return ResourceContainerI.Status.FAILED_TO_SEND_REQUEST, None return await self.wait_content(timeout)
async def wait_until(self, time: int, timeout: float) -> Optional[int]: """Wait until server time reaches the specified 'time' Return actual server's time""" request = api.Message() request.system_clock.wait_until = time if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None return api.get_message_field(response, ["system_clock", "ring"])
async def get_thrust(self, timeout: float = 0.5) -> Optional[Vector]: """Return current engine thrust""" request = api.Message() request.engine.thrust_req = True if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None thrust = api.get_message_field(response, ["engine", "thrust"]) if not thrust: return None return Vector(x=thrust.x, y=thrust.y).set_length(thrust.thrust)
async def get_specification(self, timeout: float = 0.5)\ -> Optional[Specification]: request = api.Message() request.passive_scanner.specification_req = True if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None spec = api.get_message_field(response, ["passive_scanner", "specification"]) if not spec: return None return Specification(scanning_radius_km=spec.scanning_radius_km, max_update_time_ms=spec.max_update_time_ms)
async def monitoring(self, interval_ms: int, timeout: float = 0.5) -> Optional[int]: """Start monitoring in this session and return current time. Server will send current timestamp in this session every 'interval_ms' milliseconds. You may call 'wait_timestamp()' with timeout=interval_ms*5 in order to receive timestamps. Note: the only way to stop monitoring is to close the session. """ request = api.Message() request.system_clock.monitor = interval_ms if not self.send(message=request): return None return await self.wait_timestamp()
async def get_specification(self, timeout: float = 0.5) \ -> (Status, Optional[Specification]): request = api.Message() request.shipyard.specification_req = True if not self.send(message=request): return ShipyardI.Status.FAILED_TO_SEND_REQUEST, None response, _ = await self.wait_message(timeout=timeout) if not response: return ShipyardI.Status.RESPONSE_TIMEOUT, None spec = api.get_message_field(response, ["shipyard", "specification"]) if not spec: return ShipyardI.Status.UNEXPECTED_RESPONSE, None spec = Specification(labor_per_sec=spec.labor_per_sec) return ShipyardI.Status.SUCCESS, spec
async def close_port(self, timeout: int = 0.5) -> Status: """Close an existing opened port""" request = api.Message() request.resource_container.close_port = True if not self.send(message=request): return ResourceContainerI.Status.FAILED_TO_SEND_REQUEST response, _ = await self.wait_message(timeout=timeout) if not response: return ResourceContainerI.Status.RESPONSE_TIMEOUT status = api.get_message_field( response, ["resource_container", "close_port_status"]) if status is not None: # Success case return ResourceContainerI.Status.convert(status)
async def get_position(self, timeout: float = 0.5) -> Optional[Position]: """Request current ship's position. Will block until the response is received or the specified 'timeout' occurs.""" request = api.Message() request.navigation.position_req = True if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None position = api.get_message_field(response, ["navigation", "position"]) if not position: return None assert response.timestamp is not None return Position.from_protobuf(position, timestamp=response.timestamp)
async def close_tunnel(self, tunnel_id: int) -> Status: request = api.Message() request.commutator.close_tunnel = tunnel_id self.send(request) # it shouldn't take much time response, _ = await self.wait_message(timeout=0.2) if not response: return CommutatorI.Status.RESPONSE_TIMEOUT status = get_message_field(response, ["commutator", "close_tunnel_status"]) if status is None: return CommutatorI.Status.UNEXPECTED_RESPONSE return CommutatorI.Status.convert(status)
async def get_specification(self, timeout: float = 0.5)\ -> Optional[Specification]: request = api.Message() request.celestial_scanner.specification_req = True if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None spec = api.get_message_field(response, ["celestial_scanner", "specification"]) if not spec: return None return Specification(max_radius_km=spec.max_radius_km, processing_time_us=spec.processing_time_us)
async def bind_to_cargo(self, cargo_name: str, timeout: float = 0.5) -> Status: request = api.Message() request.shipyard.bind_to_cargo = cargo_name if not self.send(message=request): return ShipyardI.Status.FAILED_TO_SEND_REQUEST response, _ = await self.wait_message(timeout=timeout) if not response: return ShipyardI.Status.RESPONSE_TIMEOUT status = api.get_message_field(response, ["shipyard", "bind_to_cargo_status"]) if status is None: return ShipyardI.Status.UNEXPECTED_RESPONSE return ShipyardI.Status.from_protobuf(status)
async def stop_mining(self, timeout: float = 0.5) -> Status: """Stop the mining process""" request = api.Message() request.asteroid_miner.stop_mining = True if not self.send(message=request): return AsteroidMinerI.Status.FAILED_TO_SEND_REQUEST response, _ = await self.wait_message(timeout=timeout) if not response: return AsteroidMinerI.Status.RESPONSE_TIMEOUT status = api.get_message_field( response, ["asteroid_miner", "stop_mining_status"]) if status is None: return AsteroidMinerI.Status.UNEXPECTED_RESPONSE return AsteroidMinerI.Status.from_protobuf(status)
async def scan(self, scanning_radius_km: int, minimal_radius_m: int, result_cb: Callable[[ObjectsList, Error], None], timeout: float) -> Optional[str]: """Scanning all bodies within a 'scanning_radius_km' radius. Bodies with radius less then the specified 'minimal_radius_m' will be ignored. The 'result_cb' callback will be called when another portion of data received from the server. After all data received callback will be called for the last time with (None, None) argument. Return None on success otherwise return error string """ request = api.Message() scan_req = request.celestial_scanner.scan scan_req.scanning_radius_km = scanning_radius_km scan_req.minimal_radius_m = minimal_radius_m if not self.send(message=request): error = "Failed to send request" result_cb(None, error) return error continue_scanning = True while continue_scanning: response, _ = await self.wait_message(timeout=timeout) body = api.get_message_field(response, ["celestial_scanner"]) if not body: error = "No response" result_cb(None, error) return error report = api.get_message_field(body, ["scanning_report"]) if not report: fail = api.get_message_field(body, ["scanning_failed"]) if fail: error = CelestialScannerI.Status.convert(fail).value else: error = self.__unexpected_msg_str(fail) result_cb(None, error) return error timestamp: Optional[int] = response.timestamp or None result_cb([ self.__build_object(body, timestamp) for body in report.asteroids ], None) continue_scanning = report.left > 0 result_cb(None, None) # Scanning is finished return None
async def bind_to_cargo(self, cargo_name: str, timeout: float = 0.5) -> Status: """Bind miner to the container with the specified 'cargo_name'""" request = api.Message() request.asteroid_miner.bind_to_cargo = cargo_name if not self.send(message=request): return AsteroidMinerI.Status.FAILED_TO_SEND_REQUEST response, _ = await self.wait_message(timeout=timeout) if not response: return AsteroidMinerI.Status.RESPONSE_TIMEOUT protobuf_status = api.get_message_field( response, ["asteroid_miner", "bind_to_cargo_status"]) if protobuf_status is None: return AsteroidMinerI.Status.UNEXPECTED_RESPONSE return AsteroidMinerI.Status.from_protobuf(protobuf_status)
async def get_module_info(self, slot_id: int)\ -> Optional[ModuleInfo]: """Return information about module, installed into the specified 'slot_id'. """ request = api.Message() request.commutator.module_info_req = slot_id self.send(request) response, _ = await self.wait_message() if not response: return None module_info = get_message_field(response, ["commutator", "module_info"]) if not module_info: return None info = ModuleInfo.from_protubuf(module_info) return info
async def get_specification(self, timeout: float = 0.5)\ -> (Status, Optional[Specification]): status = AsteroidMinerI.Status request = api.Message() request.asteroid_miner.specification_req = True if not self.send(message=request): return status.FAILED_TO_SEND_REQUEST, None response, _ = await self.wait_message(timeout=timeout) if not response: return status.RESPONSE_TIMEOUT, None spec = api.get_message_field(response, ["asteroid_miner", "specification"]) if not spec: return status.UNEXPECTED_RESPONSE, None spec = Specification(max_distance=spec.max_distance, cycle_time_ms=spec.cycle_time_ms, yield_per_cycle=spec.yield_per_cycle) return status.SUCCESS, spec
async def get_specification(self, timeout: float = 0.5, reset_cached=False)\ -> Optional[Specification]: if reset_cached: self.specification = None if self.specification: return self.specification request = api.Message() request.engine.specification_req = True if not self.send(message=request): return None response, _ = await self.wait_message(timeout=timeout) if not response: return None spec = api.get_message_field(response, ["engine", "specification"]) if not spec: return None self.specification = Specification(max_thrust=spec.max_thrust) return self.specification
async def get_all_modules(self) -> Optional[List[ModuleInfo]]: """Return all modules, attached to commutator. Modules received will be stored to a local cache""" success, total_slots = await self.get_total_slots() if not success: return None request = api.Message() request.commutator.all_modules_info_req = True self.send(request) result: List[ModuleInfo] = [] for i in range(total_slots): response, _ = await self.wait_message() module_info = get_message_field(response, ["commutator", "module_info"]) if not module_info: return None result.append(ModuleInfo.from_protubuf(module_info)) return result
async def transfer(self, port: int, access_key: int, resource: ResourceItem, progress_cb: Optional[Callable[[ResourceItem], None]] = None, timeout: int = 0.5) -> Status: """Transfer the specified 'resource' to the specified 'port' with the specified 'access_key'. The optionally specified 'progress_cb' will be called to report transferring status (a total amount of transferred resources).""" request = api.Message() req_body = request.resource_container.transfer req_body.port_id = port req_body.access_key = access_key resource_item = req_body.resource resource.to_protobuf(resource_item) if not self.send(message=request): return ResourceContainerI.Status.FAILED_TO_SEND_REQUEST response, _ = await self.wait_message(timeout=timeout) if not response: return ResourceContainerI.Status.RESPONSE_TIMEOUT status = api.get_message_field( response, ["resource_container", "transfer_status"]) if status is None: return ResourceContainerI.Status.UNEXPECTED_RESPONSE if status != api.IResourceContainer.Status.SUCCESS: return ResourceContainerI.Status.convert(status) # Status is success. Waiting for reports while True: response, _ = await self.wait_message(timeout=2) report = api.get_message_field( response, ["resource_container", "transfer_report"]) if not report: # May be complete status is received: status = api.get_message_field( response, ["resource_container", "transfer_finished"]) return ResourceContainerI.Status.convert(status) \ if status is not None else ResourceContainerI.Status.UNEXPECTED_RESPONSE # Got transfer report: if progress_cb is not None: progress_cb(ResourceItem.from_protobuf(report))
async def set_thrust(self, thrust: Vector, at: int = 0, duration_ms: int = 0) -> bool: """Set engine thrust to the specified 'thrust' for the specified 'duration_ms' milliseconds. If 'duration_ms' is 0, then the thrust will be set until another command. Function doesn't await any acknowledgement or response. Return true if a request has been sent """ request = api.Message() if at: request.timestamp = at thrust_req = request.engine.change_thrust thrust_req.x = thrust.x thrust_req.y = thrust.y thrust_req.thrust = int(thrust.abs()) thrust_req.duration_ms = duration_ms return self.channel.send(message=request)
async def get_blueprint(self, blueprint_name: str, timeout: float = 0.5) \ -> (Status, Optional[Blueprint]): request = api.Message() request.blueprints_library.blueprint_req = blueprint_name if not self.send(request): return Status.FAILED_TO_SEND_REQUEST, None response, _ = await self.wait_message(timeout=timeout) if not response: return Status.RESPONSE_TIMEOUT, None blueprint = api.get_message_field(response, ["blueprints_library", "blueprint"]) if blueprint: return Status.SUCCESS, Blueprint.from_protobuf(blueprint) fail = api.get_message_field(response, ["blueprints_library", "blueprint_fail"]) if fail: return Status.from_protobuf(fail), None