Skip to content

Commit

Permalink
Squashed commit of the following:
Browse files Browse the repository at this point in the history
commit c2b3dc5
Author: Ian Dobbie <[email protected]>
Date:   Tue Aug 22 12:21:54 2023 -0400

    Added the required getValues function to the raspberrypi valueLogger module.

commit fa592d6
Author: David Miguel Susano Pinto <[email protected]>
Date:   Tue Aug 22 13:52:42 2023 +0100

    RPiValueLogger: check for availability of MCP9808 and TSYS01 classes.

commit bcd3d86
Author: David Miguel Susano Pinto <[email protected]>
Date:   Tue Aug 22 13:44:11 2023 +0100

    PiCamera: use ROI field names instead of tuple index for readability

commit 8f631d7
Author: David Miguel Susano Pinto <[email protected]>
Date:   Tue Aug 22 13:42:07 2023 +0100

    maint: black and isort for consistent style.

commit 03e6b5c
Author: David Miguel Susano Pinto <[email protected]>
Date:   Tue Aug 22 12:42:09 2023 +0100

    Use a standard search path for DLL loading in windows (python-microscope#235)

    Since Python 3.8, ctypes uses an altered search path when loading DLLs
    in Windows which only includes the application directory, the system32
    directory, and directories manually specified with add_dll_directory.
    None of those suit us.  The standard search path can be specified with
    `winmode=0`.

    This commit adds a new function to loads libraries with `winmode=0`
    and makes all modules that load shared libraries use it.

commit 6a4ee44
Author: Ian Dobbie <[email protected]>
Date:   Mon Aug 21 14:01:48 2023 -0400

    Fix picamera roi code to use the ROI named tuples and then just return roi python-microscope#279

commit 76a6364
Author: Ian Dobbie <[email protected]>
Date:   Mon Jul 24 22:09:11 2023 -0400

    Added pulldata option to allow cockpit to pull data from valuelogger devices
  • Loading branch information
thomasmfish committed Aug 30, 2023
1 parent 252c9b1 commit 353fc98
Show file tree
Hide file tree
Showing 17 changed files with 192 additions and 69 deletions.
5 changes: 5 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ Version 0.7.0 (upcoming)

* Microscope is now dependent on Python 3.7 or later.

* Python 3.8 changed the default DLL search path in Windows which
caused all cameras, deformable mirrors, and Linkam stage to fail to
load. This version restores the use of Windows standard search
path.


Version 0.6.0 (2021/01/14)
--------------------------
Expand Down
49 changes: 33 additions & 16 deletions microscope/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,17 @@
## You should have received a copy of the GNU General Public License
## along with Microscope. If not, see <http://www.gnu.org/licenses/>.

import threading
import typing
import ctypes
import os
import sys
import ctypes
import threading
import typing

import serial

import microscope
import microscope.abc

def load_library(windows_file=None, unix_file=None):
kwargs = {}
if windows_file is not None and os.name == "nt":
dll = windows_file
if sys.version_info >= (3, 8):
kwargs["winmode"] = 0
elif unix_file is not None:
dll = unix_file
else:
raise OSError("Unable to laod DLL")

return ctypes.CDLL(dll, **kwargs)


# Both pySerial and serial distribution packages install an import
# package named serial. If both are installed we may have imported
Expand All @@ -59,6 +46,36 @@ def load_library(windows_file=None, unix_file=None):
)


def library_loader(
libname: str, dlltype: typing.Type[ctypes.CDLL] = ctypes.CDLL, **kwargs
) -> ctypes.CDLL:
"""Load shared library.
This exists mainly to search for DLL in Windows using a standard
search path, i.e, search for dlls in ``PATH``.
Args:
libname: file name or path of the library to be loaded as
required by `dlltype`
dlltype: the class of shared library to load. Typically,
``CDLL`` but sometimes ``WinDLL`` in windows.
kwargs: other arguments passed on to ``dlltype``.
"""
# Python 3.8 in Windows uses an altered search path. Their
# reasons is security and I guess it would make sense if we
# installed the DLLs we need ourselves but we don't. `winmode=0`
# restores the use of the standard search path. See issue #235.
if (
os.name == "nt"
and sys.version_info >= (3.8)
and "winmode" not in kwargs
):
winmode_kwargs = {"winmode": 0}
else:
winmode_kwargs = {}
return dlltype(libname, **winmode_kwargs, **kwargs)


