<commit_before>import enum
import logging
import pickle
from typing import List, Tuple, Any, Optional, Iterable

import numpy as np
from PyQt5 import QtCore, QtGui

from .beamstop import BeamStop
from .component import Component
from .expose import Exposer
from .samples import SampleStore
from ...dataclasses import Sample, Exposure
from ...devices.xraysource.genix.frontend import GeniX, GeniXBackend
from ...algorithms.orderforleastmotormovement import orderForLeastMotorMovement

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class TransmissionData:
    samplename: str
    darkcounts: List[float]
    emptycounts: List[float]
    samplecounts: List[float]

    def __init__(self, samplename: str):
        self.samplename = samplename
        self.darkcounts = []
        self.emptycounts = []
        self.samplecounts = []

    def addDark(self, value: float):
        self.darkcounts.append(value)

    def addEmpty(self, value: float):
        self.emptycounts.append(value)

    def addSample(self, value: float):
        self.samplecounts.append(value)

    def transmission(self) -> Optional[Tuple[float, float]]:
        empty = self.empty()
        dark = self.dark()
        sample = self.sample()
        if (empty is None) or (dark is None) or (sample is None):
            return None
        empty = (empty[0] - dark[0]), (empty[1] ** 2 + dark[1] ** 2) ** 0.5
        sample = (sample[0] - dark[0]), (sample[1] ** 2 + dark[1] ** 2) ** 0.5
        return sample[0] / empty[0], (
                sample[1] ** 2 / empty[0] ** 2 + empty[1] ** 2 * sample[0] ** 2 / empty[0] ** 4) ** 0.5

    def dark(self) -> Optional[Tuple[float, float]]:
        if not self.darkcounts:
            return None
        return np.mean(self.darkcounts), np.std(self.darkcounts)

    def empty(self) -> Optional[Tuple[float, float]]:
        if not self.emptycounts:
            return None
        return np.mean(self.emptycounts), np.std(self.emptycounts)

    def sample(self) -> Optional[Tuple[float, float]]:
        if not self.samplecounts:
            return None
        return np.mean(self.samplecounts), np.std(self.samplecounts)

    def clear(self):
        self.samplecounts = []
        self.darkcounts = []
        self.emptycounts = []


class TransmissionMeasurementStatus(enum.Enum):
    Idle = 'Idle'
    XraysToStandby = 'X-ray generator to standby mode'
    BeamstopOut = 'Moving beam-stop out'
    BeamstopIn = 'Moving beam-stop in'
    OpenShutter = 'Opening shutter'
    CloseShutter = 'Closing shutter'
    ExposingEmpty = 'Exposing empty beam'
    ExposingDark = 'Exposing dark'
    ExposingSample = 'Exposing sample'
    MovingToEmpty = 'Moving to empty beam position'
    MovingToSample = 'Moving to sample position'
    Stopping = 'Stop requested by the user'


