diff --git a/mio/data/bitfile/USBInterface-10mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-10mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-10mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-10mhz-J2_2-3v3-IEEE.bit diff --git a/mio/data/bitfile/USBInterface-12_5mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-12_5mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-12_5mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-12_5mhz-J2_2-3v3-IEEE.bit diff --git a/mio/data/bitfile/USBInterface-14_3mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-14_3mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-14_3mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-14_3mhz-J2_2-3v3-IEEE.bit diff --git a/mio/data/bitfile/USBInterface-3_03mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-3_03mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-3_03mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-3_03mhz-J2_2-3v3-IEEE.bit diff --git a/mio/data/bitfile/USBInterface-5mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-5mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-5mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-5mhz-J2_2-3v3-IEEE.bit diff --git a/mio/data/bitfile/USBInterface-6_67mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-6_67mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-6_67mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-6_67mhz-J2_2-3v3-IEEE.bit diff --git a/mio/data/bitfile/USBInterface-8_33mhz-J2_2-3v3-IEEE.bit b/mio/data/bitfile/XEM7310-A75/USBInterface-8_33mhz-J2_2-3v3-IEEE.bit similarity index 100% rename from mio/data/bitfile/USBInterface-8_33mhz-J2_2-3v3-IEEE.bit rename to mio/data/bitfile/XEM7310-A75/USBInterface-8_33mhz-J2_2-3v3-IEEE.bit diff --git a/mio/devices/camera.py b/mio/devices/camera.py index 219d2ac2..bccbe348 100644 --- a/mio/devices/camera.py +++ b/mio/devices/camera.py @@ -68,20 +68,20 @@ def fps(self, value: Union[int, float]) -> None: """ @property - @abstractmethod def exposure(self) -> int: """ Returns: int: """ + raise NotImplementedError("exposure getter is not implemented") @exposure.setter - @abstractmethod def exposure(self, value: int) -> None: """ Args: value (int): Value to set """ + raise NotImplementedError("exposure setter is not implemented") @property @abstractmethod diff --git a/mio/devices/device.py b/mio/devices/device.py index 10f73147..34324259 100644 --- a/mio/devices/device.py +++ b/mio/devices/device.py @@ -3,9 +3,10 @@ """ from abc import abstractmethod -from typing import TYPE_CHECKING, Any, Union +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Optional, Union -from mio.models import MiniscopeConfig, MiniscopeIOModel, Pipeline, PipelineConfig +from mio.models import MiniscopeConfig, Pipeline, PipelineConfig if TYPE_CHECKING: from mio.models.pipeline import Sink, Source, Transform @@ -21,15 +22,16 @@ class DeviceConfig(MiniscopeConfig): pipeline: PipelineConfig = PipelineConfig() -class Device(MiniscopeIOModel): +@dataclass(kw_only=True) +class Device: """ Abstract base class for devices. Currently a placeholder to allow room for expansion/renaming in the future """ - pipeline: Pipeline - config: DeviceConfig + pipeline: Optional[Pipeline] = None + # config: Optional[DeviceConfig] = None @abstractmethod def init(self) -> None: diff --git a/mio/devices/wirefree.py b/mio/devices/wirefree.py index 0b867c5f..751cc638 100644 --- a/mio/devices/wirefree.py +++ b/mio/devices/wirefree.py @@ -3,8 +3,9 @@ """ import contextlib +from dataclasses import dataclass, field from pathlib import Path -from typing import BinaryIO, Literal, Optional, Union, overload +from typing import Any, BinaryIO, Literal, Optional, Union, overload import cv2 import numpy as np @@ -15,6 +16,7 @@ from mio.exceptions import EndOfRecordingException, ReadHeaderException from mio.models.data import Frame from mio.models.sdcard import SDBufferHeader, SDConfig, SDLayout +from mio.types import ConfigSource, Resolution class WireFreeConfig(DeviceConfig): @@ -22,7 +24,7 @@ class WireFreeConfig(DeviceConfig): pass - +@dataclass(kw_only=True) class WireFreeMiniscope(Miniscope, RecordingCameraMixin): """ I/O for data on an SD Card recorded with a WireFree Miniscope @@ -39,13 +41,22 @@ class WireFreeMiniscope(Miniscope, RecordingCameraMixin): drive: Path """The path to the SD card drive""" - config: WireFreeConfig - """Configuration """ - # layout: + # config: WireFreeConfig + # """Configuration """ + layout: Union[SDLayout, ConfigSource] = "wirefree-sd-layout" + positions: dict[int, int] = field(default_factory=dict) + """ + A mapping between frame number and byte position in the video that makes for + faster seeking :) + + As we read, we store the locations of each frame before reading it. Later, + we can assign to `frame` to seek back to those positions. Assigning to `frame` + works without caching position, but has to manually iterate through each frame. + """ - def __post_init__(self, drive: Union[str, Path], layout: SDLayout): - self.drive = drive - self.layout = layout + def __post_init__(self) -> None: + """post-init create private vars""" + self.layout = SDLayout.from_any(self.layout) self.logger = init_logger("WireFreeMiniscope") # Private attributes used when the file reading context is entered @@ -57,15 +68,7 @@ def __post_init__(self, drive: Union[str, Path], layout: SDLayout): """ n_pix x 1 array used to store pixels while reading buffers """ - self.positions = {} - """ - A mapping between frame number and byte position in the video that makes for - faster seeking :) - - As we read, we store the locations of each frame before reading it. Later, - we can assign to `frame` to seek back to those positions. Assigning to `frame` - works without caching position, but has to manually iterate through each frame. - """ + # -------------------------------------------------- # Properties @@ -91,6 +94,13 @@ def config(self) -> SDConfig: return self._config + @classmethod + def configure(cls, drive: Union[str, Path], config: WireFreeConfig) -> None: + """ + Configure a WireFreeMiniscope SD card for recording + """ + raise NotImplementedError() + @property def position(self) -> Optional[int]: """ @@ -606,3 +616,50 @@ def check_valid(self) -> bool: valid = True return valid + + # -------------------------------------------------- + # ABC methods + # -------------------------------------------------- + + def init(self) -> None: + """Do nothing""" + pass + + def deinit(self) -> None: + """Do nothing""" + pass + + def start(self) -> None: + """start pipeline""" + raise NotImplementedError() + + def stop(self) -> None: + """stop pipeline""" + raise NotImplementedError() + + def join(self) -> None: + """join pipeline""" + raise NotImplementedError() + + @property + def excitation(self) -> float: + """LED Excitation""" + raise NotImplementedError() + + @property + def fps(self) -> int: + """FPS""" + return self.config.fs + + @property + def resolution(self) -> Resolution: + """Resolution of recorded video""" + return Resolution(self.config.width, self.config.height) + + def get(self, key:str) -> Any: + """get a configuration value by its name""" + return getattr(self.config, key) + + def set(self, key:str, value:Any) -> None: + """set a configuration value""" + raise NotImplementedError() \ No newline at end of file diff --git a/pdm.lock b/pdm.lock index 010af761..3e7964ca 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "all", "dev", "docs", "plot", "tests"] strategy = ["inherit_metadata"] lock_version = "4.5.0" -content_hash = "sha256:45c1d1f2b3d9add57b72934ccc1ee0e8798c25bd2e094d76b91f9bc2fb61f3e1" +content_hash = "sha256:3cad0b2c79342e3968cf3e7d8ec49f1e168aa1c10201231286d5dfc0211c9c05" [[metadata.targets]] requires_python = "~=3.9" @@ -1898,6 +1898,21 @@ files = [ {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, ] +[[package]] +name = "sphinxcontrib-mermaid" +version = "1.0.0" +requires_python = ">=3.8" +summary = "Mermaid diagrams in yours Sphinx powered docs" +groups = ["all", "docs"] +dependencies = [ + "pyyaml", + "sphinx", +] +files = [ + {file = "sphinxcontrib_mermaid-1.0.0-py3-none-any.whl", hash = "sha256:60b72710ea02087f212028feb09711225fbc2e343a10d34822fe787510e1caa3"}, + {file = "sphinxcontrib_mermaid-1.0.0.tar.gz", hash = "sha256:2e8ab67d3e1e2816663f9347d026a8dee4a858acdd4ad32dd1c808893db88146"}, +] + [[package]] name = "sphinxcontrib-programoutput" version = "0.18" diff --git a/tests/test_devices/test_wirefree.py b/tests/test_devices/test_wirefree.py index 1b5b3781..73074cd8 100644 --- a/tests/test_devices/test_wirefree.py +++ b/tests/test_devices/test_wirefree.py @@ -7,7 +7,6 @@ from mio.devices import WireFreeMiniscope from mio.exceptions import EndOfRecordingException -from mio.formats import WireFreeSDLayout, WireFreeSDLayout_Battery from mio.models.data import Frame from mio.models.sdcard import SDBufferHeader from mio.utils import hash_video, hash_file @@ -98,7 +97,7 @@ def test_relative_path(): rel_path = abs_child.relative_to(abs_cwd) assert not rel_path.is_absolute() - sdcard = WireFreeMiniscope(drive=rel_path, layout=WireFreeSDLayout) + sdcard = WireFreeMiniscope(drive=rel_path, layout="wirefree-sd-layout") # check we can do something basic like read config assert sdcard.config is not None @@ -109,7 +108,7 @@ def test_relative_path(): # now try with an absolute path abs_path = rel_path.resolve() assert abs_path.is_absolute() - sdcard_abs = WireFreeMiniscope(drive=abs_path, layout=WireFreeSDLayout) + sdcard_abs = WireFreeMiniscope(drive=abs_path, layout="wirefree-sd-layout") assert sdcard_abs.config is not None assert sdcard_abs.drive.is_absolute() @@ -141,7 +140,7 @@ def test_to_img(wirefree_battery, n_frames, hash, tmp_path): assert out_hash == hash - sd = WireFreeMiniscope(out_file, WireFreeSDLayout_Battery) + sd = WireFreeMiniscope(drive=out_file, layout="wirefree-sd-layout") # we should be able to read all the frames! frames = []