class OnlyTriggersOnceOnSoftwareMixin(microscope.abc.TriggerTargetMixin):
"""Utility mixin for devices that only trigger "once" with software.
Expand Down
13 changes: 10 additions & 3 deletions microscope/_wrappers/BMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@
"""

import ctypes
import os
from ctypes import c_char, c_char_p, c_double, c_int, c_uint, c_uint32
from microscope._utils import load_library

# Not actually tested BMC2 yet
SDK = load_library(windows_file="BMC2", unix_file="libBMC.so.3")
import microscope._utils


if os.name == "nt": # is windows
# Not actually tested yet
SDK = microscope._utils.library_loader("BMC2", ctypes.WinDLL)
else:
SDK = microscope._utils.library_loader("libBMC.so.3")


# Definitions from BMCDefs.h
MAX_PATH = 260
Expand Down
12 changes: 10 additions & 2 deletions microscope/_wrappers/asdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@
"""

import ctypes
import os
from ctypes import c_char_p, c_double, c_int, c_size_t, c_uint32
from microscope._utils import load_library

SDK = load_library(windows_file="ASDK", unix_file="libasdk.so")
import microscope._utils


if os.name == "nt": # is windows
_libname = "ASDK"
else:
_libname = "libasdk.so" # Not actually tested yet
SDK = microscope._utils.library_loader(_libname)


class DM(ctypes.Structure):
pass
Expand Down
6 changes: 4 additions & 2 deletions microscope/_wrappers/dcamapi4.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@
import enum
import os

import microscope._utils


if os.name == "nt":
_LIB = ctypes.WinDLL("dcamapi")
_LIB = microscope._utils.library_loader("dcamapi", ctypes.WinDLL)
else:
_LIB = ctypes.CDLL("libdcamapi.so")
_LIB = microscope._utils.library_loader("libdcamapi.so", ctypes.CDLL)


if os.name == "nt":
Expand Down
6 changes: 4 additions & 2 deletions microscope/_wrappers/mirao52e.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@

import ctypes

from microscope._utils import load_library
import microscope._utils


# Vendor only supports Windows
SDK = load_library(windows_file="mirao52e")
SDK = microscope._utils.library_loader("mirao52e", ctypes.WinDLL)


TRUE = 1 # TRUE MroBoolean value
FALSE = 0 # FALSE MroBoolean value
Expand Down
11 changes: 10 additions & 1 deletion microscope/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1696,13 +1696,17 @@ class ValueLogger(DataDevice, metaclass=abc.ABCMeta):
numSensors: total number of measurements.
"""
def __init__(self, numSensors: int, **kwargs) -> None:

def __init__(self, numSensors: int, pullData: bool, **kwargs) -> None:
super().__init__(**kwargs)
if numSensors < 1:
raise ValueError(
"NumSensors must be a positive number (was %d)" % numSensors
)
self._numSensors = numSensors
# If pull data is True data will be pulled from the server if False
# data will be pushed from microsocpe (default)
self.pullData = pullData

@abc.abstractmethod
def initialize(self):
Expand All @@ -1712,3 +1716,8 @@ def initialize(self):
def get_num_sensors(self):
"""Returns the number of sensors lines present in this instance."""
return self._numSensors

@abc.abstractmethod
def getValues(self):
"""Returns values from all sensors"""
raise NotImplementedError()
28 changes: 21 additions & 7 deletions microscope/cameras/_SDK3.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
## along with Microscope. If not, see <http://www.gnu.org/licenses/>.

import ctypes
import os
from ctypes import POINTER, c_double, c_int, c_uint, c_void_p

from microscope._utils import load_library
import microscope._utils


#### typedefs
AT_H = ctypes.c_int
Expand All @@ -31,12 +33,24 @@
AT_U8 = ctypes.c_uint8
AT_WC = ctypes.c_wchar

_stdcall_libraries = {
"ATCORE": load_library(windows_file="atcore", unix_file="atcore.so"),
"ATUTIL": load_library(windows_file="atutility", unix_file="atutility.so")
}

CALLBACKTYPE = ctypes.WINFUNCTYPE(c_int, AT_H, POINTER(AT_WC), c_void_p)
_stdcall_libraries = {}