class TransmissionMeasurement(QtCore.QAbstractItemModel, Component):
    """Transmission measurement sequence:

    1. Initialization
        1.1. Close beam shutter
        1.2. X-ray source to low-power / standby mode
        1.3. Beamstop out
    2. Exposures
        2.1. Ensure that the beam shutter is closed
        2.2. Expose dark image
        2.3. Move to the empty-beam position
        2.4. Open beam shutter
        2.5. Expose
        2.6. Close beam shutter
        2.7. Move to sample
        2.8. Open beam shutter
        2.9. Expose
        2.10. Close beam shutter
        2.11. Return to 2.1 for the next sample
    3. Finalization
        3.1 Ensure that the beam shutter is closed
        3.2 Move beam-stop in
        3.3. Wait for images to be received

    If an error happens
    """

    lazymode: bool
    _data: List[TransmissionData]
    status: TransmissionMeasurementStatus = TransmissionMeasurementStatus.Idle
    currentlymeasuredsample: Optional[int] = None
    emptysample: str
    countingtime: float
    delaytime: float
    nimages: int
    waitingforimages: Optional[int] = None
    progress = QtCore.pyqtSignal(float, float, float, str)  # minimum, maximum, current, message
    sampleStarted = QtCore.pyqtSignal(str, int, int)  # sample name, index, number of samples
    finished = QtCore.pyqtSignal(bool, str)
    started = QtCore.pyqtSignal()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._data = []

    def setStatus(self, status: TransmissionMeasurementStatus):
        logger.debug(f'Transmission status: {status}')
        self.status = status

    def startComponent(self):
        self.instrument.samplestore.sampleListChanged.connect(self.onSampleListChanged)

    def onSampleListChanged(self):
        # see if some samples vanished
        vanishedsamples = [data.samplename for data in self._data if data.samplename not in self.instrument.samplestore]
        for sample in vanishedsamples:
            self.removeSample(sample)  # ToDo: what happens if we are currently measuring?
        # update mu and 1/mu if thicknesses changed.
        self.beginResetModel()
        self.endResetModel()

    # QAbstractItemModel reimplemented methods

    def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return len(self._data)

    def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return 7

    def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = ...) -> Any:
        if (orientation == QtCore.Qt.Horizontal) and (role == QtCore.Qt.DisplayRole):
            return ['Sample name', 'Dark', 'Empty', 'Sample', 'Transmission', 'Mu (1/cm)', 'Absorption length (cm)'][
                section]

    def data(self, index: QtCore.QModelIndex, role: int = ...) -> Any:
        data = self._data[index.row()]
        if (index.column() == 0) and (role == QtCore.Qt.DisplayRole):
            return data.samplename
        elif (index.column() == 0) and (role == QtCore.Qt.DecorationRole):
            return QtGui.QIcon.fromTheme(
                'media-playback-start') if index.row() == self.currentlymeasuredsample else None
        elif role == QtCore.Qt.BackgroundColorRole:
            return QtGui.QColor(QtCore.Qt.green) if index.row() == self.currentlymeasuredsample else None
        elif (index.column() == 1) and (role == QtCore.Qt.DisplayRole):
            value = data.dark()
            return '--' if value is None else f'{value[0]} \xb1 {value[1]}'
        elif (index.column() == 2) and (role == QtCore.Qt.DisplayRole):
            value = data.empty()
            return '--' if value is None else f'{value[0]} \xb1 {value[1]}'
        elif (index.column() == 3) and (role == QtCore.Qt.DisplayRole):
            value = data.sample()
            return '--' if value is None else f'{value[0]} \xb1 {value[1]}'
        elif (index.column() == 4) and (role == QtCore.Qt.DisplayRole):
            value = data.transmission()
            return '--' if value is None else f'{value[0]:.4f} \xb1 {value[1]:.4f}'
        elif (index.column() == 5) and (role == QtCore.Qt.DisplayRole):
            transm = data.transmission()
            if transm is None:
                return '--'
            mud = -np.log(transm[0]), np.abs(transm[1] / transm[0])
            sample = self.instrument.samplestore[data.samplename]
            assert isinstance(sample, Sample)
            mu = mud[0] / sample.thickness[0], (
                    mud[1] ** 2 / sample.thickness[0] ** 2 + mud[0] ** 2 * sample.thickness[1] ** 2 /
                    sample.thickness[0] ** 4) ** 0.5
            return f'{mu[0]:.4f} \xb1 {mu[1]:.4f}'
        elif (index.column() == 6) and (role == QtCore.Qt.DisplayRole):
            transm = data.transmission()<commit_msg>Count rates are shown with less decimals in transmission<commit_after>import enum
import logging
import pickle
from typing import List, Tuple, Any, Optional, Iterable

import numpy as np
from PyQt5 import QtCore, QtGui

from .beamstop import BeamStop
from .component import Component
from .expose import Exposer
from .samples import SampleStore
from ...dataclasses import Sample, Exposure
from ...devices.xraysource.genix.frontend import GeniX, GeniXBackend
from ...algorithms.orderforleastmotormovement import orderForLeastMotorMovement

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class TransmissionData:
    samplename: str
    darkcounts: List[float]
    emptycounts: List[float]
    samplecounts: List[float]

    def __init__(self, samplename: str):
        self.samplename = samplename
        self.darkcounts = []
        self.emptycounts = []
        self.samplecounts = []

    def addDark(self, value: float):
        self.darkcounts.append(value)

    def addEmpty(self, value: float):
        self.emptycounts.append(value)

    def addSample(self, value: float):
        self.samplecounts.append(value)

    def transmission(self) -> Optional[Tuple[float, float]]:
        empty = self.empty()
        dark = self.dark()
        sample = self.sample()
        if (empty is None) or (dark is None) or (sample is None):
            return None
        empty = (empty[0] - dark[0]), (empty[1] ** 2 + dark[1] ** 2) ** 0.5
        sample = (sample[0] - dark[0]), (sample[1] ** 2 + dark[1] ** 2) ** 0.5
        return sample[0] / empty[0], (
                sample[1] ** 2 / empty[0] ** 2 + empty[1] ** 2 * sample[0] ** 2 / empty[0] ** 4) ** 0.5

    def dark(self) -> Optional[Tuple[float, float]]:
        if not self.darkcounts:
            return None
        return np.mean(self.darkcounts), np.std(self.darkcounts)

    def empty(self) -> Optional[Tuple[float, float]]:
        if not self.emptycounts:
            return None
        return np.mean(self.emptycounts), np.std(self.emptycounts)

    def sample(self) -> Optional[Tuple[float, float]]:
        if not self.samplecounts:
            return None
        return np.mean(self.samplecounts), np.std(self.samplecounts)

    def clear(self):
        self.samplecounts = []
        self.darkcounts = []
        self.emptycounts = []


class TransmissionMeasurementStatus(enum.Enum):
    Idle = 'Idle'
    XraysToStandby = 'X-ray generator to standby mode'
    BeamstopOut = 'Moving beam-stop out'
    BeamstopIn = 'Moving beam-stop in'
    OpenShutter = 'Opening shutter'
    CloseShutter = 'Closing shutter'
    ExposingEmpty = 'Exposing empty beam'
    ExposingDark = 'Exposing dark'
    ExposingSample = 'Exposing sample'
    MovingToEmpty = 'Moving to empty beam position'
    MovingToSample = 'Moving to sample position'
    Stopping = 'Stop requested by the user'


