#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import glob
from itertools import chain
from psychopy import logging
from .manager import DeviceManager, deviceManager
from .base import BaseDevice, BaseResponse, BaseResponseDevice
try:
from collections.abc import Iterable
except ImportError:
from collections import Iterable
__all__ = [
'forp',
'cedrus',
'minolta',
'gammasci',
'pr',
'crs',
'iolab',
'eyetracker',
'deviceManager',
'listener'
]
def getSerialPorts():
"""Finds the names of all (virtual) serial ports present on the system
Returns
-------
list
Iterable with all the serial ports.
"""
if sys.platform == "darwin":
ports = [
'/dev/tty.USA*', # keyspan twin adapter is usually USA28X13P1.1
'/dev/tty.Key*', # some are Keyspan.1 or Keyserial.1
'/dev/tty.modem*',
'/dev/cu.usbmodem*', # for PR650
'/dev/tty.usbserial*', # for the 'Plugable' converter,
# according to Tushar Chauhan
]
elif sys.platform.startswith("linux"):
ports = [
"/dev/ttyACM?", # USB CDC devices (virtual serial ports)
"/dev/ttyUSB?", # USB to serial adapters using the
# usb-serial kernel module
"/dev/ttyS?", # genuine serial ports usually
# /dev/ttyS0 or /dev/ttyS1
]
elif sys.platform == "cygwin":
# I don't think anyone has actually tried this
# Cygwin maps the windows serial ports like this
ports = ["/dev/ttyS?", ]
elif sys.platform == "win32":
# While PsychoPy does support using numeric values to specify
# which serial port to use, it is better in this case to
# provide a cannoncial name.
return map("COM{0}".format, range(11)) # COM0-10
else:
logging.error("We don't support serial ports on {0} yet!"
.format(sys.platform))
return []
# This creates an iterator for each glob expression. The glob
# expressions are then chained together. This is more efficient
# because it means we don't perform the lookups before we actually
# need to.
return chain.from_iterable(map(glob.iglob, ports))
def getAllPhotometers():
"""Gets all available photometers.
The returned photometers may vary depending on which drivers are installed.
Standalone PsychoPy ships with libraries for all supported photometers.
Returns
-------
list
A list of all photometer classes.
"""
from .photometer import getAllPhotometerClasses
# need values returned as a list for now
return getAllPhotometerClasses()
def getPhotometerByName(name):
"""Gets a Photometer class by name.
You can use either short names like 'pr650' or a long name like 'CRS
ColorCAL'.
Parameters
----------
name : str
The name of the device.
Returns
-------
object
Returns the photometer matching the passed in device name or `None` if
we were unable to find it.
"""
for thisName, photom in getAllPhotometers().items():
# longName is used from the GUI and driverFor is for coders
if name.lower() == thisName:
return photom
[docs]
def findPhotometer(ports=None, device=None):
"""Try to find a connected photometer/photospectrometer!
PsychoPy will sweep a series of serial ports trying to open them.
If a port successfully opens then it will try to issue a command to
the device. If it responds with one of the expected values then it
is assumed to be the appropriate device.
Parameters
----------
ports : list
A list of ports to search. Each port can be a string (e.g. 'COM1',
'/dev/tty.Keyspan1.1') or a number (for win32 comports only). If `None`
is provided then PsychoPy will sweep COM0-10 on Win32 and search known
likely port names on MacOS and Linux.
device : str
String giving expected device (e.g. 'PR650', 'PR655', 'CS100A', 'LS100',
'LS110', 'S470'). If this is not given then an attempt will be made to
find a device of any type, but this often fails.
Returns
-------
object or None
An object representing the first photometer found, `None` if the ports
didn't yield a valid response. `None` if there were not even any valid
ports (suggesting a driver not being installed.)
Examples
--------
Sweeps ports 0 to 10 searching for a PR655::
photom = findPhotometer(device='PR655')
print(photom.getLum())
if hasattr(photom, 'getSpectrum'):
# can retrieve spectrum (e.g. a PR650)
print(photom.getSpectrum())
"""
from .photometer import getAllPhotometerClasses
# try each port
if isinstance(ports, str) or ports is None:
ports = [ports]
for port in ports:
# get all available devices
for cls, profiles in DeviceManager.getAvailableDevices(
list(getAllPhotometerClasses().values())
).items():
# iterate through each
for profile in profiles:
# if port matches, or device matches and no port given, initialise from this profile
if (
port is None and device == cls.__name__
) or (
port and "port" in profile and profile['port'] in port
):
return DeviceManager.addDevice(**profile)
if __name__ == "__main__":
pass