StarRailCopilot/module/device/platform/platform_base.py
2023-05-14 15:48:34 +08:00

177 lines
5.9 KiB
Python

import sys
import typing as t
import yaml
from pydantic import BaseModel, SecretStr
from module.base.decorator import cached_property, del_cached_property
from module.device.connection import Connection
from module.device.platform.emulator_base import EmulatorInstanceBase, EmulatorManagerBase
from module.logger import logger
from module.base.utils import SelectedGrids
class EmulatorInfo(BaseModel):
emulator: str = ''
name: str = ''
path: str = ''
# For APIs of chinac.com, a phone cloud platform.
# access_key: SecretStr = ''
# secret: SecretStr = ''
class PlatformBase(Connection, EmulatorManagerBase):
"""
Base interface of a platform, platform can be various operating system or phone clouds.
For each `Platform` class, the following APIs must be implemented.
- all_emulators()
- all_emulator_instances()
- emulator_start()
- emulator_stop()
"""
def emulator_start(self):
"""
Start a emulator, until startup completed.
- Retry is required.
- Using bored sleep to wait startup is forbidden.
"""
logger.info(f'Current platform {sys.platform} does not support emulator_start, skip')
def emulator_stop(self):
"""
Stop a emulator.
"""
logger.info(f'Current platform {sys.platform} does not support emulator_stop, skip')
@cached_property
def emulator_info(self) -> EmulatorInfo:
emulator = self.config.EmulatorInfo_Emulator
name = str(self.config.EmulatorInfo_name).strip().replace('\n', '')
path = str(self.config.EmulatorInfo_path).strip().replace('\n', '')
return EmulatorInfo(
emulator=emulator,
name=name,
path=path,
)
@cached_property
def emulator_instance(self) -> t.Optional[EmulatorInstanceBase]:
"""
Returns:
EmulatorInstanceBase: Emulator instance or None
"""
data = self.emulator_info
old_info = dict(
emulator=data.emulator,
path=data.path,
name=data.name,
)
instance = self.find_emulator_instance(
serial=str(self.config.Emulator_Serial).strip(),
name=data.name,
path=data.path,
emulator=data.emulator,
)
# Write complete emulator data
if instance is not None:
new_info = dict(
emulator=instance.type,
path=instance.path,
name=instance.name,
)
if new_info != old_info:
with self.config.multi_set():
self.config.EmulatorInfo_Emulator = instance.type
self.config.EmulatorInfo_name = instance.name
self.config.EmulatorInfo_path = instance.path
del_cached_property(self, 'emulator_info')
return instance
def find_emulator_instance(
self,
serial: str,
name: str = None,
path: str = None,
emulator: str = None
) -> t.Optional[EmulatorInstanceBase]:
"""
Args:
serial: Serial like "127.0.0.1:5555"
name: Instance name like "Nougat64"
path: Emulator install path like "C:/Program Files/BlueStacks_nxt/HD-Player.exe"
emulator: Emulator type defined in Emulator class, like "BlueStacks5"
Returns:
EmulatorInstance: Emulator instance or None if no instances not found.
"""
logger.hr('Find emulator instance', level=2)
instances = SelectedGrids(self.all_emulator_instances)
for instance in instances:
logger.info(instance)
search_args = dict(serial=serial)
# Search by serial
select = instances.select(**search_args)
if select.count == 0:
logger.warning(f'No emulator instance with {search_args}')
return None
if select.count == 1:
instance = select[0]
logger.hr('Emulator instance', level=2)
logger.info(f'Found emulator instance: {instance}')
return instance
# Multiple instances in given serial, search by name
if name:
search_args['name'] = name
select = instances.select(**search_args)
if select.count == 0:
logger.warning(f'No emulator instances with {search_args}')
return None
if select.count == 1:
instance = select[0]
logger.hr('Emulator instance', level=2)
logger.info(f'Found emulator instance: {instance}')
return instance
# Multiple instances in given serial and name, search by path
if path:
search_args['path'] = path
select = instances.select(**search_args)
if select.count == 0:
logger.warning(f'No emulator instances with {search_args}')
return None
if select.count == 1:
instance = select[0]
logger.hr('Emulator instance', level=2)
logger.info(f'Found emulator instance: {instance}')
return instance
# Multiple instances in given serial, name and path, search by emulator
if emulator:
search_args['type'] = emulator
select = instances.select(**search_args)
if select.count == 0:
logger.warning(f'No emulator instances with {search_args}')
return None
if select.count == 1:
instance = select[0]
logger.hr('Emulator instance', level=2)
logger.info(f'Found emulator instance: {instance}')
return instance
# Still too many instances
logger.warning(f'Found multiple emulator instances with {search_args}')
return None
if __name__ == '__main__':
self = PlatformBase('alas')
d = self.emulator_instance
print(d)