class TransmissionMeasurement(QtCore.QAbstractItemModel, Component):
    """Transmission measurement sequence:

    1. Initialization
        1.1. Close beam shutter
        1.2. X-ray source to low-power / standby mode
        1.3. Beamstop out
    2. Exposures
        2.1. Ensure that the beam shutter is closed
        2.2. Expose dark image
        2.3. Move to the empty-beam position
        2.4. Open beam shutter
        2.5. Expose
        2.6. Close beam shutter
        2.7. Move to sample
        2.8. Open beam shutter
        2.9. Expose
        2.10. Close beam shutter
        2.11. Return to 2.1 for the next sample
    3. Finalization
        3.1 Ensure that the beam shutter is closed
        3.2 Move beam-stop in
        3.3. Wait for images to be received

    If an error happens
    """

    lazymode: bool
    _data: List[TransmissionData]
    status: TransmissionMeasurementStatus = TransmissionMeasurementStatus.Idle
    currentlymeasuredsample: Optional[int] = None
    emptysample: str
    countingtime: float
    delaytime: float
    nimages: int
    waitingforimages: Optional[int] = None
    progress = QtCore.pyqtSignal(float, float, float, str)  # minimum, maximum, current, message
    sampleStarted = QtCore.pyqtSignal(str, int, int)  # sample name, index, number of samples
    finished = QtCore.pyqtSignal(bool, str)
    started = QtCore.pyqtSignal()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._data = []

    def setStatus(self, status: TransmissionMeasurementStatus):
        logger.debug(f'Transmission status: {status}')
        self.status = status

    def startComponent(self):
        self.instrument.samplestore.sampleListChanged.connect(self.onSampleListChanged)

    def onSampleListChanged(self):
        # see if some samples vanished
        vanishedsamples = [data.samplename for data in self._data if data.samplename not in self.instrument.samplestore]
        for sample in vanishedsamples:
            self.removeSample(sample)  # ToDo: what happens if we are currently measuring?
        # update mu and 1/mu if thicknesses changed.
        self.beginResetModel()
        self.endResetModel()

    # QAbstractItemModel reimplemented methods

    def rowCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return len(self._data)

    def columnCount(self, parent: QtCore.QModelIndex = ...) -> int:
        return 7

    def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = ...) -> Any:
        if (orientation == QtCore.Qt.Horizontal) and (role == QtCore.Qt.DisplayRole):
            return ['Sample name', 'Dark', 'Empty', 'Sample', 'Transmission', 'Mu (1/cm)', 'Absorption length (cm)'][
                section]

    def data(self, index: QtCore.QModelIndex, role: int = ...) -> Any:
        data = self._data[index.row()]
        if (index.column() == 0) and (role == QtCore.Qt.DisplayRole):
            return data.samplename
        elif (index.column() == 0) and (role == QtCore.Qt.DecorationRole):
            return QtGui.QIcon.fromTheme(
                'media-playback-start') if index.row() == self.currentlymeasuredsample else None
        elif role == QtCore.Qt.BackgroundColorRole:
            return QtGui.QColor(QtCore.Qt.green) if index.row() == self.currentlymeasuredsample else None
        elif (index.column() == 1) and (role == QtCore.Qt.DisplayRole):
            value = data.dark()
            return '--' if value is None else f'{value[0]:.1f} \xb1 {value[1]:.1f}'
        elif (index.column() == 2) and (role == QtCore.Qt.DisplayRole):
            value = data.empty()
            return '--' if value is None else f'{value[0]:.1f} \xb1 {value[1]:.1f}'
        elif (index.column() == 3) and (role == QtCore.Qt.DisplayRole):
            value = data.sample()
            return '--' if value is None else f'{value[0]:.1f} \xb1 {value[1]:.1f}'
        elif (index.column() == 4) and (role == QtCore.Qt.DisplayRole):
            value = data.transmission()
            return '--' if value is None else f'{value[0]:.4f} \xb1 {value[1]:.4f}'
        elif (index.column() == 5) and (role == QtCore.Qt.DisplayRole):
            transm = data.transmission()
            if transm is None:
                return '--'
            mud = -np.log(transm[0]), np.abs(transm[1] / transm[0])
            sample = self.instrument.samplestore[data.samplename]
            assert isinstance(sample, Sample)
            mu = mud[0] / sample.thickness[0], (
                    mud[1] ** 2 / sample.thickness[0] ** 2 + mud[0] ** 2 * sample.thickness[1] ** 2 /
                    sample.thickness[0] ** 4) ** 0.5
            return f'{mu[0]:.4f} \xb1 {mu[1]:.4f}'
        elif (index.column() == 6) and (role == QtCore.Qt.DisplayRole):
            transm = data.transmission()
{'fraction_non_alphanumeric': 0.12069753771798054, 'fraction_numerical': 0.01593625498007968, 'mean_word_length': 2.5062972292191437, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2486651', 'n_tokens_mistral': 4802, 'n_tokens_neox': 4606, 'n_words': 1285}