1722 lines
43 KiB
Python
1722 lines
43 KiB
Python
"""Controller API, it define the data/message protocol between the web server and the controller server.
|
|
|
|
**Controller Server**
|
|
|
|
A facet server which transfer the request to purpose manager class.
|
|
|
|
"""
|
|
|
|
import base64
|
|
from typing import Optional, Iterable, TypeVar, Generic
|
|
|
|
from biopro.text import *
|
|
from biopro.util.json import *
|
|
from biopro.util.socket import SocketClient, SocketClientMacro, SocketTimeoutError
|
|
|
|
try:
|
|
from biopro.util.router import router, Router
|
|
except ImportError:
|
|
Router = type
|
|
|
|
|
|
def router(*args, **kwargs):
|
|
return lambda f: f
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
|
class ComplexResponse(Generic[T]):
|
|
"""response with the cookie data"""
|
|
|
|
__slots__ = 'response', 'response_cookie'
|
|
|
|
def __init__(self, response: T, cookie: Union[Dict[str, str], 'ComplexResponse'] = None):
|
|
self.response: T = response
|
|
'''original response data'''
|
|
|
|
if isinstance(cookie, dict):
|
|
cookie = dict(cookie)
|
|
elif isinstance(cookie, ComplexResponse):
|
|
cookie = dict(cookie.response_cookie)
|
|
else:
|
|
cookie = {}
|
|
|
|
self.response_cookie = cookie
|
|
'''cookie'''
|
|
|
|
def set_cookie(self, **cookie: str):
|
|
self.response_cookie.update(cookie)
|
|
|
|
|
|
class ControlAPI(metaclass=Router):
|
|
"""Application Programming Interface for Controller server.
|
|
|
|
**API grouping**
|
|
|
|
1. ``hardware_*``
|
|
|
|
controller general function
|
|
|
|
#. ``get_device_*``, ``device_*``
|
|
|
|
device relate function
|
|
|
|
#. ``file_*``
|
|
|
|
file system, export function from :class:`biopro.file.manager.FileManager`
|
|
|
|
#. ``user_*``
|
|
|
|
user setting, export function from :class:`biopro.file.user.manager.UserSettingManager`
|
|
|
|
#. ``exp_pro_*``
|
|
|
|
experimental protocol. export function from :class:`biopro.exp_pro.manager.ExpManager`
|
|
|
|
"""
|
|
|
|
ADDR = '/tmp/BPS.socket'
|
|
'''controller server socket file path'''
|
|
|
|
__slots__ = ()
|
|
|
|
@router('hardware/init')
|
|
def hardware_info(self) -> JSON_OBJECT:
|
|
"""the information of the master hardware.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.server.controller.HardwareInfo`
|
|
|
|
:return: server information in json object.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/datetime', GET=dict(content=None), POST={})
|
|
def hardware_datetime(self, content: Optional[str]) -> str:
|
|
"""get/set hardware datetime.
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
datetime expression
|
|
|
|
**Response Specification**
|
|
|
|
datetime expression
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/scan', PUT={})
|
|
def hardware_scan(self) -> bool:
|
|
"""scan nearby device.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
this function may broadcast scan message.
|
|
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/device')
|
|
def hardware_device(self) -> JSON_ARRAY:
|
|
"""get the information of the found devices.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
:return: id list of the device response information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/device', POST={})
|
|
def hardware_device_found(self, content: str, *, scan: str = 'false') -> JSON:
|
|
"""get the information of the specific device.
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
``MAC_ADDRESS``
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
:return: device response information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/device', PUT={})
|
|
def hardware_device_connect(self, content: Union[str, JSON_OBJECT]) -> JSON_OBJECT:
|
|
"""connect device.
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
:class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
required "device_address".
|
|
|
|
secondary required "device_name" and "serial_number"
|
|
|
|
other left property are ignored.
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.devlib.device.Device`.
|
|
|
|
this function will broadcast connected message.
|
|
|
|
:param content: device name and address
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def hardware_device_direct_connect(self, content: Union[str, JSON_OBJECT]) -> JSON_OBJECT:
|
|
"""direct connect device.
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
:class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
required "device_address".
|
|
|
|
secondary required "device_name" and "serial_number"
|
|
|
|
other left property are ignored.
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.devlib.device.Device`.
|
|
|
|
this function will broadcast connected message.
|
|
|
|
:param content: device name and address
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/device', DEL={'device': None})
|
|
@router('hardware/device/<int:device>', DEL={})
|
|
def hardware_device_disconnect(self, device: Optional[int], force=False) -> bool:
|
|
"""disconnect device.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
this function will broadcast disconnected message.
|
|
|
|
:param device: device ID, None for all
|
|
:param force: force disconnect
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/menu')
|
|
def hardware_menu(self) -> Optional[JSON_OBJECT]:
|
|
"""hardware manager menu.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
nullable menu json.
|
|
|
|
:class:`biopro.devlib.menu.Menu`.
|
|
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/send/<str:command>')
|
|
def hardware_send(self, command: str, **options: str) -> JSON:
|
|
"""send command to the hardware.
|
|
|
|
**Request Url**
|
|
|
|
**command format**
|
|
|
|
**general/controller command**
|
|
|
|
=============== =============== =========================
|
|
command options description
|
|
=============== =============== =========================
|
|
led state=STATE
|
|
power_off s=SEC
|
|
test [SECTION=S[,S]]
|
|
device manager=COMMAND
|
|
measure item=ITEM
|
|
=============== =============== =========================
|
|
|
|
**master device command**
|
|
|
|
=================== =========== =============================
|
|
command options description
|
|
=================== =========== =============================
|
|
scan
|
|
create_demo_device
|
|
=================== =========== =============================
|
|
|
|
**Response Specification**
|
|
|
|
:param command: hardware command
|
|
:param options: command options
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/')
|
|
def get_device_info_all(self) -> JSON_ARRAY:
|
|
"""get all of the information of the connected device.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.devlib.device.Device`
|
|
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/info')
|
|
def get_device_info(self, device: int) -> JSON_OBJECT:
|
|
"""get the information of the device with ID ``device`` .
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.devlib.device.Device`
|
|
|
|
:param device: device ID
|
|
:return: json object of device information, null it not Found
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/instruction')
|
|
def device_instruction_all(self, device: int) -> List[str]:
|
|
"""list device instructions.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of instruction
|
|
|
|
:param device: device ID
|
|
:return: list of instruction
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/all/instruction/<str:instruction>', POST={})
|
|
def device_all_instruction(self, instruction: str, content: str) -> bool:
|
|
"""send instruction to all of the connected device
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
device ID list spread with ','
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
:param instruction: device instruction
|
|
:param content: device IDs
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/instruction/<str:instruction>', POST={})
|
|
def device_instruction(self, device: int, instruction: str) -> bool:
|
|
"""send instruction to device
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
:param device: device ID
|
|
:param instruction: device instruction
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/battery', GET={})
|
|
def device_battery(self) -> List[int]:
|
|
"""get device battery information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
battery
|
|
|
|
:return: battery
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/parent', GET={})
|
|
def device_parent(self) -> List[int]:
|
|
"""get device parent information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
parent
|
|
|
|
:return: parent
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/recording_file_name', GET={})
|
|
def device_recording_file_name(self) -> List[int]:
|
|
"""get device recording_file_name information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
recording_file_name
|
|
|
|
:return: recording_file_name
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/sd_card', GET={})
|
|
def device_sd_card_status(self, device:int) -> None:
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/calibration', GET={})
|
|
def device_update_calibration(self) -> str:
|
|
"""get device battery information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
Calibration
|
|
|
|
:return: Boolean
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/<int:radio_frequency>', GET={})
|
|
def device_update_radio_frequency(self, device: int, radio_frequency:int) -> bool:
|
|
"""get device radio frequency.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:return: Boolean
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/<int:led_brightness>', GET={})
|
|
def device_update_led_brightness(self, device: int, led_brightness:int) -> bool:
|
|
"""get device led brightness.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:return: Boolean
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/<int:accelerator_sensitivity>', GET={})
|
|
def device_update_accelerator_sensitivity(self, device: int, accelerator_sensitivity:int) -> bool:
|
|
"""get device accelerator sensitivity.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:return: Boolean
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/p', GET={})
|
|
def device_parameter_last_update_timestamp(self, device: int) -> int:
|
|
"""get last timestamp of parameter operating.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
timestamp
|
|
|
|
:param device: device ID
|
|
:return: timestamp
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/p/<str:parameter>', GET={}, POST={})
|
|
def device_parameter(self, device: int, parameter: str, content: Optional[str] = None) -> JSON:
|
|
"""get/set device parameter P value.
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
new parameter value or operator
|
|
|
|
**Response Specification**
|
|
|
|
raw parameter value.
|
|
|
|
:param device: device ID
|
|
:param parameter: parameter name
|
|
:param content: new parameter value
|
|
:return: new parameter P value
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/P/<str:parameter>', GET={})
|
|
def device_parameter_value(self, device: int, parameter: str) -> JSON:
|
|
"""get device parameter V value.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
human readable parameter value.
|
|
|
|
:param device: device ID
|
|
:param parameter: parameter name
|
|
:return: parameter V value
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/file',
|
|
GET={'unset': False}, DEL={'unset': True})
|
|
def device_save_path(self, device: int, unset: bool) -> Optional[str]:
|
|
"""get the current saving *path*.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
file path, None if DELETE
|
|
|
|
:param device: device ID
|
|
:param unset: unset path
|
|
:return: file path
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/<int:device>/file',
|
|
PUT={'dry_run': False}, POST={'dry_run': True})
|
|
def device_save_path_set(self, device: int, content: str, dry_run: bool) -> str:
|
|
"""set device saving *path*
|
|
|
|
**Request Url**
|
|
|
|
**Request content**
|
|
|
|
filename, use % to auto generate field.
|
|
|
|
========= ============================================
|
|
flag meaning
|
|
========= ============================================
|
|
%% char '%'
|
|
%t datetime
|
|
%Td date
|
|
%Tt time
|
|
%TY year
|
|
%Tn month in number
|
|
%TN month in text
|
|
%Tw week in number
|
|
%TW week in text
|
|
%TD day
|
|
%Th hour (12 hour)
|
|
%TH hour (24 hour)
|
|
%Tm minute
|
|
%Ts second
|
|
%d device ID
|
|
%Dn device name
|
|
%Dm device address
|
|
%Dt device library name
|
|
%Un user name (not support now)
|
|
%?FLAG{%} if FLAG result not empty, use content in {%}
|
|
========= ============================================
|
|
|
|
**Response Specification**
|
|
|
|
file path
|
|
|
|
:param device: device ID
|
|
:param content: file name
|
|
:param dry_run: just return result, do not set anything
|
|
:return: file path
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/history/', GET={'user': None})
|
|
@router('device/history/<str:user>')
|
|
def device_history_get(self, user: Optional[str]) -> JSON_ARRAY:
|
|
"""get user used device history.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
:param user: certain user
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/history/<str:user>/all/found')
|
|
def device_history_found_all(self, user: str) -> JSON_ARRAY:
|
|
"""
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
:param user: certain user
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/history/<str:user>/<int:device>/found')
|
|
def device_history_found(self, user: str, device: int) -> JSON_OBJECT:
|
|
"""
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.devlib.device.DeviceResponseInfo`
|
|
|
|
:param user: certain user
|
|
:param device: device number
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('device/history/<str:user>', DEL={'device': None})
|
|
@router('device/history/<str:user>/<int:device>', DEL={})
|
|
def device_history_delete(self, user: str, device: Optional[int]) -> bool:
|
|
"""remove used device record from history.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
:param user: certain user
|
|
:param device: device number
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/', DEL={})
|
|
def file_clean_storage(self) -> JSON:
|
|
"""clean storage
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
:func:`biopro.file.api.FileAPI.clean_storage`
|
|
|
|
:return:
|
|
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/p')
|
|
def file_index(self) -> JSON:
|
|
"""root directory information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.util.RootInfo`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.index`
|
|
|
|
:return: root directory information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/p/', GET={'path': ''})
|
|
@router('file/p/<path:path>')
|
|
def file_list(self, path: str) -> JSON:
|
|
"""list directory content
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.util.DirectoryInfo`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.list`
|
|
|
|
:param path: directory path
|
|
:return: directory information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/p/', GET={'path': '', 'start': 0, 'end': 0})
|
|
@router('file/p/<path:path>/<start:int>/<end:int>')
|
|
def file_list_range(self, path: str) -> JSON:
|
|
"""list directory content
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.util.DirectoryInfo`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.list`
|
|
|
|
:param path: directory path
|
|
:return: directory information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/p/<path:path>', POST={})
|
|
def file_rename(self, path: str, content: str) -> JSON:
|
|
"""rename recording session to *path*
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
target file path
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.util.FileInfo`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.rename`
|
|
|
|
:param path: source file path
|
|
:param content: target file path
|
|
:return: file information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/p/<path:path>', PUT={})
|
|
def file_mkdir(self, path: str) -> JSON:
|
|
"""create directory at *path*
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.util.DirectoryInfo`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.mkdir`
|
|
|
|
:param path: directory path need to be create
|
|
:return: directory information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/p/<path:path>', DEL={})
|
|
def file_delete(self, path: str) -> JSON:
|
|
"""delete file at *path*
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
bool.
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.delete`
|
|
|
|
:param path:
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/f/', GET={'path': ''})
|
|
@router('file/f/<path:path>')
|
|
def file_info(self, path: str) -> JSON:
|
|
"""get file information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.util.FileInfo`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.info`
|
|
|
|
:param path: file path
|
|
:return: file information
|
|
:raises FileNotFoundError:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/f/<path:path>/-meta')
|
|
def file_meta_info(self, path: str) -> JSON:
|
|
"""get meta file information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.recording.RecordingMetaFile`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.meta`
|
|
|
|
:param path: file path
|
|
:return: meta information
|
|
:raises FileNotFoundError:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/f/<path:path>/-export')
|
|
def file_export_info(self, path: str) -> JSON:
|
|
"""get exported files information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.file.api.FileAPI.export_info`
|
|
|
|
:param path: file path
|
|
:return: file export information
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/e/')
|
|
def file_export_functions(self) -> JSON:
|
|
"""list export functions
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.export.ExternalFunction`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.manager.FileManager.get_export_functions`
|
|
|
|
:return: list of functions
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/e/<str:ftype>')
|
|
def file_export_parameters(self, ftype: str) -> JSON:
|
|
"""list export function options.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.export.ExternalParameter`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.manager.FileManager.get_export_parameters`
|
|
|
|
:param ftype: export function type
|
|
:return: list of parameters
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/e/<str:ftype>/<path:path>')
|
|
def file_export(self, ftype: str, path: str, **options: str) -> str:
|
|
"""export file from *path*
|
|
|
|
**Request Url**
|
|
|
|
**Request Options**
|
|
|
|
export function options.
|
|
|
|
**Response Specification**
|
|
|
|
path
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.file.api.FileAPI.export_file`
|
|
|
|
:param ftype: export function
|
|
:param path: source file path
|
|
:param options: export function options
|
|
:return: export result file path
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/s', DEL=dict(method='invalid_all', ftype='', path=''))
|
|
@router('file/s/p/<path:path>',
|
|
GET=dict(method='info'),
|
|
PUT=dict(method='new'),
|
|
POST=dict(method='reload'),
|
|
DEL=dict(method='invalid'))
|
|
def file_segment(self, method: str, path: str) -> JSON:
|
|
"""file segment operating.
|
|
|
|
if *method* is **invalid_all**, ignore *ftype* and *path*.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
for method is *info* and *new*
|
|
|
|
:class:`biopro.file.segment.FileSegment`
|
|
|
|
for left methods:
|
|
|
|
boolean, success or not
|
|
|
|
:param method: one of info, new, reload, invalid and invalid_all
|
|
:param path: export file link
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/s/<int:segment>/<path:path>')
|
|
def file_segment_get(self, segment: int, path: str) -> JSON:
|
|
"""get file segment
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.segment.FileSegmentContent`
|
|
|
|
:param segment: segment index
|
|
:param path: export file link
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/h', PUT={})
|
|
def file_handle_open(self, content: str) -> JSON:
|
|
"""open file
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
open file path
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.server.cache.FileHandle`
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.server.cache.CacheManager.file_open`
|
|
|
|
:param content: file path
|
|
:return: file handle
|
|
:raises FileNotFoundError:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/h/<int:handle>', GET={})
|
|
def file_handle_info(self, handle: int) -> JSON:
|
|
"""get information of file handle
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.server.cache.FileHandle`, null if *handle* not found
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.server.cache.CacheManager.file_meta`
|
|
|
|
:param handle: file handle
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('file/h/<int:handle>', DEL={})
|
|
def file_handle_close(self, handle: int):
|
|
"""close file handle
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
null
|
|
|
|
**Relative Method**
|
|
|
|
:func:`biopro.server.cache.CacheManager.file_close`
|
|
|
|
:param handle: file handle
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user')
|
|
def user_all(self) -> JSON_ARRAY:
|
|
"""get all of the user name
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of string
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.file.user.UserSettingAPI.user_list`
|
|
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/-info', GET=dict(user=''))
|
|
@router('user/u/<str:user>')
|
|
def user_info(self, user: str, *,
|
|
user_session: Optional[str] = None) -> JSON:
|
|
"""get user information
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.AbstractUserSetting`
|
|
|
|
:class:`biopro.file.user.UserSetting`
|
|
|
|
null if no user login.
|
|
|
|
**Used Cookie**
|
|
|
|
:param user: user name
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/u/<str:user>', POST={}, COOKIE=('user_session',))
|
|
def user_login(self, user: str, content: str, *,
|
|
user_session: Optional[str] = None) -> ComplexResponse[JSON]:
|
|
"""login an user
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
user password
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.UserSetting`
|
|
|
|
**Used Cookie**
|
|
|
|
:param user: user name
|
|
:param content: user password
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/-logout', POST={}, COOKIE=('user_session',))
|
|
def user_logout(self, user_session: Optional[str] = None) -> JSON:
|
|
"""logout an user
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
user password
|
|
|
|
**Response Specification**
|
|
|
|
null
|
|
|
|
**Used Cookie**
|
|
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/u/<str:user>', PUT={}, COOKIE=('user_session',))
|
|
def user_add(self, user: str, content: str, *,
|
|
user_session: Optional[str] = None) -> ComplexResponse[JSON]:
|
|
"""add an user
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
user password
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.UserSetting`
|
|
|
|
**Used Cookie**
|
|
|
|
:param user: user name
|
|
:param content: user password
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/u/<str:user>', DEL={}, COOKIE=('user_session',))
|
|
def user_del(self, user: str, content: str, *,
|
|
user_session: Optional[str]) -> bool:
|
|
"""delete an user
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
user password
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**Used Cookie**
|
|
|
|
:param user: user name
|
|
:param content: user password
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/-sync', POST={}, COOKIE=('user_session',))
|
|
def user_sync(self, user_session: Optional[str]) -> bool:
|
|
"""sync user setting
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
success or not
|
|
|
|
**Used Cookie**
|
|
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/c', COOKIE=('user_session',))
|
|
def user_configuration_list(self, user_session: Optional[str], **options: str) -> List[str]:
|
|
"""list/filter user's configurations
|
|
|
|
**Request Url**
|
|
|
|
**Request Options**
|
|
|
|
* ``device`` : device name filter.
|
|
* ``library`` : library name filter.
|
|
|
|
**Response Specification**
|
|
|
|
list of configuration name, empty if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param user_session: user session
|
|
:param options: filter options
|
|
:return: configuration name list
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/c/<str:name>', COOKIE=('user_session',))
|
|
def user_configuration_get(self, name: str, user_session: Optional[str] = None) -> JSON_OBJECT:
|
|
"""get user configuration.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.recording.RecordingMetaFile`
|
|
|
|
null if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: configuration name
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/c/<str:name>', PUT={}, COOKIE=('user_session',))
|
|
def user_configuration_add(self, name: str, content: str, user_session: Optional[str] = None) -> bool:
|
|
"""add new user configuration from meta file
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
source meta filepath
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: new configuration name.
|
|
:param content: source meta filepath
|
|
:param user_session: user session
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/c/<str:name>', DEL={}, COOKIE=('user_session',))
|
|
def user_configuration_del(self, name: str, user_session: Optional[str] = None) -> bool:
|
|
"""delete user configuration.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: configuration name want to removed.
|
|
:param user_session: user session
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/c/<str:name>', POST={}, COOKIE=('user_session',))
|
|
def user_configuration_clone(self, name: str, content: str, user_session: Optional[str] = None) -> bool:
|
|
"""clone configuration *name* from *from_user* to *user* and given a *new_name*
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
``[USER:]NAME``
|
|
|
|
* ``USER`` : *from_user*
|
|
* ``NAME`` : configuration name
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: source configuration name
|
|
:param content: target configuration name
|
|
:param user_session: user session
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/e', COOKIE=('user_session',))
|
|
def user_setup_list(self, user_session: Optional[str] = None) -> List[str]:
|
|
"""list user experimental setup list.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of name of experimental setup, empty if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/e/<str:name>', COOKIE=('user_session',))
|
|
def user_setup_info(self, name: str, user_session: Optional[str] = None) -> JSON_OBJECT:
|
|
"""get user experimental setup information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.UserSetup`, null if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: setup name
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/e/<str:name>', PUT={}, COOKIE=('user_session',))
|
|
def user_setup_new(self, name: str, user_session: Optional[str] = None) -> JSON_OBJECT:
|
|
"""add new user experimental setup.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.UserSetup`, null if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: setup name
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/e/<str:name>', DEL={}, COOKIE=('user_session',))
|
|
def user_setup_del(self, name: str, user_session: Optional[str] = None) -> bool:
|
|
"""remove user experimental setup.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: new setup name
|
|
:param user_session: user session
|
|
:return: success or not
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/a', COOKIE=('user_session',))
|
|
def user_alias_list(self, user_session: Optional[str] = None) -> JSON_ARRAY:
|
|
"""list user device alias.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.file.user.UserDeviceAlias`, empty if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/a/<str:name>', COOKIE=('user_session',))
|
|
def user_alias_info(self, name: str, user_session: Optional[str] = None) -> JSON_OBJECT:
|
|
"""get user device alias information.
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.UserDeviceAlias`, null if user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: device name, alias or mac address
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('user/a/<str:name>',
|
|
PUT={},
|
|
DEL=dict(content=''),
|
|
COOKIE=('user_session',))
|
|
def user_alias_edit(self, name: str, content: str, user_session: Optional[str] = None) -> JSON_OBJECT:
|
|
"""edit user device alias information.
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
new device alias. empty for delete
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.file.user.UserDeviceAlias`
|
|
|
|
null if DELETE, or create a alias without giving device address, or user not login
|
|
|
|
**Used Cookie**
|
|
|
|
:param name: device name, alias or mac address
|
|
:param content: new device alias
|
|
:param user_session: user session
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp')
|
|
def exp_pro_list(self) -> JSON:
|
|
"""list available experimental protocol
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.exp_pro.protocol.ExpProtocolInfo`
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.list_available_protocol`
|
|
|
|
:return: protocol
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp', PUT={}, COOKIE=('user_session',))
|
|
def exp_pro_create(self, content: str, *,
|
|
user_session: Optional[str] = None) -> int:
|
|
"""create new experimental protocol
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
protocol name
|
|
|
|
**Response Specification**
|
|
|
|
protocol ID
|
|
|
|
**Used Cookie**
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.create_protocol`
|
|
|
|
:return: protocol ID
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp/p/<int:protocol>', GET={})
|
|
def exp_pro_options(self, protocol: int) -> JSON:
|
|
"""list experimental protocol options
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.exp_pro.protocol.ExpOption`
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.protocol_options`
|
|
|
|
:param protocol: protocol ID
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp/p/<int:protocol>/',
|
|
DEL=dict(option='', content=''))
|
|
@router('exp/p/<int:protocol>/<str:option>',
|
|
GET=dict(content=None),
|
|
PUT={},
|
|
DEL=dict(content=''))
|
|
def exp_pro_option(self, protocol: int, option: str, content: Optional[str] = None) -> bool:
|
|
"""set/reset experimental protocol option
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
new option value
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.protocol_option`
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.protocol_reset`
|
|
|
|
:param protocol: protocol ID
|
|
:param option: protocol option name
|
|
:param content: option value
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp/s')
|
|
def exp_pro_status_all(self) -> JSON:
|
|
"""get all experimental protocol option status
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
list of :class:`biopro.exp_pro.schedule.AbstractExpProtocolState` or
|
|
:class:`biopro.exp_pro.schedule.ExpProtocolState`
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.protocol_status`
|
|
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp/s/<int:protocol>')
|
|
def exp_pro_status(self, protocol: int) -> JSON:
|
|
"""get experimental protocol status
|
|
|
|
**Request Url**
|
|
|
|
**Response Specification**
|
|
|
|
:class:`biopro.exp_pro.schedule.AbstractExpProtocolState`
|
|
|
|
:class:`biopro.exp_pro.schedule.ExpProtocolState`
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.protocol_status`
|
|
|
|
:param protocol: protocol ID
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('exp/s/<int:protocol>', POST={})
|
|
def exp_pro_command(self, protocol: int, content: str) -> bool:
|
|
"""get experimental protocol status
|
|
|
|
**Request Url**
|
|
|
|
**Request Body**
|
|
|
|
============== ===============================
|
|
action command description
|
|
============== ===============================
|
|
ensure ensure experimental setup
|
|
start start experimental
|
|
interrupt interrupt experimental
|
|
suspend suspend experimental
|
|
resume resume suspend experimental
|
|
============== ===============================
|
|
|
|
|
|
**Response Specification**
|
|
|
|
boolean, success or not
|
|
|
|
**relative method**
|
|
|
|
:func:`biopro.exp_pro.manager.ExpManager.protocol_command`
|
|
|
|
:param protocol: protocol ID
|
|
:param content: action command
|
|
:return:
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@router('hardware/restart')
|
|
def restart_server(self) -> None:
|
|
"""restart server process.
|
|
|
|
**Request Url**
|
|
|
|
This method will terminate current process with restart exit code. The startup shell script
|
|
should handle this exit code and re-run server process.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def run_project(self, project) -> bool:
|
|
raise NotImplementedError()
|
|
|
|
def get_running_project(self) -> bool:
|
|
raise NotImplementedError()
|
|
|
|
def stop_project(self, project) -> bool:
|
|
raise NotImplementedError()
|
|
|
|
# noinspection PyAbstractClass
|
|
class ControlClient(SocketClient, ControlAPI, metaclass=SocketClientMacro(ControlAPI)):
|
|
"""Connect to controller server through the socket.
|
|
|
|
All of the method in this class will pack the parameter into bytes and transmit to the controller server
|
|
through the socket.
|
|
|
|
"""
|
|
|
|
__slots__ = ()
|
|
|
|
def __init__(self):
|
|
super().__init__(self.ADDR)
|
|
|
|
|
|
class ControlContext(JsonSerialize):
|
|
"""Connect to controller server through the socket.
|
|
|
|
It can hole the socket connection between the command call. ::
|
|
|
|
context = ControlContext()
|
|
with context as client:
|
|
# do something
|
|
context.accept(client.call_function())
|
|
|
|
This class implement context manager, and will catch all exception then set the error response.
|
|
|
|
**Error Response Specification**
|
|
|
|
::
|
|
|
|
return = response | error
|
|
error = {
|
|
"error": str # error message
|
|
}
|
|
|
|
"""
|
|
|
|
__slots__ = '_client', '_data', '_response_cookie', '_error'
|
|
|
|
def __init__(self):
|
|
self._client: ControlClient = None
|
|
self._data = None
|
|
self._response_cookie = {}
|
|
self._error = None
|
|
|
|
def accept(self, data: Any):
|
|
"""accept the data from controller server."""
|
|
if isinstance(data, ComplexResponse):
|
|
self._response_cookie = data.response_cookie
|
|
data = data.response
|
|
|
|
if isinstance(data, bytes):
|
|
data = base64.b64encode(data).decode()
|
|
|
|
self._data = data
|
|
|
|
@property
|
|
def response_data(self) -> JSON:
|
|
"""original response data"""
|
|
return self._data
|
|
|
|
@property
|
|
def response_cookie(self) -> Iterable[Dict[str, Any]]:
|
|
"""response cookie"""
|
|
for k, v in self._response_cookie.items():
|
|
yield dict(key=k, value=v)
|
|
|
|
@property
|
|
def has_error(self) -> bool:
|
|
return self._error is not None
|
|
|
|
def as_json(self) -> JSON_OBJECT:
|
|
"""
|
|
|
|
:return: json data
|
|
"""
|
|
if self._error is not None:
|
|
return {
|
|
'error': self._error[0],
|
|
'message': self._error[1]
|
|
}
|
|
else:
|
|
return {
|
|
'data': self._data
|
|
}
|
|
|
|
def __enter__(self):
|
|
"""nothing, return self"""
|
|
|
|
self._client = ret = ControlClient()
|
|
|
|
ret.open_socket()
|
|
|
|
return ret
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""capture error, and set it into json data with key ``error``."""
|
|
|
|
client: Optional[ControlClient] = self._client
|
|
self._client = None
|
|
|
|
if client is not None:
|
|
try:
|
|
client.close_socket()
|
|
except:
|
|
raise NotImplementedError()
|
|
|
|
# handler error during DataClient using
|
|
if exc_type is not None:
|
|
if exc_type == ConnectionRefusedError:
|
|
# for AF_INET
|
|
self._error = (exc_type.__name__, SERVER_NOT_AVAILABLE)
|
|
elif exc_type == FileNotFoundError:
|
|
# for AF_UNIX
|
|
self._error = (exc_type.__name__, SERVER_NOT_AVAILABLE)
|
|
elif exc_type == SocketTimeoutError:
|
|
self._error = (exc_type.__name__, SERVER_NO_RESPONSE)
|
|
elif exc_type == RuntimeError and len(exc_val.args) >= 2:
|
|
exc_type = exc_val.args[0]
|
|
exc_val = exc_val.args[1:]
|
|
|
|
if isinstance(exc_val, str):
|
|
self._error = (exc_type, str(exc_val))
|
|
elif isinstance(exc_val, (tuple, list)):
|
|
self._error = (exc_type, ' '.join(map(str, exc_val)))
|
|
else:
|
|
self._error = (exc_type, str(exc_val))
|
|
else:
|
|
self._error = (exc_type.__name__, str(exc_val))
|
|
|
|
# suppressed exception
|
|
return True
|