if os.name == "nt": # is windows
_stdcall_libraries["ATCORE"] = microscope._utils.library_loader(
"atcore", ctypes.WinDLL
)
_stdcall_libraries["ATUTIL"] = microscope._utils.library_loader(
"atutility", ctypes.WinDLL
)
CALLBACKTYPE = ctypes.WINFUNCTYPE(c_int, AT_H, POINTER(AT_WC), c_void_p)
else:
_stdcall_libraries["ATCORE"] = microscope._utils.library_loader(
"atcore.so"
)
_stdcall_libraries["ATUTIL"] = microscope._utils.library_loader(
"atutility.so"
)
CALLBACKTYPE = ctypes.CFUNCTYPE(c_int, AT_H, POINTER(AT_WC), c_void_p)

#### Defines
errorCodes = {}
Expand Down
7 changes: 5 additions & 2 deletions microscope/cameras/atmcd.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@
from numpy.ctypeslib import ndpointer

import microscope
import microscope._utils
import microscope.abc
from microscope._utils import load_library


_logger = logging.getLogger(__name__)
Expand All @@ -88,7 +88,10 @@
_dllName = "atmcd32d"
else:
_dllName = "atmcd64d"
_dll = load_library(windows_file=_dllName, unix_file=_dllName + ".so")
if os.name == "nt": # is windows
_dll = microscope._utils.library_loader(_dllName, ctypes.WinDLL)
else:
_dll = microscope._utils.library_loader(_dllName + ".so")

# Andor's types
at_32 = c_long
Expand Down
16 changes: 11 additions & 5 deletions microscope/cameras/picamera.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ def _get_binning(self):
return Binning(1, 1)

@keep_acquiring
def _set_roi(self, left, top, width, height):
"""Set the ROI to (left, tip, width, height)."""
self.roi = ROI(left, top, width, height)
def _set_roi(self, region):
"""Set the ROI to the name tuple ROI(left, top, width, height)."""
self.roi = region

# set camera LED status, off is best for microscopy.
def setLED(self, state=False):
Expand All @@ -252,7 +252,7 @@ def _get_sensor_shape(self):
self.camera.resolution = (2592, 1944)
# faqll back to defualt if not set above.
res = self.camera.resolution
self._set_roi(0, 0, res[0], res[1])
self._set_roi(ROI(0, 0, res[0], res[1]))
return res

def _do_trigger(self):
Expand All @@ -265,7 +265,13 @@ def soft_trigger(self):
if self._acquiring:
with picamera.array.PiYUVArray(self.camera) as output:
self.camera.capture(output, format="yuv", use_video_port=False)
self._queue.put(output.array[:, :, 0])
self._queue.put(
output.array[
self.roi.top : self.roi.top + self.roi.height,
self.roi.left : self.roi.left + self.roi.width,
0,
]
)


# ongoing implemetation notes
Expand Down
14 changes: 8 additions & 6 deletions microscope/cameras/pvcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,6 @@
import ctypes
import logging
import os
import sys
import platform
import time
import weakref
Expand All @@ -145,8 +144,8 @@
import Pyro4

import microscope
import microscope._utils
import microscope.abc
from microscope._utils import load_library


_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -675,11 +674,14 @@ class md_frame(ctypes.Structure):
("roiCount", uns16),
]

def _load_lib():
_dllsuffix = platform.architecture()[0].rstrip("bit")
return load_library(windows_file="pvcam" + _dllsuffix, unix_file="pvcam.so")

_lib = _load_lib()
if os.name == "nt": # is windows
if platform.architecture()[0] == "32bit":
_lib = microscope._utils.library_loader("pvcam32", ctypes.WinDLL)
else:
_lib = microscope._utils.library_loader("pvcam64", ctypes.WinDLL)
else:
_lib = microscope._utils.library_loader("pvcam.so")


### Functions ###
Expand Down
4 changes: 2 additions & 2 deletions microscope/controllers/ludl.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@ def move_command(self, command: bytes) -> None:
self._command_and_validate(command, b"N")
#
# No Following is not true as Cockpit expects moves to happen
# before the return.
# before the return.
# actully beter to just issue the move command and rely on
# other process to check position
#self.get_command(command)
# self.get_command(command)

def move_by_relative_position(self, axis: bytes, delta: float) -> None:
"""Send a relative movement command to stated axis"""
Expand Down
7 changes: 4 additions & 3 deletions microscope/digitalio/raspberrypi.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@
"""

import contextlib
import logging
import queue
import re
import threading
import time
import typing
import logging
import queue
import microscope.abc

import RPi.GPIO as GPIO

import microscope.abc


# Support for async digital IO control on the Raspberryy Pi.
# Currently supports digital input and output via GPIO lines
Expand Down
Loading

0 comments on commit 353fc98

Please sign in to comment.