Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions autogen-config.json

This file was deleted.

311 changes: 311 additions & 0 deletions ev3dev/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,311 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2015 Ralph Hempel <rhempel@hempeldesigngroup.com>
# Copyright (c) 2015 Anton Vanhoucke <antonvh@gmail.com>
# Copyright (c) 2015 Denis Demidov <dennis.demidov@gmail.com>
# Copyright (c) 2015 Eric Pascual <eric@pobot.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# -----------------------------------------------------------------------------

import sys

if sys.version_info < (3,4):
raise SystemError('Must be using Python 3.4 or higher')

import os
import io
import fnmatch
import re
import stat
import errno
from os.path import abspath

INPUT_AUTO = ''
OUTPUT_AUTO = ''


def get_current_platform():
"""
Look in /sys/class/board-info/ to determine the platform type.

This can return 'ev3', 'evb', 'pistorms', 'brickpi' or 'brickpi3'.
"""
board_info_dir = '/sys/class/board-info/'

for board in os.listdir(board_info_dir):
uevent_filename = os.path.join(board_info_dir, board, 'uevent')

if os.path.exists(uevent_filename):
with open(uevent_filename, 'r') as fh:
for line in fh.readlines():
(key, value) = line.strip().split('=')

if key == 'BOARD_INFO_MODEL':

if value == 'LEGO MINDSTORMS EV3':
return 'ev3'

elif value in ('FatcatLab EVB', 'QuestCape'):
return 'evb'

elif value == 'PiStorms':
return 'pistorms'

# This is the same for both BrickPi and BrickPi+.
# There is not a way to tell the difference.
elif value == 'Dexter Industries BrickPi':
return 'brickpi'

elif value == 'Dexter Industries BrickPi3':
return 'brickpi3'
return None


# -----------------------------------------------------------------------------
def list_device_names(class_path, name_pattern, **kwargs):
"""
This is a generator function that lists names of all devices matching the
provided parameters.

Parameters:
class_path: class path of the device, a subdirectory of /sys/class.
For example, '/sys/class/tacho-motor'.
name_pattern: pattern that device name should match.
For example, 'sensor*' or 'motor*'. Default value: '*'.
keyword arguments: used for matching the corresponding device
attributes. For example, address='outA', or
driver_name=['lego-ev3-us', 'lego-nxt-us']. When argument value
is a list, then a match against any entry of the list is
enough.
"""

if not os.path.isdir(class_path):
return

def matches(attribute, pattern):
try:
with io.FileIO(attribute) as f:
value = f.read().strip().decode()
except:
return False

if isinstance(pattern, list):
return any([value.find(p) >= 0 for p in pattern])
else:
return value.find(pattern) >= 0

for f in os.listdir(class_path):
if fnmatch.fnmatch(f, name_pattern):
path = class_path + '/' + f
if all([matches(path + '/' + k, kwargs[k]) for k in kwargs]):
yield f


# -----------------------------------------------------------------------------
# Define the base class from which all other ev3dev classes are defined.

class Device(object):
"""The ev3dev device base class"""

__slots__ = ['_path', 'connected', '_device_index', 'kwargs']

DEVICE_ROOT_PATH = '/sys/class'

_DEVICE_INDEX = re.compile(r'^.*(\d+)$')

def __init__(self, class_name, name_pattern='*', name_exact=False, **kwargs):
"""Spin through the Linux sysfs class for the device type and find
a device that matches the provided name pattern and attributes (if any).

Parameters:
class_name: class name of the device, a subdirectory of /sys/class.
For example, 'tacho-motor'.
name_pattern: pattern that device name should match.
For example, 'sensor*' or 'motor*'. Default value: '*'.
name_exact: when True, assume that the name_pattern provided is the
exact device name and use it directly.
keyword arguments: used for matching the corresponding device
attributes. For example, address='outA', or
driver_name=['lego-ev3-us', 'lego-nxt-us']. When argument value
is a list, then a match against any entry of the list is
enough.

Example::

d = ev3dev.Device('tacho-motor', address='outA')
s = ev3dev.Device('lego-sensor', driver_name=['lego-ev3-us', 'lego-nxt-us'])

When connected succesfully, the `connected` attribute is set to True.
"""

classpath = abspath(Device.DEVICE_ROOT_PATH + '/' + class_name)
self.kwargs = kwargs

def get_index(file):
match = Device._DEVICE_INDEX.match(file)
if match:
return int(match.group(1))
else:
return None

if name_exact:
self._path = classpath + '/' + name_pattern
self._device_index = get_index(name_pattern)
self.connected = True
else:
try:
name = next(list_device_names(classpath, name_pattern, **kwargs))
self._path = classpath + '/' + name
self._device_index = get_index(name)
self.connected = True
except StopIteration:
self._path = None
self._device_index = None
self.connected = False

def __str__(self):
if 'address' in self.kwargs:
return "%s(%s)" % (self.__class__.__name__, self.kwargs.get('address'))
else:
return self.__class__.__name__

def _attribute_file_open(self, name):
path = os.path.join(self._path, name)
mode = stat.S_IMODE(os.stat(path)[stat.ST_MODE])
r_ok = mode & stat.S_IRGRP
w_ok = mode & stat.S_IWGRP

if r_ok and w_ok:
mode = 'r+'
elif w_ok:
mode = 'w'
else:
mode = 'r'

return io.FileIO(path, mode)

def _get_attribute(self, attribute, name):
"""Device attribute getter"""
if self.connected:
if attribute is None:
attribute = self._attribute_file_open( name )
else:
attribute.seek(0)
return attribute, attribute.read().strip().decode()
else:
#log.info("%s: path %s, attribute %s" % (self, self._path, name))
raise Exception("%s is not connected" % self)

