Files
controller-wisetopdataserver/python/biopro/message/data.py
T
2021-12-20 14:52:55 +08:00

235 lines
7.0 KiB
Python

from enum import IntEnum
from pathlib import Path
from typing import Optional, Union, List, Tuple, Any
# noinspection PyUnresolvedReferences
import biopro.ext.message as util_message
from biopro.recording import RecordingMetaFile, RecordingData
from biopro.util.channel import ChannelMask
__all__ = ['DataPresentMode', 'DataMessageEncoder', 'RecordingFileReader']
inf = float('inf')
class DataPresentMode(IntEnum):
RAW = 0
FIRST = 1
AVERAGE = 2
# XXX not supported current
AVERAGE_BOUNDARY = 3
class DataMessageEncoder:
"""It is typing used of the abstract interface of DataMessageEncoder defined in c extension.
::
| | 1 | 2 | 3 |
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
-----------------------------------------------------------------
| header | device ID | sample rate |
| ch_count |
| channel | width | mode | data length |
| time stamp |
| ------------------------------------------------------------- |
| index | data[*] | mode == FIRST|AVERAGE
| ------------------------------------------------------------- |
| index | data[*] | mode == AVERAGE_BOUNDARY
| min % | max % |
| ------------------------------------------------------------- |
| channel' | width | mode | data length | next data section
| ------------------------------------------------------------- |
| header=0x03 | device' ID | sample rate' | next device section
| ------------------------------------------------------------- |
header
message header, value > 0
data length
little endian
time stamp
msec, little endian
sample rate
unit 1/s, little endian
ch_count
channel count
width
data width, 2 or 4 bytes
mode
data present mode
index
time_step. always increase. may overflow
data
unsigned little endian int. actually value equal to data - 0x8000
"""
__slots__ = '__impl', '__mod_count', '__mod_message', '__message_cache'
def __init__(self,
header: int,
mode: DataPresentMode,
sample_rate: int,
device: int,
channel: Union[int, List[int], ChannelMask]):
"""
:param header: data message header
:param mode: data present mode
:param sample_rate: sample rate in unit 1/sec
:param device: listen device ID
:param channel: listen channel or channel mask
:return: data message encoder
"""
if sample_rate < 1:
raise ValueError('sample rate less then 1 : ' + str(sample_rate))
if isinstance(channel, int):
channel = [channel]
self.__impl: 'DataMessageEncoder' = util_message.DataMessageEncoder(header,
mode.value,
sample_rate,
device,
channel)
# message() cache
self.__mod_count = 0
self.__mod_message = 0
self.__message_cache: Optional[bytes] = None
@property
def header(self) -> int:
return self.__impl.header
@property
def width(self) -> int:
return self.__impl.width
@property
def mode(self) -> int:
return self.__impl.mode
@mode.setter
def mode(self, value: int):
self.__impl.mode = value
@property
def sample_rate(self) -> float:
return self.__impl.sample_rate
@property
def device(self) -> int:
return self.__impl.device
def channels(self) -> List[int]:
return self.__impl.channels()
def contain_channel(self, channel: int) -> bool:
return self.__impl.contain_channel(channel)
def append_entry(self, time_stamp: float, channel: int, value: int):
"""
:param time_stamp: time stamp in unit msec
:param channel: channel ID
:param value: data value
"""
self.__impl.append_entry(time_stamp, channel, value)
self.__mod_count += 1
def append_data(self, data: RecordingData, time_start: float = 0.0, time_stop: float = inf):
self.__impl.append_data(data, time_start, time_stop)
self.__mod_count += 1
def append_file(self, file: 'RecordingFileReader', time_start: float = 0.0, time_stop: float = inf):
self.__impl.append_file(file._impl, time_start, time_stop)
self.__mod_count += 1
def message(self) -> Optional[bytes]:
if self.__message_cache is not None:
if self.__mod_message == self.__mod_count:
return self.__message_cache
ret = self.__impl.message()
self.__message_cache = ret
self.__mod_message = self.__mod_count
return ret
class RecordingFileReader:
"""It is typing used of the warp class for RecordingFileReader defined in c extension.
"""
__slots__ = ('_impl',)
def __init__(self, meta: Union[RecordingMetaFile, Path, str]):
if isinstance(meta, RecordingMetaFile):
path = str(meta.filepath)
else:
path = str(meta)
self._impl: 'RecordingFileReader' = util_message.RecordingFileReader(path.encode())
@property
def file_path(self) -> bytes:
return self._impl.file_path
@property
def version_number(self) -> int:
return self._impl.version_number
@property
def create_time(self) -> int:
return self._impl.create_time
@property
def file_uuid(self) -> bytes:
return self._impl.file_uuid
@property
def data_format(self) -> bytes:
return self._impl.data_format
@property
def device_name(self) -> bytes:
return self._impl.device_name
@property
def device_address(self) -> bytes:
return self._impl.device_address
@property
def device_serial(self) -> bytes:
return self._impl.device_serial
@property
def parameter_count(self) -> int:
return self._impl.parameter_count
def parameter(self, index: int) -> Tuple[str, Any]:
return self._impl.parameter(index)
@property
def recording_file_size(self) -> int:
return self._impl.recording_file_size
def recording_file_name(self, file_index: int) -> bytes:
return self._impl.recording_file_name(file_index)
def invalid_cache(self):
# TODO not implement in C extension
pass