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

217 lines
5.7 KiB
Python

import uuid
from enum import IntEnum, unique
from biopro.devlib.device import DeviceResponseInfo, DeviceSerialNumber
from biopro.devlib.encoder import *
from biopro.util.channel import ChannelMask
META_MAGIC_NUMBER = b'BPRM'
FILE_MAGIC_NUMBER = b'BPRF'
@unique
class RecordingFileVersion(IntEnum):
"""Recording file version table."""
VERSION_00_01 = 0x0000
"""nothing"""
VERSION_00_02 = 0x0001
"""introduce recording file format"""
__slots__ = ()
class RecordingFileDataFormat:
RAW_DATA = b'raw\0'
REC_DATA = b'rec\0'
DATA = (RAW_DATA, REC_DATA)
__slots__ = ()
class RecordingMetaFileLoader(metaclass=abc.ABCMeta):
__slots__ = ('header', 'version', 'create_time', 'last_time', 'file_uuid', 'data_format',
'device_response', 'configuration', 'channel_mask', 'recording_file_ch')
def __init__(self):
# fields
self.header = None
self.version = None
self.create_time = None
self.last_time = None
self.file_uuid = None
self.data_format = None
self.device_response = None
self.configuration = None
self.channel_mask = None
# self.recording_file = []
self.recording_file_ch = []
# self.recording_file_start_time = []
@classmethod
def load(cls, filepath: Path) -> 'RecordingMetaFileLoader':
"""load meta file
:param filepath: file path
:return: meta file
:raises IOError: not a meta file header, or unsupported version
:raises FileNotFoundError:
"""
try:
with filepath.open('rb') as _f:
f = FileDecoder(_f)
# magic number
header = f.read(4)
if header != META_MAGIC_NUMBER:
raise IOError('not a BioPro Recording File : ' + str(filepath))
# version number
try:
version = f.u32()
version = RecordingFileVersion(version)
except ValueError as e:
raise IOError('unsupported BioPro Recording File version : ' + str(version)) from e
# get loader
loader = cls.loader(version)
# init fields
loader.header = header
loader.version = version
# load left content
loader.load_content(f)
return loader
except IOError as e:
raise IOError('load meta file : ' + ' '.join(e.args)) from e
@classmethod
def loader(cls, version: RecordingFileVersion) -> 'RecordingMetaFileLoader':
if version == RecordingFileVersion.VERSION_00_02:
return DefaultRecordingMetaFileLoader()
else:
raise RuntimeError('illegal recording meta file version : ' + str(version))
@abc.abstractmethod
def load_content(self, f: FileDecoder):
pass
# noinspection PyMethodMayBeStatic
class DefaultRecordingMetaFileLoader(RecordingMetaFileLoader):
__slots__ = ()
def __init__(self):
super().__init__()
def load_content(self, f: FileDecoder):
"""load meta file content
:param f: file stream
:return: device configurations
:raises IOError:
"""
# create time
self.create_time = f.u64()
# last time
self.last_time = f.u64()
# file uuid
self.file_uuid = uuid.UUID(bytes=f.read(16))
# data format
data_format = f.read(4)
if data_format not in RecordingFileDataFormat.DATA:
raise IndexError('unknown data format : ' + repr(data_format))
else:
self.data_format = data_format
# recording device
self.device_response = self.load_device_response(f)
# recording parameter
self.configuration = self.load_configuration(f)
# channel information
self.channel_mask = self.load_channel(f)
# recording file list
# self.recording_file.clear()
self.recording_file_ch.clear()
self.load_recording_files(f)
def load_device_response(self, f: FileDecoder) -> str:
device = f.decode_string()
# address = f.decode_string()
# serial = f.u8a(6)
return device
def load_configuration(self, f: FileDecoder) -> DeviceConfiguration:
"""load configuration
:param f: file stream
:return: device configuration
:raises IOError:
"""
try:
return DeviceConfigurationDecoder.decode(f)
except (IOError, UnicodeDecodeError, EOFError, ValueError) as e:
raise IOError('load configuration : ' + str(e)) from e
def load_channel(self, f: FileDecoder) -> ChannelMask:
ret = ChannelMask()
size = f.u8()
ret.add_channel(f.u8a(size))
return ret
def load_recording_files(self, f: FileDecoder):
# while True:
# data = self.load_recording_file_data(f)
# if data is None or data == 'channel_level':
# break
# else:
# self.recording_file.append(data)
while True:
data = self.load_recording_file_data(f)
if data is None:
break
else:
self.recording_file_ch.append(data)
# while True:
# data = self.load_recording_file_data(f)
# if data is None:
# break
# else:
# self.recording_file_start_time.append(data)
def load_recording_file_data(self, f: FileDecoder) -> Optional[str]:
name = f.decode_string()
if len(name) == 0:
return None
else:
return name