def _set_attribute(self, attribute, name, value):
"""Device attribute setter"""
if self.connected:
try:
if attribute is None:
attribute = self._attribute_file_open( name )
else:
attribute.seek(0)

if isinstance(value, str):
value = value.encode()
attribute.write(value)
attribute.flush()
except Exception as ex:
self._raise_friendly_access_error(ex, name)
return attribute
else:
#log.info("%s: path %s, attribute %s" % (self, self._path, name))
raise Exception("%s is not connected" % self)

def _raise_friendly_access_error(self, driver_error, attribute):
if not isinstance(driver_error, OSError):
raise driver_error

if driver_error.errno == errno.EINVAL:
if attribute == "speed_sp":
try:
max_speed = self.max_speed
except (AttributeError, Exception):
raise ValueError("The given speed value was out of range") from driver_error
else:
raise ValueError("The given speed value was out of range. Max speed: +/-" + str(max_speed)) from driver_error
raise ValueError("One or more arguments were out of range or invalid") from driver_error
elif driver_error.errno == errno.ENODEV or driver_error.errno == errno.ENOENT:
# We will assume that a file-not-found error is the result of a disconnected device
# rather than a library error. If that isn't the case, at a minimum the underlying
# error info will be printed for debugging.
raise Exception("%s is no longer connected" % self) from driver_error
raise driver_error

def get_attr_int(self, attribute, name):
attribute, value = self._get_attribute(attribute, name)
return attribute, int(value)

def set_attr_int(self, attribute, name, value):
return self._set_attribute(attribute, name, str(int(value)))

def set_attr_raw(self, attribute, name, value):
return self._set_attribute(attribute, name, value)

def get_attr_string(self, attribute, name):
return self._get_attribute(attribute, name)

def set_attr_string(self, attribute, name, value):
return self._set_attribute(attribute, name, value)

def get_attr_line(self, attribute, name):
return self._get_attribute(attribute, name)

def get_attr_set(self, attribute, name):
attribute, value = self.get_attr_line(attribute, name)
return attribute, [v.strip('[]') for v in value.split()]

def get_attr_from_set(self, attribute, name):
attribute, value = self.get_attr_line(attribute, name)
for a in value.split():
v = a.strip('[]')
if v != a:
return v
return ""

@property
def device_index(self):
return self._device_index


def list_devices(class_name, name_pattern, **kwargs):
"""
This is a generator function that takes same arguments as `Device` class
and enumerates all devices present in the system that match the provided
arguments.

Parameters:
class_name: class name of the device, a subdirectory of /sys/class.
For example, 'tacho-motor'.
name_pattern: pattern that device name should match.
For example, 'sensor*' or 'motor*'. Default value: '*'.
keyword arguments: used for matching the corresponding device
attributes. For example, address='outA', or
driver_name=['lego-ev3-us', 'lego-nxt-us']. When argument value
is a list, then a match against any entry of the list is
enough.
"""
classpath = abspath(Device.DEVICE_ROOT_PATH + '/' + class_name)

return (Device(class_name, name, name_exact=True)
for name in list_device_names(classpath, name_pattern, **kwargs))
Empty file added ev3dev/_platform/__init__.py
Empty file.
59 changes: 12 additions & 47 deletions ev3dev/brickpi.py → ev3dev/_platform/brickpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@
An assortment of classes modeling specific features of the BrickPi.
"""

from .core import *

from collections import OrderedDict

OUTPUT_A = 'ttyAMA0:MA'
OUTPUT_B = 'ttyAMA0:MB'
Expand All @@ -39,51 +38,17 @@
INPUT_3 = 'ttyAMA0:S3'
INPUT_4 = 'ttyAMA0:S4'

BUTTONS_FILENAME = None
EVDEV_DEVICE_NAME = None

class Leds(object):
"""
The BrickPi LEDs.
"""
blue_led1 = Led(name_pattern='brickpi:led1:blue:ev3dev')
blue_led2 = Led(name_pattern='brickpi:led2:blue:ev3dev')

LED1 = ( blue_led1, )
LED2 = ( blue_led2, )

BLACK = ( 0, )
BLUE = ( 1, )

@staticmethod
def set_color(group, color, pct=1):
"""
Sets brigthness of leds in the given group to the values specified in
color tuple. When percentage is specified, brightness of each led is
reduced proportionally.

Example::

Leds.set_color(LEFT, AMBER)
"""
for l, v in zip(group, color):
l.brightness_pct = v * pct

@staticmethod
def set(group, **kwargs):
"""
Set attributes for each led in group.

Example::
LEDS = OrderedDict()
LEDS['blue_led1'] = 'brickpi:led1:blue:ev3dev'
LEDS['blue_led2'] = 'brickpi:led2:blue:ev3dev'

Leds.set(LEFT, brightness_pct=0.5, trigger='timer')
"""
for led in group:
for k in kwargs:
setattr(led, k, kwargs[k])
LED_GROUPS = OrderedDict()
LED_GROUPS['LED1'] = ('blue_led1',)
LED_GROUPS['LED2'] = ('blue_led2',)

@staticmethod
def all_off():
"""
Turn all leds off
"""
Leds.blue_led1.brightness = 0
Leds.blue_led2.brightness = 0
LED_COLORS = OrderedDict()
LED_COLORS['BLACK'] = (0,)
LED_COLORS['BLUE'] = (1,)
3 changes: 3 additions & 0 deletions ev3dev/_platform/brickpi3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

BUTTONS_FILENAME = None
EVDEV_DEVICE_NAME = None
Loading