diff --git a/.black.cfg b/.black.cfg new file mode 100644 index 0000000..80976db --- /dev/null +++ b/.black.cfg @@ -0,0 +1,21 @@ +[tool.black] +line-length = 120 +target_version = ['py36'] +include = '\.pyi?$' +exclude = ''' + +( + /( + \.eggs # exclude a few common directories in the + | \.git # root of the project + | \.hg + | \.mypy_cache + | \.tox + | _build + | buck-out + | build + | dist + | venv + )/ +) +''' diff --git a/Makefile b/Makefile index 697b250..ff100da 100644 --- a/Makefile +++ b/Makefile @@ -16,3 +16,10 @@ install: micropython-install: ${MPYS} cp -R $(OUT)/* /usr/lib/micropython/ + +clean: + find . -name __pycache__ | xargs rm -rf + find . -name *.pyc | xargs rm -rf + +format: + python3 -m black --config=.black.cfg . diff --git a/debian/changelog b/debian/changelog index 29947f4..5dbb846 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,7 @@ python-ev3dev2 (2.1.0) UNRELEASED; urgency=medium [Daniel Walton] * RPyC update docs and make it easier to use * use double backticks consistently in docstrings + * use double quotes consistently (ran black) [Matěj Volf] * LED animation fix duration None diff --git a/docs/conf.py b/docs/conf.py index 5af6eb7..e0eb001 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,13 +18,13 @@ import shlex import subprocess -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) from git_version import git_version -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +on_rtd = os.environ.get("READTHEDOCS", None) == "True" if on_rtd: - subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'sphinx_bootstrap_theme', 'recommonmark', 'evdev']) + subprocess.check_call([sys.executable, "-m", "pip", "install", "sphinx_bootstrap_theme", "recommonmark", "evdev"]) import sphinx_bootstrap_theme from recommonmark.parser import CommonMarkParser @@ -33,43 +33,38 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.autosummary', -] +extensions = ["sphinx.ext.autodoc", "sphinx.ext.autosummary"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] -source_parsers = { - '.md': CommonMarkParser, -} +source_parsers = {".md": CommonMarkParser} # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] -source_suffix = ['.rst', '.md'] +source_suffix = [".rst", ".md"] # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'python-ev3dev' -copyright = '2015, Ralph Hempel et al' -author = 'Ralph Hempel et al' +project = "python-ev3dev" +copyright = "2015, Ralph Hempel et al" +author = "Ralph Hempel et al" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -89,37 +84,37 @@ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -130,162 +125,151 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() -html_theme = 'bootstrap' +html_theme = "bootstrap" html_theme_options = { - 'bootswatch_theme': 'yeti', - 'navbar_links' : [ - ("GitHub", "https://github.com/ev3dev/ev3dev-lang-python", True) - ] - } + "bootswatch_theme": "yeti", + "navbar_links": [("GitHub", "https://github.com/ev3dev/ev3dev-lang-python", True)], +} # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr' -#html_search_language = 'en' +# html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'python-ev3devdoc' +htmlhelp_basename = "python-ev3devdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', - -# Latex figure (float) alignment -#'figure_align': 'htbp', + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', + # Additional stuff for the LaTeX preamble. + #'preamble': '', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). -latex_documents = [ - (master_doc, 'python-ev3dev.tex', 'python-ev3dev Documentation', - 'Ralph Hempel et al', 'manual'), -] +latex_documents = [(master_doc, "python-ev3dev.tex", "python-ev3dev Documentation", "Ralph Hempel et al", "manual")] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'python-ev3dev', 'python-ev3dev Documentation', - [author], 1) -] +man_pages = [(master_doc, "python-ev3dev", "python-ev3dev Documentation", [author], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -294,41 +278,46 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'python-ev3dev', 'python-ev3dev Documentation', - author, 'python-ev3dev', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "python-ev3dev", + "python-ev3dev Documentation", + author, + "python-ev3dev", + "One line description of project.", + "Miscellaneous", + ) ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" -suppress_warnings = ['image.nonlocal_uri'] +suppress_warnings = ["image.nonlocal_uri"] nitpick_ignore = [ - ('py:class', 'ev3dev2.display.FbMem'), - ('py:class', 'ev3dev2.button.ButtonBase'), - ('py:class', 'int'), - ('py:class', 'float'), - ('py:class', 'string'), - ('py:class', 'iterable'), - ('py:class', 'tuple'), - ('py:class', 'list'), - ('py:exc', 'ValueError') + ("py:class", "ev3dev2.display.FbMem"), + ("py:class", "ev3dev2.button.ButtonBase"), + ("py:class", "int"), + ("py:class", "float"), + ("py:class", "string"), + ("py:class", "iterable"), + ("py:class", "tuple"), + ("py:class", "list"), + ("py:exc", "ValueError"), ] + def setup(app): - app.add_config_value('recommonmark_config', { - 'enable_eval_rst': True, - }, True) + app.add_config_value("recommonmark_config", {"enable_eval_rst": True}, True) app.add_transform(AutoStructify) diff --git a/ev3dev2/__init__.py b/ev3dev2/__init__.py index 2d2895e..08b5c75 100644 --- a/ev3dev2/__init__.py +++ b/ev3dev2/__init__.py @@ -25,18 +25,21 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") + def is_micropython(): return sys.implementation.name == "micropython" + def chain_exception(exception, cause): if is_micropython(): raise exception else: raise exception from cause + try: # if we are in a released build, there will be an auto-generated "version" # module @@ -52,46 +55,47 @@ def chain_exception(exception, cause): import errno from os.path import abspath + def get_current_platform(): """ Look in /sys/class/board-info/ to determine the platform type. This can return 'ev3', 'evb', 'pistorms', 'brickpi', 'brickpi3' or 'fake'. """ - board_info_dir = '/sys/class/board-info/' + board_info_dir = "/sys/class/board-info/" if not os.path.exists(board_info_dir) or os.environ.get("FAKE_SYS"): - return 'fake' + return "fake" for board in os.listdir(board_info_dir): - uevent_filename = os.path.join(board_info_dir, board, 'uevent') + uevent_filename = os.path.join(board_info_dir, board, "uevent") if os.path.exists(uevent_filename): - with open(uevent_filename, 'r') as fh: + with open(uevent_filename, "r") as fh: for line in fh.readlines(): - (key, value) = line.strip().split('=') + (key, value) = line.strip().split("=") - if key == 'BOARD_INFO_MODEL': + if key == "BOARD_INFO_MODEL": - if value == 'LEGO MINDSTORMS EV3': - return 'ev3' + if value == "LEGO MINDSTORMS EV3": + return "ev3" - elif value in ('FatcatLab EVB', 'QuestCape'): - return 'evb' + elif value in ("FatcatLab EVB", "QuestCape"): + return "evb" - elif value == 'PiStorms': - return 'pistorms' + 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 BrickPi": + return "brickpi" - elif value == 'Dexter Industries BrickPi3': - return 'brickpi3' + elif value == "Dexter Industries BrickPi3": + return "brickpi3" - elif value == 'FAKE-SYS': - return 'fake' + elif value == "FAKE-SYS": + return "fake" return None @@ -131,35 +135,33 @@ def matches(attribute, pattern): 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]): + path = class_path + "/" + f + if all([matches(path + "/" + k, kwargs[k]) for k in kwargs]): yield f def library_load_warning_message(library_name, dependent_class): return 'Import warning: Failed to import "{}". {} will be unusable!'.format(library_name, dependent_class) + class DeviceNotFound(Exception): pass + # ----------------------------------------------------------------------------- # Define the base class from which all other ev3dev classes are defined. + class Device(object): """The ev3dev device base class""" - __slots__ = [ - '_path', - '_device_index', - '_attr_cache', - 'kwargs', - ] + __slots__ = ["_path", "_device_index", "_attr_cache", "kwargs"] - DEVICE_ROOT_PATH = '/sys/class' + DEVICE_ROOT_PATH = "/sys/class" - _DEVICE_INDEX = re.compile(r'^.*(\d+)$') + _DEVICE_INDEX = re.compile(r"^.*(\d+)$") - def __init__(self, class_name, name_pattern='*', name_exact=False, **kwargs): + 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). @@ -184,7 +186,7 @@ def __init__(self, class_name, name_pattern='*', name_exact=False, **kwargs): If there was no valid connected device, an error is thrown. """ - classpath = abspath(Device.DEVICE_ROOT_PATH + '/' + class_name) + classpath = abspath(Device.DEVICE_ROOT_PATH + "/" + class_name) self.kwargs = kwargs self._attr_cache = {} @@ -196,12 +198,12 @@ def get_index(file): return None if name_exact: - self._path = classpath + '/' + name_pattern + self._path = classpath + "/" + name_pattern self._device_index = get_index(name_pattern) else: try: name = next(list_device_names(classpath, name_pattern, **kwargs)) - self._path = classpath + '/' + name + self._path = classpath + "/" + name self._device_index = get_index(name) except StopIteration: self._path = None @@ -210,11 +212,11 @@ def get_index(file): chain_exception(DeviceNotFound("%s is not connected." % self), None) def __str__(self): - if 'address' in self.kwargs: - return "%s(%s)" % (self.__class__.__name__, self.kwargs.get('address')) + if "address" in self.kwargs: + return "%s(%s)" % (self.__class__.__name__, self.kwargs.get("address")) else: return self.__class__.__name__ - + def __repr__(self): return self.__str__() @@ -229,11 +231,11 @@ def _attribute_file_open(self, name): w_ok = mode & stat.S_IWGRP if r_ok and w_ok: - mode_str = 'r+' + mode_str = "r+" elif w_ok: - mode_str = 'w' + mode_str = "w" else: - mode_str = 'r' + mode_str = "r" return io.FileIO(path, mode_str) @@ -241,7 +243,7 @@ def _get_attribute(self, attribute, name): """Device attribute getter""" try: if attribute is None: - attribute = self._attribute_file_open( name ) + attribute = self._attribute_file_open(name) else: attribute.seek(0) return attribute, attribute.read().strip().decode() @@ -252,7 +254,7 @@ def _set_attribute(self, attribute, name, value): """Device attribute setter""" try: if attribute is None: - attribute = self._attribute_file_open( name ) + attribute = self._attribute_file_open(name) else: attribute.seek(0) @@ -275,11 +277,17 @@ def _raise_friendly_access_error(self, driver_error, attribute, value): try: max_speed = self.max_speed except (AttributeError, Exception): - chain_exception(ValueError("The given speed value {} was out of range".format(value)), - driver_error) + chain_exception(ValueError("The given speed value {} was out of range".format(value)), driver_error) else: - chain_exception(ValueError("The given speed value {} was out of range. Max speed: +/-{}".format(value, max_speed)), driver_error) - chain_exception(ValueError("One or more arguments were out of range or invalid, value {}".format(value)), driver_error) + chain_exception( + ValueError( + "The given speed value {} was out of range. Max speed: +/-{}".format(value, max_speed) + ), + driver_error, + ) + chain_exception( + ValueError("One or more arguments were out of range or invalid, value {}".format(value)), driver_error + ) elif driver_errorno == errno.ENODEV or driver_errorno == 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 @@ -326,7 +334,7 @@ def get_attr_line(self, 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()] + return attribute, [v.strip("[]") for v in value.split()] def get_cached_attr_set(self, filehandle, keyword): value = self._attr_cache.get(keyword) @@ -340,7 +348,7 @@ def get_cached_attr_set(self, filehandle, keyword): def get_attr_from_set(self, attribute, name): attribute, value = self.get_attr_line(attribute, name) for a in value.split(): - v = a.strip('[]') + v = a.strip("[]") if v != a: return v return "" @@ -367,7 +375,6 @@ def list_devices(class_name, name_pattern, **kwargs): is a list, then a match against any entry of the list is enough. """ - classpath = abspath(Device.DEVICE_ROOT_PATH + '/' + class_name) + 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)) + return (Device(class_name, name, name_exact=True) for name in list_device_names(classpath, name_pattern, **kwargs)) diff --git a/ev3dev2/_platform/brickpi.py b/ev3dev2/_platform/brickpi.py index 47768ae..a19bd52 100644 --- a/ev3dev2/_platform/brickpi.py +++ b/ev3dev2/_platform/brickpi.py @@ -28,29 +28,29 @@ from collections import OrderedDict -OUTPUT_A = 'serial0-0:MA' -OUTPUT_B = 'serial0-0:MB' -OUTPUT_C = 'serial0-0:MC' -OUTPUT_D = 'serial0-0:MD' +OUTPUT_A = "serial0-0:MA" +OUTPUT_B = "serial0-0:MB" +OUTPUT_C = "serial0-0:MC" +OUTPUT_D = "serial0-0:MD" -INPUT_1 = 'serial0-0:S1' -INPUT_2 = 'serial0-0:S2' -INPUT_3 = 'serial0-0:S3' -INPUT_4 = 'serial0-0:S4' +INPUT_1 = "serial0-0:S1" +INPUT_2 = "serial0-0:S2" +INPUT_3 = "serial0-0:S3" +INPUT_4 = "serial0-0:S4" BUTTONS_FILENAME = None EVDEV_DEVICE_NAME = None LEDS = OrderedDict() -LEDS['blue_led1'] = 'led1:blue:brick-status' -LEDS['blue_led2'] = 'led2:blue:brick-status' +LEDS["blue_led1"] = "led1:blue:brick-status" +LEDS["blue_led2"] = "led2:blue:brick-status" LED_GROUPS = OrderedDict() -LED_GROUPS['LED1'] = ('blue_led1',) -LED_GROUPS['LED2'] = ('blue_led2',) +LED_GROUPS["LED1"] = ("blue_led1",) +LED_GROUPS["LED2"] = ("blue_led2",) LED_COLORS = OrderedDict() -LED_COLORS['BLACK'] = (0,) -LED_COLORS['BLUE'] = (1,) +LED_COLORS["BLACK"] = (0,) +LED_COLORS["BLUE"] = (1,) -LED_DEFAULT_COLOR = 'BLUE' +LED_DEFAULT_COLOR = "BLUE" diff --git a/ev3dev2/_platform/brickpi3.py b/ev3dev2/_platform/brickpi3.py index 25a414c..2711533 100644 --- a/ev3dev2/_platform/brickpi3.py +++ b/ev3dev2/_platform/brickpi3.py @@ -1,57 +1,57 @@ from collections import OrderedDict # Up to four brickpi3s can be stacked -OUTPUT_A = 'spi0.1:MA' -OUTPUT_B = 'spi0.1:MB' -OUTPUT_C = 'spi0.1:MC' -OUTPUT_D = 'spi0.1:MD' - -OUTPUT_E = 'spi0.1:ME' -OUTPUT_F = 'spi0.1:MF' -OUTPUT_G = 'spi0.1:MG' -OUTPUT_H = 'spi0.1:MH' - -OUTPUT_I = 'spi0.1:MI' -OUTPUT_J = 'spi0.1:MJ' -OUTPUT_K = 'spi0.1:MK' -OUTPUT_L = 'spi0.1:ML' - -OUTPUT_M = 'spi0.1:MM' -OUTPUT_N = 'spi0.1:MN' -OUTPUT_O = 'spi0.1:MO' -OUTPUT_P = 'spi0.1:MP' - -INPUT_1 = 'spi0.1:S1' -INPUT_2 = 'spi0.1:S2' -INPUT_3 = 'spi0.1:S3' -INPUT_4 = 'spi0.1:S4' - -INPUT_5 = 'spi0.1:S5' -INPUT_6 = 'spi0.1:S6' -INPUT_7 = 'spi0.1:S7' -INPUT_8 = 'spi0.1:S8' - -INPUT_9 = 'spi0.1:S9' -INPUT_10 = 'spi0.1:S10' -INPUT_11 = 'spi0.1:S11' -INPUT_12 = 'spi0.1:S12' - -INPUT_13 = 'spi0.1:S13' -INPUT_14 = 'spi0.1:S14' -INPUT_15 = 'spi0.1:S15' -INPUT_16 = 'spi0.1:S16' +OUTPUT_A = "spi0.1:MA" +OUTPUT_B = "spi0.1:MB" +OUTPUT_C = "spi0.1:MC" +OUTPUT_D = "spi0.1:MD" + +OUTPUT_E = "spi0.1:ME" +OUTPUT_F = "spi0.1:MF" +OUTPUT_G = "spi0.1:MG" +OUTPUT_H = "spi0.1:MH" + +OUTPUT_I = "spi0.1:MI" +OUTPUT_J = "spi0.1:MJ" +OUTPUT_K = "spi0.1:MK" +OUTPUT_L = "spi0.1:ML" + +OUTPUT_M = "spi0.1:MM" +OUTPUT_N = "spi0.1:MN" +OUTPUT_O = "spi0.1:MO" +OUTPUT_P = "spi0.1:MP" + +INPUT_1 = "spi0.1:S1" +INPUT_2 = "spi0.1:S2" +INPUT_3 = "spi0.1:S3" +INPUT_4 = "spi0.1:S4" + +INPUT_5 = "spi0.1:S5" +INPUT_6 = "spi0.1:S6" +INPUT_7 = "spi0.1:S7" +INPUT_8 = "spi0.1:S8" + +INPUT_9 = "spi0.1:S9" +INPUT_10 = "spi0.1:S10" +INPUT_11 = "spi0.1:S11" +INPUT_12 = "spi0.1:S12" + +INPUT_13 = "spi0.1:S13" +INPUT_14 = "spi0.1:S14" +INPUT_15 = "spi0.1:S15" +INPUT_16 = "spi0.1:S16" BUTTONS_FILENAME = None EVDEV_DEVICE_NAME = None LEDS = OrderedDict() -LEDS['amber_led'] = 'led1:amber:brick-status' +LEDS["amber_led"] = "led1:amber:brick-status" LED_GROUPS = OrderedDict() -LED_GROUPS['LED'] = ('amber_led',) +LED_GROUPS["LED"] = ("amber_led",) LED_COLORS = OrderedDict() -LED_COLORS['BLACK'] = (0,) -LED_COLORS['AMBER'] = (1,) +LED_COLORS["BLACK"] = (0,) +LED_COLORS["AMBER"] = (1,) -LED_DEFAULT_COLOR = 'AMBER' +LED_DEFAULT_COLOR = "AMBER" diff --git a/ev3dev2/_platform/ev3.py b/ev3dev2/_platform/ev3.py index 6bf166b..f60cd1f 100644 --- a/ev3dev2/_platform/ev3.py +++ b/ev3dev2/_platform/ev3.py @@ -28,35 +28,35 @@ from collections import OrderedDict -OUTPUT_A = 'ev3-ports:outA' -OUTPUT_B = 'ev3-ports:outB' -OUTPUT_C = 'ev3-ports:outC' -OUTPUT_D = 'ev3-ports:outD' +OUTPUT_A = "ev3-ports:outA" +OUTPUT_B = "ev3-ports:outB" +OUTPUT_C = "ev3-ports:outC" +OUTPUT_D = "ev3-ports:outD" -INPUT_1 = 'ev3-ports:in1' -INPUT_2 = 'ev3-ports:in2' -INPUT_3 = 'ev3-ports:in3' -INPUT_4 = 'ev3-ports:in4' +INPUT_1 = "ev3-ports:in1" +INPUT_2 = "ev3-ports:in2" +INPUT_3 = "ev3-ports:in3" +INPUT_4 = "ev3-ports:in4" -BUTTONS_FILENAME = '/dev/input/by-path/platform-gpio_keys-event' -EVDEV_DEVICE_NAME = 'EV3 Brick Buttons' +BUTTONS_FILENAME = "/dev/input/by-path/platform-gpio_keys-event" +EVDEV_DEVICE_NAME = "EV3 Brick Buttons" LEDS = OrderedDict() -LEDS['red_left'] = 'led0:red:brick-status' -LEDS['red_right'] = 'led1:red:brick-status' -LEDS['green_left'] = 'led0:green:brick-status' -LEDS['green_right'] = 'led1:green:brick-status' +LEDS["red_left"] = "led0:red:brick-status" +LEDS["red_right"] = "led1:red:brick-status" +LEDS["green_left"] = "led0:green:brick-status" +LEDS["green_right"] = "led1:green:brick-status" LED_GROUPS = OrderedDict() -LED_GROUPS['LEFT'] = ('red_left', 'green_left') -LED_GROUPS['RIGHT'] = ('red_right', 'green_right') +LED_GROUPS["LEFT"] = ("red_left", "green_left") +LED_GROUPS["RIGHT"] = ("red_right", "green_right") LED_COLORS = OrderedDict() -LED_COLORS['BLACK'] = (0, 0) -LED_COLORS['RED'] = (1, 0) -LED_COLORS['GREEN'] = (0, 1) -LED_COLORS['AMBER'] = (1, 1) -LED_COLORS['ORANGE'] = (1, 0.5) -LED_COLORS['YELLOW'] = (0.1, 1) - -LED_DEFAULT_COLOR = 'GREEN' +LED_COLORS["BLACK"] = (0, 0) +LED_COLORS["RED"] = (1, 0) +LED_COLORS["GREEN"] = (0, 1) +LED_COLORS["AMBER"] = (1, 1) +LED_COLORS["ORANGE"] = (1, 0.5) +LED_COLORS["YELLOW"] = (0.1, 1) + +LED_DEFAULT_COLOR = "GREEN" diff --git a/ev3dev2/_platform/evb.py b/ev3dev2/_platform/evb.py index 9350470..35f6849 100644 --- a/ev3dev2/_platform/evb.py +++ b/ev3dev2/_platform/evb.py @@ -1,23 +1,22 @@ - """ An assortment of classes modeling specific features of the EVB. """ -OUTPUT_A = 'outA' -OUTPUT_B = 'outB' -OUTPUT_C = 'outC' -OUTPUT_D = 'outD' +OUTPUT_A = "outA" +OUTPUT_B = "outB" +OUTPUT_C = "outC" +OUTPUT_D = "outD" -INPUT_1 = 'in1' -INPUT_2 = 'in2' -INPUT_3 = 'in3' -INPUT_4 = 'in4' +INPUT_1 = "in1" +INPUT_2 = "in2" +INPUT_3 = "in3" +INPUT_4 = "in4" -BUTTONS_FILENAME = '/dev/input/by-path/platform-evb-buttons-event' -EVDEV_DEVICE_NAME = 'evb-input' +BUTTONS_FILENAME = "/dev/input/by-path/platform-evb-buttons-event" +EVDEV_DEVICE_NAME = "evb-input" # EVB does not have LEDs LEDS = {} LED_GROUPS = {} LED_COLORS = {} -LED_DEFAULT_COLOR = '' +LED_DEFAULT_COLOR = "" diff --git a/ev3dev2/_platform/fake.py b/ev3dev2/_platform/fake.py index d13a1fd..02ff45c 100644 --- a/ev3dev2/_platform/fake.py +++ b/ev3dev2/_platform/fake.py @@ -1,13 +1,12 @@ +OUTPUT_A = "outA" +OUTPUT_B = "outB" +OUTPUT_C = "outC" +OUTPUT_D = "outD" -OUTPUT_A = 'outA' -OUTPUT_B = 'outB' -OUTPUT_C = 'outC' -OUTPUT_D = 'outD' - -INPUT_1 = 'in1' -INPUT_2 = 'in2' -INPUT_3 = 'in3' -INPUT_4 = 'in4' +INPUT_1 = "in1" +INPUT_2 = "in2" +INPUT_3 = "in3" +INPUT_4 = "in4" BUTTONS_FILENAME = None EVDEV_DEVICE_NAME = None @@ -15,4 +14,4 @@ LEDS = {} LED_GROUPS = {} LED_COLORS = {} -LED_DEFAULT_COLOR = '' +LED_DEFAULT_COLOR = "" diff --git a/ev3dev2/_platform/pistorms.py b/ev3dev2/_platform/pistorms.py index 38a4f91..54b7f71 100644 --- a/ev3dev2/_platform/pistorms.py +++ b/ev3dev2/_platform/pistorms.py @@ -1,43 +1,42 @@ - """ An assortment of classes modeling specific features of the PiStorms. """ from collections import OrderedDict -OUTPUT_A = 'pistorms:BAM1' -OUTPUT_B = 'pistorms:BAM2' -OUTPUT_C = 'pistorms:BBM1' -OUTPUT_D = 'pistorms:BBM2' +OUTPUT_A = "pistorms:BAM1" +OUTPUT_B = "pistorms:BAM2" +OUTPUT_C = "pistorms:BBM1" +OUTPUT_D = "pistorms:BBM2" -INPUT_1 = 'pistorms:BAS1' -INPUT_2 = 'pistorms:BAS2' -INPUT_3 = 'pistorms:BBS1' -INPUT_4 = 'pistorms:BBS2' +INPUT_1 = "pistorms:BAS1" +INPUT_2 = "pistorms:BAS2" +INPUT_3 = "pistorms:BBS1" +INPUT_4 = "pistorms:BBS2" -BUTTONS_FILENAME = '/dev/input/by-path/platform-3f804000.i2c-event' -EVDEV_DEVICE_NAME = 'PiStorms' +BUTTONS_FILENAME = "/dev/input/by-path/platform-3f804000.i2c-event" +EVDEV_DEVICE_NAME = "PiStorms" LEDS = OrderedDict() -LEDS['red_left'] = 'pistorms:BB:red:brick-status' -LEDS['red_right'] = 'pistorms:BA:red:brick-status' -LEDS['green_left'] = 'pistorms:BB:green:brick-status' -LEDS['green_right'] = 'pistorms:BA:green:brick-status' -LEDS['blue_left'] = 'pistorms:BB:blue:brick-status' -LEDS['blue_right'] = 'pistorms:BA:blue:brick-status' +LEDS["red_left"] = "pistorms:BB:red:brick-status" +LEDS["red_right"] = "pistorms:BA:red:brick-status" +LEDS["green_left"] = "pistorms:BB:green:brick-status" +LEDS["green_right"] = "pistorms:BA:green:brick-status" +LEDS["blue_left"] = "pistorms:BB:blue:brick-status" +LEDS["blue_right"] = "pistorms:BA:blue:brick-status" LED_GROUPS = OrderedDict() -LED_GROUPS['LEFT'] = ('red_left', 'green_left', 'blue_left') -LED_GROUPS['RIGHT'] = ('red_right', 'green_right', 'blue_right') +LED_GROUPS["LEFT"] = ("red_left", "green_left", "blue_left") +LED_GROUPS["RIGHT"] = ("red_right", "green_right", "blue_right") LED_COLORS = OrderedDict() -LED_COLORS['BLACK'] = (0, 0, 0) -LED_COLORS['RED'] = (1, 0, 0) -LED_COLORS['GREEN'] = (0, 1, 0) -LED_COLORS['BLUE'] = (0, 0, 1) -LED_COLORS['YELLOW'] = (1, 1, 0) -LED_COLORS['CYAN'] = (0, 1, 1) -LED_COLORS['MAGENTA'] = (1, 0, 1) - -LED_DEFAULT_COLOR = 'GREEN' +LED_COLORS["BLACK"] = (0, 0, 0) +LED_COLORS["RED"] = (1, 0, 0) +LED_COLORS["GREEN"] = (0, 1, 0) +LED_COLORS["BLUE"] = (0, 0, 1) +LED_COLORS["YELLOW"] = (1, 1, 0) +LED_COLORS["CYAN"] = (0, 1, 1) +LED_COLORS["MAGENTA"] = (1, 0, 1) + +LED_DEFAULT_COLOR = "GREEN" diff --git a/ev3dev2/auto.py b/ev3dev2/auto.py index 7f7c8bb..93cef97 100644 --- a/ev3dev2/auto.py +++ b/ev3dev2/auto.py @@ -2,32 +2,32 @@ platform = get_current_platform() -if platform == 'ev3': +if platform == "ev3": from ev3dev2._platform.ev3 import INPUT_1, INPUT_2, INPUT_3, INPUT_4 from ev3dev2._platform.ev3 import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D from ev3dev2._platform.ev3 import LEDS, LED_GROUPS, LED_COLORS -elif platform == 'evb': +elif platform == "evb": from ev3dev2._platform.evb import INPUT_1, INPUT_2, INPUT_3, INPUT_4 from ev3dev2._platform.evb import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D from ev3dev2._platform.evb import LEDS, LED_GROUPS, LED_COLORS -elif platform == 'pistorms': +elif platform == "pistorms": from ev3dev2._platform.pistorms import INPUT_1, INPUT_2, INPUT_3, INPUT_4 from ev3dev2._platform.pistorms import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D from ev3dev2._platform.pistorms import LEDS, LED_GROUPS, LED_COLORS -elif platform == 'brickpi': +elif platform == "brickpi": from ev3dev2._platform.brickpi import INPUT_1, INPUT_2, INPUT_3, INPUT_4 from ev3dev2._platform.brickpi import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D from ev3dev2._platform.brickpi import LEDS, LED_GROUPS, LED_COLORS -elif platform == 'brickpi3': +elif platform == "brickpi3": from ev3dev2._platform.brickpi3 import INPUT_1, INPUT_2, INPUT_3, INPUT_4 from ev3dev2._platform.brickpi3 import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D from ev3dev2._platform.brickpi3 import LEDS, LED_GROUPS, LED_COLORS -elif platform == 'fake': +elif platform == "fake": from ev3dev2._platform.fake import INPUT_1, INPUT_2, INPUT_3, INPUT_4 from ev3dev2._platform.fake import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D from ev3dev2._platform.fake import LEDS, LED_GROUPS, LED_COLORS diff --git a/ev3dev2/button.py b/ev3dev2/button.py index f43d1e6..0f3fabd 100644 --- a/ev3dev2/button.py +++ b/ev3dev2/button.py @@ -25,8 +25,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") from ev3dev2.stopwatch import StopWatch from ev3dev2 import get_current_platform, is_micropython, library_load_warning_message @@ -37,22 +37,22 @@ # Import the button filenames, this is platform specific platform = get_current_platform() -if platform == 'ev3': +if platform == "ev3": from ._platform.ev3 import BUTTONS_FILENAME, EVDEV_DEVICE_NAME -elif platform == 'evb': +elif platform == "evb": from ._platform.evb import BUTTONS_FILENAME, EVDEV_DEVICE_NAME -elif platform == 'pistorms': +elif platform == "pistorms": from ._platform.pistorms import BUTTONS_FILENAME, EVDEV_DEVICE_NAME -elif platform == 'brickpi': +elif platform == "brickpi": from ._platform.brickpi import BUTTONS_FILENAME, EVDEV_DEVICE_NAME -elif platform == 'brickpi3': +elif platform == "brickpi3": from ._platform.brickpi3 import BUTTONS_FILENAME, EVDEV_DEVICE_NAME -elif platform == 'fake': +elif platform == "fake": from ._platform.fake import BUTTONS_FILENAME, EVDEV_DEVICE_NAME else: @@ -64,7 +64,6 @@ class MissingButton(Exception): class ButtonCommon(object): - def __str__(self): return self.__class__.__name__ @@ -130,12 +129,12 @@ def process(self, new_state=None): """ if new_state is None: new_state = set(self.buttons_pressed) - old_state = self._state if hasattr(self, '_state') else set() + old_state = self._state if hasattr(self, "_state") else set() self._state = new_state state_diff = new_state.symmetric_difference(old_state) for button in state_diff: - handler = getattr(self, 'on_' + button) + handler = getattr(self, "on_" + button) if handler is not None: handler(button in new_state) @@ -161,42 +160,42 @@ def up(self): """ Check if ``up`` button is pressed. """ - return 'up' in self.buttons_pressed + return "up" in self.buttons_pressed @property def down(self): """ Check if ``down`` button is pressed. """ - return 'down' in self.buttons_pressed + return "down" in self.buttons_pressed @property def left(self): """ Check if ``left`` button is pressed. """ - return 'left' in self.buttons_pressed + return "left" in self.buttons_pressed @property def right(self): """ Check if ``right`` button is pressed. """ - return 'right' in self.buttons_pressed + return "right" in self.buttons_pressed @property def enter(self): """ Check if ``enter`` button is pressed. """ - return 'enter' in self.buttons_pressed + return "enter" in self.buttons_pressed @property def backspace(self): """ Check if ``backspace`` button is pressed. """ - return 'backspace' in self.buttons_pressed + return "backspace" in self.buttons_pressed # micropython implementation @@ -213,13 +212,11 @@ def backspace(self): if platform not in ("ev3", "fake"): raise Exception("micropython button support has not been implemented for '%s'" % platform) - def _test_bit(buf, index): byte = buf[int(index >> 3)] bit = byte & (1 << (index % 8)) return bool(bit) - class ButtonBase(ButtonCommon): pass @@ -238,25 +235,18 @@ class Button(ButtonCommon, EV3ButtonCommon): # Note, this order is intentional and comes from the EV3-G software _BUTTONS = (UP, DOWN, LEFT, RIGHT, ENTER, BACK) - _BUTTON_DEV = '/dev/input/by-path/platform-gpio_keys-event' - - _BUTTON_TO_STRING = { - UP : "up", - DOWN : "down", - LEFT : "left", - RIGHT : "right", - ENTER : "enter", - BACK : "backspace", - } + _BUTTON_DEV = "/dev/input/by-path/platform-gpio_keys-event" + + _BUTTON_TO_STRING = {UP: "up", DOWN: "down", LEFT: "left", RIGHT: "right", ENTER: "enter", BACK: "backspace"} # stuff from linux/input.h and linux/input-event-codes.h _KEY_MAX = 0x2FF _KEY_BUF_LEN = (_KEY_MAX + 7) // 8 - _EVIOCGKEY = 2 << (14 + 8 + 8) | _KEY_BUF_LEN << (8 + 8) | ord('E') << 8 | 0x18 + _EVIOCGKEY = 2 << (14 + 8 + 8) | _KEY_BUF_LEN << (8 + 8) | ord("E") << 8 | 0x18 def __init__(self): super(Button, self).__init__() - self._devnode = open(Button._BUTTON_DEV, 'b') + self._devnode = open(Button._BUTTON_DEV, "b") self._fd = self._devnode.fileno() self._buffer = bytearray(Button._KEY_BUF_LEN) @@ -285,10 +275,10 @@ def _wait(self, wait_for_button_press, wait_for_button_release, timeout_ms): # with the name of a single button. If it is a string of a single # button convert that to a list. if isinstance(wait_for_button_press, str): - wait_for_button_press = [wait_for_button_press, ] + wait_for_button_press = [wait_for_button_press] if isinstance(wait_for_button_release, str): - wait_for_button_release = [wait_for_button_release, ] + wait_for_button_release = [wait_for_button_release] while True: all_pressed = True @@ -311,6 +301,7 @@ def _wait(self, wait_for_button_press, wait_for_button_release, timeout_ms): if timeout_ms is not None and stopwatch.value_ms >= timeout_ms: return False + # python3 implementation else: import array @@ -331,11 +322,11 @@ def _wait(self, wait_for_button_press, wait_for_button_release, timeout_ms): except ImportError: log.warning(library_load_warning_message("evdev", "Button")) - class ButtonBase(ButtonCommon): """ Abstract button interface. """ + _state = set([]) @property @@ -356,7 +347,6 @@ def process_forever(self): if event.type == evdev.ecodes.EV_KEY: self.process() - class ButtonEVIO(ButtonBase): """ Provides a generic button reading mechanism that works with event interface @@ -369,7 +359,7 @@ class ButtonEVIO(ButtonBase): KEY_MAX = 0x2FF KEY_BUF_LEN = int((KEY_MAX + 7) / 8) - EVIOCGKEY = (2 << (14 + 8 + 8) | KEY_BUF_LEN << (8 + 8) | ord('E') << 8 | 0x18) + EVIOCGKEY = 2 << (14 + 8 + 8) | KEY_BUF_LEN << (8 + 8) | ord("E") << 8 | 0x18 _buttons = {} @@ -379,14 +369,14 @@ def __init__(self): self._buffer_cache = {} for b in self._buttons: - name = self._buttons[b]['name'] + name = self._buttons[b]["name"] if name is None: raise MissingButton("Button '%s' is not available on this platform" % b) if name not in self._file_cache: - self._file_cache[name] = open(name, 'rb', 0) - self._buffer_cache[name] = array.array('B', [0] * self.KEY_BUF_LEN) + self._file_cache[name] = open(name, "rb", 0) + self._buffer_cache[name] = array.array("B", [0] * self.KEY_BUF_LEN) def _button_file(self, name): return self._file_cache[name] @@ -404,8 +394,8 @@ def buttons_pressed(self): pressed = [] for k, v in self._buttons.items(): - buf = self._buffer_cache[v['name']] - bit = v['value'] + buf = self._buffer_cache[v["name"]] + bit = v["value"] if bool(buf[int(bit / 8)] & 1 << bit % 8): pressed.append(k) @@ -420,10 +410,10 @@ def _wait(self, wait_for_button_press, wait_for_button_release, timeout_ms): # with the name of a single button. If it is a string of a single # button convert that to a list. if isinstance(wait_for_button_press, str): - wait_for_button_press = [wait_for_button_press, ] + wait_for_button_press = [wait_for_button_press] if isinstance(wait_for_button_release, str): - wait_for_button_release = [wait_for_button_release, ] + wait_for_button_release = [wait_for_button_release] for event in self.evdev_device.read_loop(): if event.type == evdev.ecodes.EV_KEY: @@ -447,18 +437,17 @@ def _wait(self, wait_for_button_press, wait_for_button_release, timeout_ms): if timeout_ms is not None and stopwatch.value_ms >= timeout_ms: return False - class Button(ButtonEVIO, EV3ButtonCommon): """ EV3 Buttons """ _buttons = { - 'up': {'name': BUTTONS_FILENAME, 'value': 103}, - 'down': {'name': BUTTONS_FILENAME, 'value': 108}, - 'left': {'name': BUTTONS_FILENAME, 'value': 105}, - 'right': {'name': BUTTONS_FILENAME, 'value': 106}, - 'enter': {'name': BUTTONS_FILENAME, 'value': 28}, - 'backspace': {'name': BUTTONS_FILENAME, 'value': 14}, + "up": {"name": BUTTONS_FILENAME, "value": 103}, + "down": {"name": BUTTONS_FILENAME, "value": 108}, + "left": {"name": BUTTONS_FILENAME, "value": 105}, + "right": {"name": BUTTONS_FILENAME, "value": 106}, + "enter": {"name": BUTTONS_FILENAME, "value": 28}, + "backspace": {"name": BUTTONS_FILENAME, "value": 14}, } evdev_device_name = EVDEV_DEVICE_NAME diff --git a/ev3dev2/console.py b/ev3dev2/console.py index a7d2dc0..61fadf9 100644 --- a/ev3dev2/console.py +++ b/ev3dev2/console.py @@ -21,7 +21,7 @@ import os -class Console(): +class Console: """ A class that represents the EV3 LCD console, which implements ANSI codes for cursor positioning, text color, and resetting the screen. Supports changing @@ -90,7 +90,7 @@ def cursor(self, value): Set to True to show the cursor, or False to hide it. """ self._cursor = value - print("\x1b[?25{}".format('h' if value else 'l'), end='') + print("\x1b[?25{}".format("h" if value else "l"), end="") def text_at(self, text, column=1, row=1, reset_console=False, inverse=False, alignment="L"): """ @@ -140,7 +140,7 @@ def text_at(self, text, column=1, row=1, reset_console=False, inverse=False, ali if inverse: text = "\x1b[7m{}\x1b[m".format(text) - print("\x1b[{};{}H{}".format(row, column, text), end='') + print("\x1b[{};{}H{}".format(row, column, text), end="") def set_font(self, font="Lat15-TerminusBold24x12", reset_console=True): """ @@ -157,7 +157,7 @@ def set_font(self, font="Lat15-TerminusBold24x12", reset_console=True): if font is not None and font != self._font: self._font = font os.system("setfont {}".format(font)) - rows, columns = os.popen('stty size').read().strip().split(" ") + rows, columns = os.popen("stty size").read().strip().split(" ") self._rows = int(rows) self._columns = int(columns) @@ -176,11 +176,11 @@ def clear_to_eol(self, column=None, row=None): """ if column is not None and row is not None: - print("\x1b[{};{}H".format(row, column), end='') - print("\x1b[K", end='') + print("\x1b[{};{}H".format(row, column), end="") + print("\x1b[K", end="") def reset_console(self): """ Clear the EV3 LCD console using ANSI codes, and move the cursor to 1,1 """ - print("\x1b[2J\x1b[H", end='') + print("\x1b[2J\x1b[H", end="") diff --git a/ev3dev2/control/GyroBalancer.py b/ev3dev2/control/GyroBalancer.py index 1d4929e..3962f74 100644 --- a/ev3dev2/control/GyroBalancer.py +++ b/ev3dev2/control/GyroBalancer.py @@ -82,28 +82,29 @@ class GyroBalancer(object): Robot will keep its balance. """ - def __init__(self, - gain_gyro_angle=1700, - gain_gyro_rate=120, - gain_motor_angle=7, - gain_motor_angular_speed=9, - gain_motor_angle_error_accumulated=3, - power_voltage_nominal=8.0, - pwr_friction_offset_nom=3, - timing_loop_msec=30, - motor_angle_history_length=5, - gyro_drift_compensation_factor=0.05, - left_motor_port=OUTPUT_D, - right_motor_port=OUTPUT_A, - debug=False): + def __init__( + self, + gain_gyro_angle=1700, + gain_gyro_rate=120, + gain_motor_angle=7, + gain_motor_angular_speed=9, + gain_motor_angle_error_accumulated=3, + power_voltage_nominal=8.0, + pwr_friction_offset_nom=3, + timing_loop_msec=30, + motor_angle_history_length=5, + gyro_drift_compensation_factor=0.05, + left_motor_port=OUTPUT_D, + right_motor_port=OUTPUT_A, + debug=False, + ): """Create GyroBalancer.""" # Gain parameters self.gain_gyro_angle = gain_gyro_angle self.gain_gyro_rate = gain_gyro_rate self.gain_motor_angle = gain_motor_angle self.gain_motor_angular_speed = gain_motor_angular_speed - self.gain_motor_angle_error_accumulated =\ - gain_motor_angle_error_accumulated + self.gain_motor_angle_error_accumulated = gain_motor_angle_error_accumulated # Power parameters self.power_voltage_nominal = power_voltage_nominal @@ -138,13 +139,10 @@ def __init__(self, # Open sensor and motor files self.gyro_file = open(self.gyro._path + "/value0", "rb") self.touch_file = open(self.touch._path + "/value0", "rb") - self.encoder_left_file = open(self.motor_left._path + "/position", - "rb") - self.encoder_right_file = open(self.motor_right._path + "/position", - "rb") + self.encoder_left_file = open(self.motor_left._path + "/position", "rb") + self.encoder_right_file = open(self.motor_right._path + "/position", "rb") self.dc_left_file = open(self.motor_left._path + "/duty_cycle_sp", "w") - self.dc_right_file = open(self.motor_right._path + "/duty_cycle_sp", - "w") + self.dc_right_file = open(self.motor_right._path + "/duty_cycle_sp", "w") # Drive queue self.drive_queue = queue.Queue() @@ -174,7 +172,7 @@ def shutdown(self): def _fast_read(self, infile): """Function for fast reading from sensor files.""" infile.seek(0) - return(int(infile.read().decode().strip())) + return int(infile.read().decode().strip()) def _fast_write(self, outfile, value): """Function for fast writing to motor files.""" @@ -182,11 +180,10 @@ def _fast_write(self, outfile, value): outfile.write(str(int(value))) outfile.flush() - def _set_duty(self, motor_duty_file, duty, friction_offset, - voltage_comp): + def _set_duty(self, motor_duty_file, duty, friction_offset, voltage_comp): """Function to set the duty cycle of the motors.""" # Compensate for nominal voltage and round the input - duty_int = int(round(duty*voltage_comp)) + duty_int = int(round(duty * voltage_comp)) # Add or subtract offset and clamp the value between -100 and 100 if duty_int > 0: @@ -298,8 +295,7 @@ def _balance(self): voltage_comp = self.power_voltage_nominal / voltage_idle # Offset to limit friction deadlock - friction_offset = int(round(self.pwr_friction_offset_nom * - voltage_comp)) + friction_offset = int(round(self.pwr_friction_offset_nom * voltage_comp)) # Timing settings for the program # Time of each loop, measured in seconds. @@ -309,14 +305,13 @@ def _balance(self): # A deque (a fifo array) which we'll use to keep track of # previous motor positions, which we can use to calculate the # rate of change (speed) - motor_angle_hist =\ - deque([0], self.motor_angle_history_length) + motor_angle_hist = deque([0], self.motor_angle_history_length) # The rate at which we'll update the gyro offset (precise # definition given in docs) - gyro_drift_comp_rate =\ - self.gyro_drift_compensation_factor *\ - loop_time_target * RAD_PER_SEC_PER_RAW_GYRO_UNIT + gyro_drift_comp_rate = ( + self.gyro_drift_compensation_factor * loop_time_target * RAD_PER_SEC_PER_RAW_GYRO_UNIT + ) # Calibrate Gyro log.info("-----------------------------------") @@ -346,9 +341,9 @@ def _balance(self): # Data logging data = OrderedDict() loop_times = OrderedDict() - data['loop_times'] = loop_times + data["loop_times"] = loop_times gyro_readings = OrderedDict() - data['gyro_readings'] = gyro_readings + data["gyro_readings"] = gyro_readings # Initial fast read touch sensor value touch_pressed = False @@ -375,9 +370,9 @@ def _balance(self): touch_pressed = self._fast_read(self.touch_file) # Read the Motor Position - motor_angle_raw = ((self._fast_read(self.encoder_left_file) + - self._fast_read(self.encoder_right_file)) / - 2.0) + motor_angle_raw = ( + self._fast_read(self.encoder_left_file) + self._fast_read(self.encoder_right_file) + ) / 2.0 motor_angle = motor_angle_raw * RAD_PER_RAW_MOTOR_UNIT # Read the Gyro @@ -385,7 +380,7 @@ def _balance(self): # Busy wait for the loop to reach target time length loop_time = 0 - while(loop_time < loop_time_target): + while loop_time < loop_time_target: loop_time = time.time() - loop_start_time time.sleep(0.001) @@ -402,48 +397,39 @@ def _balance(self): loop_times[time_of_sample] = loop_time * 1000.0 # Calculate gyro rate - gyro_rate = (gyro_rate_raw - gyro_offset) *\ - RAD_PER_SEC_PER_RAW_GYRO_UNIT + gyro_rate = (gyro_rate_raw - gyro_offset) * RAD_PER_SEC_PER_RAW_GYRO_UNIT # Calculate Motor Parameters - motor_angular_speed_ref =\ - speed * RAD_PER_SEC_PER_PERCENT_SPEED - motor_angle_ref = motor_angle_ref +\ - motor_angular_speed_ref * loop_time_target + motor_angular_speed_ref = speed * RAD_PER_SEC_PER_PERCENT_SPEED + motor_angle_ref = motor_angle_ref + motor_angular_speed_ref * loop_time_target motor_angle_error = motor_angle - motor_angle_ref # Compute Motor Speed - motor_angular_speed =\ - ((motor_angle - motor_angle_hist[0]) / - (self.motor_angle_history_length * loop_time_target)) + motor_angular_speed = (motor_angle - motor_angle_hist[0]) / ( + self.motor_angle_history_length * loop_time_target + ) motor_angular_speed_error = motor_angular_speed motor_angle_hist.append(motor_angle) # Compute the motor duty cycle value - motor_duty_cycle =\ - (self.gain_gyro_angle * gyro_est_angle + - self.gain_gyro_rate * gyro_rate + - self.gain_motor_angle * motor_angle_error + - self.gain_motor_angular_speed * - motor_angular_speed_error + - self.gain_motor_angle_error_accumulated * - motor_angle_error_acc) + motor_duty_cycle = ( + self.gain_gyro_angle * gyro_est_angle + + self.gain_gyro_rate * gyro_rate + + self.gain_motor_angle * motor_angle_error + + self.gain_motor_angular_speed * motor_angular_speed_error + + self.gain_motor_angle_error_accumulated * motor_angle_error_acc + ) # Apply the signal to the motor, and add steering - self._set_duty(self.dc_right_file, motor_duty_cycle + steering, - friction_offset, voltage_comp) - self._set_duty(self.dc_left_file, motor_duty_cycle - steering, - friction_offset, voltage_comp) + self._set_duty(self.dc_right_file, motor_duty_cycle + steering, friction_offset, voltage_comp) + self._set_duty(self.dc_left_file, motor_duty_cycle - steering, friction_offset, voltage_comp) # Update angle estimate and gyro offset estimate - gyro_est_angle = gyro_est_angle + gyro_rate *\ - loop_time_target - gyro_offset = (1 - gyro_drift_comp_rate) *\ - gyro_offset + gyro_drift_comp_rate * gyro_rate_raw + gyro_est_angle = gyro_est_angle + gyro_rate * loop_time_target + gyro_offset = (1 - gyro_drift_comp_rate) * gyro_offset + gyro_drift_comp_rate * gyro_rate_raw # Update Accumulated Motor Error - motor_angle_error_acc = motor_angle_error_acc +\ - motor_angle_error * loop_time_target + motor_angle_error_acc = motor_angle_error_acc + motor_angle_error * loop_time_target # Closing down & Cleaning up @@ -469,7 +455,7 @@ def _balance(self): if self.debug: # Dump logged data to file - with open("data.txt", 'w') as data_file: + with open("data.txt", "w") as data_file: json.dump(data, data_file) def _move(self, speed=0, steering=0, seconds=None): diff --git a/ev3dev2/control/rc_tank.py b/ev3dev2/control/rc_tank.py index 4e367b8..f9886ec 100644 --- a/ev3dev2/control/rc_tank.py +++ b/ev3dev2/control/rc_tank.py @@ -1,4 +1,3 @@ - import logging from ev3dev2.motor import MoveTank from ev3dev2.sensor.lego import InfraredSensor @@ -10,8 +9,7 @@ # Tank classes # ============ class RemoteControlledTank(MoveTank): - - def __init__(self, left_motor_port, right_motor_port, polarity='inversed', speed=400, channel=1): + def __init__(self, left_motor_port, right_motor_port, polarity="inversed", speed=400, channel=1): MoveTank.__init__(self, left_motor_port, right_motor_port) self.set_polarity(polarity) @@ -20,7 +18,7 @@ def __init__(self, left_motor_port, right_motor_port, polarity='inversed', speed self.speed_sp = speed self.remote = InfraredSensor() self.remote.on_channel1_top_left = self.make_move(left_motor, self.speed_sp) - self.remote.on_channel1_bottom_left = self.make_move(left_motor, self.speed_sp* -1) + self.remote.on_channel1_bottom_left = self.make_move(left_motor, self.speed_sp * -1) self.remote.on_channel1_top_right = self.make_move(right_motor, self.speed_sp) self.remote.on_channel1_bottom_right = self.make_move(right_motor, self.speed_sp * -1) self.channel = channel @@ -31,6 +29,7 @@ def move(state): motor.run_forever(speed_sp=dc_sp) else: motor.stop() + return move def main(self): diff --git a/ev3dev2/control/webserver.py b/ev3dev2/control/webserver.py index 36142bc..8122182 100644 --- a/ev3dev2/control/webserver.py +++ b/ev3dev2/control/webserver.py @@ -24,13 +24,13 @@ class to handle REST APIish GETs via their do_GET() # File extension to mimetype mimetype = { - 'css' : 'text/css', - 'gif' : 'image/gif', - 'html' : 'text/html', - 'ico' : 'image/x-icon', - 'jpg' : 'image/jpg', - 'js' : 'application/javascript', - 'png' : 'image/png' + "css": "text/css", + "gif": "image/gif", + "html": "text/html", + "ico": "image/x-icon", + "jpg": "image/jpg", + "js": "application/javascript", + "png": "image/png", } def do_GET(self): @@ -42,8 +42,8 @@ def do_GET(self): self.path = "/index.html" # Serve a file (image, css, html, etc) - if '.' in self.path: - extension = self.path.split('.')[-1] + if "." in self.path: + extension = self.path.split(".")[-1] mt = self.mimetype.get(extension) if mt: @@ -52,20 +52,20 @@ def do_GET(self): # Open the static file requested and send it if os.path.exists(filename): self.send_response(200) - self.send_header('Content-type', mt) + self.send_header("Content-type", mt) self.end_headers() - if extension in ('gif', 'ico', 'jpg', 'png'): + if extension in ("gif", "ico", "jpg", "png"): # Open in binary mode, do not encode - with open(filename, mode='rb') as fh: + with open(filename, mode="rb") as fh: self.wfile.write(fh.read()) else: # Open as plain text and encode - with open(filename, mode='r') as fh: + with open(filename, mode="r") as fh: self.wfile.write(fh.read().encode()) else: log.error("404: %s not found" % self.path) - self.send_error(404, 'File Not Found: %s' % self.path) + self.send_error(404, "File Not Found: %s" % self.path) return True return False @@ -83,8 +83,8 @@ def log_message(self, format, *args): medium_motor_max_speed = None joystick_engaged = False -class TankWebHandler(RobotWebHandler): +class TankWebHandler(RobotWebHandler): def __str__(self): return "%s-TankWebHandler" % self.robot @@ -104,12 +104,12 @@ def do_GET(self): if medium_motor_max_speed is None: motor_max_speed = self.robot.left_motor.max_speed - if hasattr(self.robot, 'medium_motor'): + if hasattr(self.robot, "medium_motor"): medium_motor_max_speed = self.robot.medium_motor.max_speed else: medium_motor_max_speed = 0 - ''' + """ Sometimes we get AJAX requests out of order like this: 2016-09-06 02:29:35,846 DEBUG: seq 65: (x, y): 0, 44 -> speed 462 462 2016-09-06 02:29:35,910 DEBUG: seq 66: (x, y): 0, 45 -> speed 473 473 @@ -134,68 +134,70 @@ def do_GET(self): We can also ignore any move-xy requests that show up late by tracking the max seq for any move-xy we service. - ''' - path = self.path.split('/') + """ + path = self.path.split("/") seq = int(path[1]) action = path[2] # desktop interface - if action == 'move-start': + if action == "move-start": direction = path[3] speed_percentage = path[4] log.debug("seq %d: move %s" % (seq, direction)) - left_speed = int(int(speed_percentage) * motor_max_speed)/100.0 - right_speed = int(int(speed_percentage) * motor_max_speed)/100.0 + left_speed = int(int(speed_percentage) * motor_max_speed) / 100.0 + right_speed = int(int(speed_percentage) * motor_max_speed) / 100.0 - if direction == 'forward': + if direction == "forward": self.robot.left_motor.run_forever(speed_sp=left_speed) self.robot.right_motor.run_forever(speed_sp=right_speed) - elif direction == 'backward': + elif direction == "backward": self.robot.left_motor.run_forever(speed_sp=left_speed * -1) self.robot.right_motor.run_forever(speed_sp=right_speed * -1) - elif direction == 'left': + elif direction == "left": self.robot.left_motor.run_forever(speed_sp=left_speed * -1) self.robot.right_motor.run_forever(speed_sp=right_speed) - elif direction == 'right': + elif direction == "right": self.robot.left_motor.run_forever(speed_sp=left_speed) self.robot.right_motor.run_forever(speed_sp=right_speed * -1) # desktop & mobile interface - elif action == 'move-stop': + elif action == "move-stop": log.debug("seq %d: move stop" % seq) self.robot.left_motor.stop() self.robot.right_motor.stop() joystick_engaged = False # medium motor - elif action == 'motor-stop': + elif action == "motor-stop": motor = path[3] log.debug("seq %d: motor-stop %s" % (seq, motor)) - if motor == 'medium': - if hasattr(self.robot, 'medium_motor'): + if motor == "medium": + if hasattr(self.robot, "medium_motor"): self.robot.medium_motor.stop() else: raise Exception("motor %s not supported yet" % motor) - elif action == 'motor-start': + elif action == "motor-start": motor = path[3] direction = path[4] speed_percentage = path[5] - log.debug("seq %d: start motor %s, direction %s, speed_percentage %s" % (seq, motor, direction, speed_percentage)) - - if motor == 'medium': - if hasattr(self.robot, 'medium_motor'): - if direction == 'clockwise': - medium_speed = int(int(speed_percentage) * medium_motor_max_speed)/100.0 + log.debug( + "seq %d: start motor %s, direction %s, speed_percentage %s" % (seq, motor, direction, speed_percentage) + ) + + if motor == "medium": + if hasattr(self.robot, "medium_motor"): + if direction == "clockwise": + medium_speed = int(int(speed_percentage) * medium_motor_max_speed) / 100.0 self.robot.medium_motor.run_forever(speed_sp=medium_speed) - elif direction == 'counter-clockwise': - medium_speed = int(int(speed_percentage) * medium_motor_max_speed)/100.0 + elif direction == "counter-clockwise": + medium_speed = int(int(speed_percentage) * medium_motor_max_speed) / 100.0 self.robot.medium_motor.run_forever(speed_sp=medium_speed * -1) else: log.info("we do not have a medium_motor") @@ -203,7 +205,7 @@ def do_GET(self): raise Exception("motor %s not supported yet" % motor) # mobile interface - elif action == 'move-xy': + elif action == "move-xy": x = int(path[3]) y = int(path[4]) @@ -213,18 +215,16 @@ def do_GET(self): max_move_xy_seq = seq log.debug("seq %d: (x, y) (%4d, %4d)" % (seq, x, y)) else: - log.debug("seq %d: (x, y) %4d, %4d (ignore, max seq %d)" % - (seq, x, y, max_move_xy_seq)) + log.debug("seq %d: (x, y) %4d, %4d (ignore, max seq %d)" % (seq, x, y, max_move_xy_seq)) else: - log.debug("seq %d: (x, y) %4d, %4d (ignore, joystick idle)" % - (seq, x, y)) + log.debug("seq %d: (x, y) %4d, %4d (ignore, joystick idle)" % (seq, x, y)) - elif action == 'joystick-engaged': + elif action == "joystick-engaged": joystick_engaged = True - elif action == 'log': - msg = ''.join(path[3:]) - re_msg = re.search('^(.*)\?', msg) + elif action == "log": + msg = "".join(path[3:]) + re_msg = re.search("^(.*)\?", msg) if re_msg: msg = re_msg.group(1) @@ -237,7 +237,7 @@ def do_GET(self): # It is good practice to send this but if we are getting move-xy we # tend to get a lot of them and we need to be as fast as possible so # be bad and don't send a reply. This takes ~20ms. - if action != 'move-xy': + if action != "move-xy": self.send_response(204) return True @@ -258,7 +258,7 @@ def run(self): try: log.info("Started HTTP server (content) on port %d" % self.port_number) - self.content_server = HTTPServer(('', self.port_number), self.handler_class) + self.content_server = HTTPServer(("", self.port_number), self.handler_class) self.content_server.serve_forever() # Exit cleanly, stop both web servers and all motors diff --git a/ev3dev2/display.py b/ev3dev2/display.py index 2a46744..86a8b9b 100644 --- a/ev3dev2/display.py +++ b/ev3dev2/display.py @@ -25,8 +25,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") import os import mmap @@ -74,7 +74,7 @@ class FbMem(object): # http://sam.zoy.org/wtfpl/COPYING for more details. # ------------------------------------------------------------------ - __slots__ = ('fid', 'fix_info', 'var_info', 'mmap') + __slots__ = ("fid", "fix_info", "var_info", "mmap") FBIOGET_VSCREENINFO = 0x4600 FBIOGET_FSCREENINFO = 0x4602 @@ -87,61 +87,67 @@ class FixScreenInfo(ctypes.Structure): """The fb_fix_screeninfo from fb.h.""" _fields_ = [ - ('id_name', ctypes.c_char * 16), - ('smem_start', ctypes.c_ulong), - ('smem_len', ctypes.c_uint32), - ('type', ctypes.c_uint32), - ('type_aux', ctypes.c_uint32), - ('visual', ctypes.c_uint32), - ('xpanstep', ctypes.c_uint16), - ('ypanstep', ctypes.c_uint16), - ('ywrapstep', ctypes.c_uint16), - ('line_length', ctypes.c_uint32), - ('mmio_start', ctypes.c_ulong), - ('mmio_len', ctypes.c_uint32), - ('accel', ctypes.c_uint32), - ('reserved', ctypes.c_uint16 * 3), + ("id_name", ctypes.c_char * 16), + ("smem_start", ctypes.c_ulong), + ("smem_len", ctypes.c_uint32), + ("type", ctypes.c_uint32), + ("type_aux", ctypes.c_uint32), + ("visual", ctypes.c_uint32), + ("xpanstep", ctypes.c_uint16), + ("ypanstep", ctypes.c_uint16), + ("ywrapstep", ctypes.c_uint16), + ("line_length", ctypes.c_uint32), + ("mmio_start", ctypes.c_ulong), + ("mmio_len", ctypes.c_uint32), + ("accel", ctypes.c_uint32), + ("reserved", ctypes.c_uint16 * 3), ] class VarScreenInfo(ctypes.Structure): - class FbBitField(ctypes.Structure): """The fb_bitfield struct from fb.h.""" - _fields_ = [ - ('offset', ctypes.c_uint32), - ('length', ctypes.c_uint32), - ('msb_right', ctypes.c_uint32), - ] + _fields_ = [("offset", ctypes.c_uint32), ("length", ctypes.c_uint32), ("msb_right", ctypes.c_uint32)] def __str__(self): - return "%s (offset %s, length %s, msg_right %s)" %\ - (self.__class__.__name__, self.offset, self.length, self.msb_right) + return "%s (offset %s, length %s, msg_right %s)" % ( + self.__class__.__name__, + self.offset, + self.length, + self.msb_right, + ) """The fb_var_screeninfo struct from fb.h.""" _fields_ = [ - ('xres', ctypes.c_uint32), - ('yres', ctypes.c_uint32), - ('xres_virtual', ctypes.c_uint32), - ('yres_virtual', ctypes.c_uint32), - ('xoffset', ctypes.c_uint32), - ('yoffset', ctypes.c_uint32), - - ('bits_per_pixel', ctypes.c_uint32), - ('grayscale', ctypes.c_uint32), - - ('red', FbBitField), - ('green', FbBitField), - ('blue', FbBitField), - ('transp', FbBitField), + ("xres", ctypes.c_uint32), + ("yres", ctypes.c_uint32), + ("xres_virtual", ctypes.c_uint32), + ("yres_virtual", ctypes.c_uint32), + ("xoffset", ctypes.c_uint32), + ("yoffset", ctypes.c_uint32), + ("bits_per_pixel", ctypes.c_uint32), + ("grayscale", ctypes.c_uint32), + ("red", FbBitField), + ("green", FbBitField), + ("blue", FbBitField), + ("transp", FbBitField), ] def __str__(self): - return ("%sx%s at (%s,%s), bpp %s, grayscale %s, red %s, green %s, blue %s, transp %s" % - (self.xres, self.yres, self.xoffset, self.yoffset, self.bits_per_pixel, self.grayscale, - self.red, self.green, self.blue, self.transp)) + return "%sx%s at (%s,%s), bpp %s, grayscale %s, red %s, green %s, blue %s, transp %s" % ( + self.xres, + self.yres, + self.xoffset, + self.yoffset, + self.bits_per_pixel, + self.grayscale, + self.red, + self.green, + self.blue, + self.transp, + ) def __init__(self, fbdev=None): """Create the FbMem framebuffer memory object.""" @@ -160,7 +166,7 @@ def _open_fbdev(fbdev=None): Try to use the FRAMEBUFFER environment variable if fbdev is not given. Use '/dev/fb0' by default. """ - dev = fbdev or os.getenv('FRAMEBUFFER', '/dev/fb0') + dev = fbdev or os.getenv("FRAMEBUFFER", "/dev/fb0") fbfid = os.open(dev, os.O_RDWR) return fbfid @@ -181,13 +187,7 @@ def _get_var_info(fbfid): @staticmethod def _map_fb_memory(fbfid, fix_info): """Map the framebuffer memory.""" - return mmap.mmap( - fbfid, - fix_info.smem_len, - mmap.MAP_SHARED, - mmap.PROT_READ | mmap.PROT_WRITE, - offset=0 - ) + return mmap.mmap(fbfid, fix_info.smem_len, mmap.MAP_SHARED, mmap.PROT_READ | mmap.PROT_WRITE, offset=0) class Display(FbMem): @@ -201,7 +201,7 @@ class Display(FbMem): GRID_ROWS = 12 GRID_ROW_PIXELS = 10 - def __init__(self, desc='Display'): + def __init__(self, desc="Display"): FbMem.__init__(self) self.platform = get_current_platform() @@ -216,13 +216,13 @@ def __init__(self, desc='Display'): im_type = "RGB" else: - raise Exception("Not supported - platform %s with bits_per_pixel %s" % - (self.platform, self.var_info.bits_per_pixel)) + raise Exception( + "Not supported - platform %s with bits_per_pixel %s" % (self.platform, self.var_info.bits_per_pixel) + ) self._img = Image.new( - im_type, - (self.fix_info.line_length * 8 // self.var_info.bits_per_pixel, self.yres), - "white") + im_type, (self.fix_info.line_length * 8 // self.var_info.bits_per_pixel, self.yres), "white" + ) self._draw = ImageDraw.Draw(self._img) self.desc = desc @@ -284,11 +284,11 @@ def _color565(self, r, g, b): """Convert red, green, blue components to a 16-bit 565 RGB value. Components should be values 0 to 255. """ - return (((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) def _img_to_rgb565_bytes(self): pixels = [self._color565(r, g, b) for (r, g, b) in self._img.getdata()] - return pack('H' * len(pixels), *pixels) + return pack("H" * len(pixels), *pixels) def update(self): """ @@ -297,7 +297,7 @@ def update(self): """ if self.var_info.bits_per_pixel == 1: b = self._img.tobytes("raw", "1;R") - self.mmap[:len(b)] = b + self.mmap[: len(b)] = b elif self.var_info.bits_per_pixel == 16: self.mmap[:] = self._img_to_rgb565_bytes() @@ -306,8 +306,9 @@ def update(self): self.mmap[:] = self._img.convert("RGB").tobytes("raw", "XRGB") else: - raise Exception("Not supported - platform %s with bits_per_pixel %s" % - (self.platform, self.var_info.bits_per_pixel)) + raise Exception( + "Not supported - platform %s with bits_per_pixel %s" % (self.platform, self.var_info.bits_per_pixel) + ) def image_filename(self, filename, clear_screen=True, x1=0, y1=0, x2=None, y2=None): @@ -321,7 +322,7 @@ def image_filename(self, filename, clear_screen=True, x1=0, y1=0, x2=None, y2=No else: return self._img.paste(filename_im, (x1, y1)) - def line(self, clear_screen=True, x1=10, y1=10, x2=50, y2=50, line_color='black', width=1): + def line(self, clear_screen=True, x1=10, y1=10, x2=50, y2=50, line_color="black", width=1): """ Draw a line from (x1, y1) to (x2, y2) """ @@ -331,7 +332,7 @@ def line(self, clear_screen=True, x1=10, y1=10, x2=50, y2=50, line_color='black' return self.draw.line((x1, y1, x2, y2), fill=line_color, width=width) - def circle(self, clear_screen=True, x=50, y=50, radius=40, fill_color='black', outline_color='black'): + def circle(self, clear_screen=True, x=50, y=50, radius=40, fill_color="black", outline_color="black"): """ Draw a circle of 'radius' centered at (x, y) """ @@ -346,7 +347,7 @@ def circle(self, clear_screen=True, x=50, y=50, radius=40, fill_color='black', o return self.draw.ellipse((x1, y1, x2, y2), fill=fill_color, outline=outline_color) - def rectangle(self, clear_screen=True, x1=10, y1=10, x2=80, y2=40, fill_color='black', outline_color='black'): + def rectangle(self, clear_screen=True, x1=10, y1=10, x2=80, y2=40, fill_color="black", outline_color="black"): """ Draw a rectangle where the top left corner is at (x1, y1) and the bottom right corner is at (x2, y2) @@ -357,7 +358,7 @@ def rectangle(self, clear_screen=True, x1=10, y1=10, x2=80, y2=40, fill_color='b return self.draw.rectangle((x1, y1, x2, y2), fill=fill_color, outline=outline_color) - def point(self, clear_screen=True, x=10, y=10, point_color='black'): + def point(self, clear_screen=True, x=10, y=10, point_color="black"): """ Draw a single pixel at (x, y) """ @@ -367,7 +368,7 @@ def point(self, clear_screen=True, x=10, y=10, point_color='black'): return self.draw.point((x, y), fill=point_color) - def text_pixels(self, text, clear_screen=True, x=0, y=0, text_color='black', font=None): + def text_pixels(self, text, clear_screen=True, x=0, y=0, text_color="black", font=None): """ Display ``text`` starting at pixel (x, y). @@ -401,7 +402,7 @@ def text_pixels(self, text, clear_screen=True, x=0, y=0, text_color='black', fon else: return self.draw.text((x, y), text, fill=text_color) - def text_grid(self, text, clear_screen=True, x=0, y=0, text_color='black', font=None): + def text_grid(self, text, clear_screen=True, x=0, y=0, text_color="black", font=None): """ Display ``text`` starting at grid (x, y) @@ -423,18 +424,18 @@ def text_grid(self, text, clear_screen=True, x=0, y=0, text_color='black', font= """ - assert 0 <= x < Display.GRID_COLUMNS,\ - "grid columns must be between 0 and %d, %d was requested" %\ - ((Display.GRID_COLUMNS - 1, x)) + assert 0 <= x < Display.GRID_COLUMNS, "grid columns must be between 0 and %d, %d was requested" % ( + (Display.GRID_COLUMNS - 1, x) + ) - assert 0 <= y < Display.GRID_ROWS,\ - "grid rows must be between 0 and %d, %d was requested" %\ - ((Display.GRID_ROWS - 1), y) + assert 0 <= y < Display.GRID_ROWS, "grid rows must be between 0 and %d, %d was requested" % ( + (Display.GRID_ROWS - 1), + y, + ) - return self.text_pixels(text, clear_screen, - x * Display.GRID_COLUMN_PIXELS, - y * Display.GRID_ROW_PIXELS, - text_color, font) + return self.text_pixels( + text, clear_screen, x * Display.GRID_COLUMN_PIXELS, y * Display.GRID_ROW_PIXELS, text_color, font + ) def reset_screen(self): self.clear() diff --git a/ev3dev2/fonts/__init__.py b/ev3dev2/fonts/__init__.py index 2ec97cd..cf49b44 100644 --- a/ev3dev2/fonts/__init__.py +++ b/ev3dev2/fonts/__init__.py @@ -2,15 +2,16 @@ from glob import glob from PIL import ImageFont + def available(): """ Returns list of available font names. """ font_dir = os.path.dirname(__file__) - names = [os.path.basename(os.path.splitext(f)[0]) - for f in glob(os.path.join(font_dir, '*.pil'))] + names = [os.path.basename(os.path.splitext(f)[0]) for f in glob(os.path.join(font_dir, "*.pil"))] return sorted(names) + def load(name): """ Loads the font specified by name and returns it as an instance of @@ -19,9 +20,10 @@ def load(name): """ try: font_dir = os.path.dirname(__file__) - pil_file = os.path.join(font_dir, '{}.pil'.format(name)) - pbm_file = os.path.join(font_dir, '{}.pbm'.format(name)) + pil_file = os.path.join(font_dir, "{}.pil".format(name)) + pbm_file = os.path.join(font_dir, "{}.pbm".format(name)) return ImageFont.load(pil_file) except FileNotFoundError: - raise Exception('Failed to load font "{}". '.format(name) + - 'Check ev3dev.fonts.available() for the list of available fonts') + raise Exception( + 'Failed to load font "{}". '.format(name) + "Check ev3dev.fonts.available() for the list of available fonts" + ) diff --git a/ev3dev2/led.py b/ev3dev2/led.py index de1f1fd..7d041ea 100644 --- a/ev3dev2/led.py +++ b/ev3dev2/led.py @@ -25,8 +25,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") import os import stat @@ -40,22 +40,22 @@ # Import the LED settings, this is platform specific platform = get_current_platform() -if platform == 'ev3': +if platform == "ev3": from ev3dev2._platform.ev3 import LEDS, LED_GROUPS, LED_COLORS, LED_DEFAULT_COLOR -elif platform == 'evb': +elif platform == "evb": from ev3dev2._platform.evb import LEDS, LED_GROUPS, LED_COLORS, LED_DEFAULT_COLOR -elif platform == 'pistorms': +elif platform == "pistorms": from ev3dev2._platform.pistorms import LEDS, LED_GROUPS, LED_COLORS, LED_DEFAULT_COLOR -elif platform == 'brickpi': +elif platform == "brickpi": from ev3dev2._platform.brickpi import LEDS, LED_GROUPS, LED_COLORS, LED_DEFAULT_COLOR -elif platform == 'brickpi3': +elif platform == "brickpi3": from ev3dev2._platform.brickpi3 import LEDS, LED_GROUPS, LED_COLORS, LED_DEFAULT_COLOR -elif platform == 'fake': +elif platform == "fake": from ev3dev2._platform.fake import LEDS, LED_GROUPS, LED_COLORS, LED_DEFAULT_COLOR else: @@ -69,21 +69,11 @@ class Led(Device): for more details. """ - SYSTEM_CLASS_NAME = 'leds' - SYSTEM_DEVICE_NAME_CONVENTION = '*' - __slots__ = [ - '_max_brightness', - '_brightness', - '_triggers', - '_trigger', - '_delay_on', - '_delay_off', - 'desc', - ] - - def __init__(self, - name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, - desc=None, **kwargs): + SYSTEM_CLASS_NAME = "leds" + SYSTEM_DEVICE_NAME_CONVENTION = "*" + __slots__ = ["_max_brightness", "_brightness", "_triggers", "_trigger", "_delay_on", "_delay_off", "desc"] + + def __init__(self, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, desc=None, **kwargs): self.desc = desc super(Led, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._max_brightness = None @@ -104,7 +94,7 @@ def max_brightness(self): """ Returns the maximum allowable brightness value. """ - self._max_brightness, value = self.get_cached_attr_int(self._max_brightness, 'max_brightness') + self._max_brightness, value = self.get_cached_attr_int(self._max_brightness, "max_brightness") return value @property @@ -112,19 +102,19 @@ def brightness(self): """ Sets the brightness level. Possible values are from 0 to ``max_brightness``. """ - self._brightness, value = self.get_attr_int(self._brightness, 'brightness') + self._brightness, value = self.get_attr_int(self._brightness, "brightness") return value @brightness.setter def brightness(self, value): - self._brightness = self.set_attr_int(self._brightness, 'brightness', value) + self._brightness = self.set_attr_int(self._brightness, "brightness", value) @property def triggers(self): """ Returns a list of available triggers. """ - self._triggers, value = self.get_attr_set(self._triggers, 'trigger') + self._triggers, value = self.get_attr_set(self._triggers, "trigger") return value @property @@ -145,19 +135,19 @@ def trigger(self): trigger. However, if you set the brightness value to 0 it will also disable the ``timer`` trigger. """ - self._trigger, value = self.get_attr_from_set(self._trigger, 'trigger') + self._trigger, value = self.get_attr_from_set(self._trigger, "trigger") return value @trigger.setter def trigger(self, value): - self._trigger = self.set_attr_string(self._trigger, 'trigger', value) + self._trigger = self.set_attr_string(self._trigger, "trigger", value) # Workaround for ev3dev/ev3dev#225. # When trigger is set to 'timer', we need to wait for 'delay_on' and # 'delay_off' attributes to appear with correct permissions. - if value == 'timer': - for attr in ('delay_on', 'delay_off'): - path = self._path + '/' + attr + if value == "timer": + for attr in ("delay_on", "delay_off"): + path = self._path + "/" + attr # Make sure the file has been created: for _ in range(5): @@ -191,7 +181,7 @@ def delay_on(self): # reopen the file. for retry in (True, False): try: - self._delay_on, value = self.get_attr_int(self._delay_on, 'delay_on') + self._delay_on, value = self.get_attr_int(self._delay_on, "delay_on") return value except OSError: if retry: @@ -208,7 +198,7 @@ def delay_on(self, value): # reopen the file. for retry in (True, False): try: - self._delay_on = self.set_attr_int(self._delay_on, 'delay_on', value) + self._delay_on = self.set_attr_int(self._delay_on, "delay_on", value) return except OSError: if retry: @@ -231,7 +221,7 @@ def delay_off(self): # reopen the file. for retry in (True, False): try: - self._delay_off, value = self.get_attr_int(self._delay_off, 'delay_off') + self._delay_off, value = self.get_attr_int(self._delay_off, "delay_off") return value except OSError: if retry: @@ -250,7 +240,7 @@ def delay_off(self, value): """ for retry in (True, False): try: - self._delay_off = self.set_attr_int(self._delay_off, 'delay_off', value) + self._delay_off = self.set_attr_int(self._delay_off, "delay_off", value) return except OSError: if retry: @@ -271,7 +261,6 @@ def brightness_pct(self, value): class Leds(object): - def __init__(self): self.leds = OrderedDict() self.led_groups = OrderedDict() @@ -313,14 +302,16 @@ def set_color(self, group, color, pct=1): color_tuple = color if isinstance(color, str): - assert color in self.led_colors, \ - "%s is an invalid LED color, valid choices are %s" % \ - (color, ', '.join(self.led_colors.keys())) + assert color in self.led_colors, "%s is an invalid LED color, valid choices are %s" % ( + color, + ", ".join(self.led_colors.keys()), + ) color_tuple = self.led_colors[color] - assert group in self.led_groups, \ - "%s is an invalid LED group, valid choices are %s" % \ - (group, ', '.join(self.led_groups.keys())) + assert group in self.led_groups, "%s is an invalid LED group, valid choices are %s" % ( + group, + ", ".join(self.led_groups.keys()), + ) for led, value in zip(self.led_groups[group], color_tuple): led.brightness_pct = value * pct @@ -339,9 +330,10 @@ def set(self, group, **kwargs): if not self.leds: return - assert group in self.led_groups, \ - "%s is an invalid LED group, valid choices are %s" % \ - (group, ', '.join(self.led_groups.keys())) + assert group in self.led_groups, "%s is an invalid LED group, valid choices are %s" % ( + group, + ", ".join(self.led_groups.keys()), + ) for led in self.led_groups[group]: for k in kwargs: @@ -385,7 +377,9 @@ def animate_stop(self): while self.animate_thread_id: pass - def animate_police_lights(self, color1, color2, group1='LEFT', group2='RIGHT', sleeptime=0.5, duration=5, block=True): + def animate_police_lights( + self, color1, color2, group1="LEFT", group2="RIGHT", sleeptime=0.5, duration=5, block=True + ): """ Cycle the ``group1`` and ``group2`` LEDs between ``color1`` and ``color2`` to give the effect of police lights. Alternate the ``group1`` and ``group2`` @@ -433,7 +427,7 @@ def _animate_police_lights(): else: self.animate_thread_id = _thread.start_new_thread(_animate_police_lights, ()) - def animate_flash(self, color, groups=('LEFT', 'RIGHT'), sleeptime=0.5, duration=5, block=True): + def animate_flash(self, color, groups=("LEFT", "RIGHT"), sleeptime=0.5, duration=5, block=True): """ Turn all LEDs in ``groups`` off/on to ``color`` every ``sleeptime`` seconds @@ -477,7 +471,7 @@ def _animate_flash(): else: self.animate_thread_id = _thread.start_new_thread(_animate_flash, ()) - def animate_cycle(self, colors, groups=('LEFT', 'RIGHT'), sleeptime=0.5, duration=5, block=True): + def animate_cycle(self, colors, groups=("LEFT", "RIGHT"), sleeptime=0.5, duration=5, block=True): """ Cycle ``groups`` LEDs through ``colors``. Do this in a loop where we display each color for ``sleeptime`` seconds. @@ -492,6 +486,7 @@ def animate_cycle(self, colors, groups=('LEFT', 'RIGHT'), sleeptime=0.5, duratio leds = Leds() leds.animate_cyle(('RED', 'GREEN', 'AMBER')) """ + def _animate_cycle(): index = 0 max_index = len(colors) @@ -523,7 +518,7 @@ def _animate_cycle(): else: self.animate_thread_id = _thread.start_new_thread(_animate_cycle, ()) - def animate_rainbow(self, group1='LEFT', group2='RIGHT', increment_by=0.1, sleeptime=0.1, duration=5, block=True): + def animate_rainbow(self, group1="LEFT", group2="RIGHT", increment_by=0.1, sleeptime=0.1, duration=5, block=True): """ Gradually fade from one color to the next diff --git a/ev3dev2/motor.py b/ev3dev2/motor.py index 1c7e4b3..c7f8849 100644 --- a/ev3dev2/motor.py +++ b/ev3dev2/motor.py @@ -22,8 +22,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") import math import select @@ -52,25 +52,39 @@ # OUTPUT ports have platform specific values that we must import platform = get_current_platform() -if platform == 'ev3': +if platform == "ev3": from ev3dev2._platform.ev3 import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D -elif platform == 'evb': +elif platform == "evb": from ev3dev2._platform.evb import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D -elif platform == 'pistorms': +elif platform == "pistorms": from ev3dev2._platform.pistorms import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D -elif platform == 'brickpi': +elif platform == "brickpi": from ev3dev2._platform.brickpi import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D -elif platform == 'brickpi3': - from ev3dev2._platform.brickpi3 import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D, \ - OUTPUT_E, OUTPUT_F, OUTPUT_G, OUTPUT_H, \ - OUTPUT_I, OUTPUT_J, OUTPUT_K, OUTPUT_L, \ - OUTPUT_M, OUTPUT_N, OUTPUT_O, OUTPUT_P - -elif platform == 'fake': +elif platform == "brickpi3": + from ev3dev2._platform.brickpi3 import ( + OUTPUT_A, + OUTPUT_B, + OUTPUT_C, + OUTPUT_D, + OUTPUT_E, + OUTPUT_F, + OUTPUT_G, + OUTPUT_H, + OUTPUT_I, + OUTPUT_J, + OUTPUT_K, + OUTPUT_L, + OUTPUT_M, + OUTPUT_N, + OUTPUT_O, + OUTPUT_P, + ) + +elif platform == "fake": from ev3dev2._platform.fake import OUTPUT_A, OUTPUT_B, OUTPUT_C, OUTPUT_D else: @@ -112,8 +126,9 @@ class SpeedPercent(SpeedValue): """ def __init__(self, percent): - assert -100 <= percent <= 100,\ - "{} is an invalid percentage, must be between -100 and 100 (inclusive)".format(percent) + assert -100 <= percent <= 100, "{} is an invalid percentage, must be between -100 and 100 (inclusive)".format( + percent + ) self.percent = percent @@ -172,10 +187,12 @@ def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired rotations-per-second """ - assert abs(self.rotations_per_second) <= motor.max_rps,\ - "invalid rotations-per-second: {} max RPS is {}, {} was requested".format( - motor, motor.max_rps, self.rotations_per_second) - return self.rotations_per_second/motor.max_rps * motor.max_speed + assert ( + abs(self.rotations_per_second) <= motor.max_rps + ), "invalid rotations-per-second: {} max RPS is {}, {} was requested".format( + motor, motor.max_rps, self.rotations_per_second + ) + return self.rotations_per_second / motor.max_rps * motor.max_speed class SpeedRPM(SpeedValue): @@ -197,10 +214,12 @@ def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired rotations-per-minute """ - assert abs(self.rotations_per_minute) <= motor.max_rpm,\ - "invalid rotations-per-minute: {} max RPM is {}, {} was requested".format( - motor, motor.max_rpm, self.rotations_per_minute) - return self.rotations_per_minute/motor.max_rpm * motor.max_speed + assert ( + abs(self.rotations_per_minute) <= motor.max_rpm + ), "invalid rotations-per-minute: {} max RPM is {}, {} was requested".format( + motor, motor.max_rpm, self.rotations_per_minute + ) + return self.rotations_per_minute / motor.max_rpm * motor.max_speed class SpeedDPS(SpeedValue): @@ -222,10 +241,12 @@ def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired degrees-per-second """ - assert abs(self.degrees_per_second) <= motor.max_dps,\ - "invalid degrees-per-second: {} max DPS is {}, {} was requested".format( - motor, motor.max_dps, self.degrees_per_second) - return self.degrees_per_second/motor.max_dps * motor.max_speed + assert ( + abs(self.degrees_per_second) <= motor.max_dps + ), "invalid degrees-per-second: {} max DPS is {}, {} was requested".format( + motor, motor.max_dps, self.degrees_per_second + ) + return self.degrees_per_second / motor.max_dps * motor.max_speed class SpeedDPM(SpeedValue): @@ -247,10 +268,12 @@ def to_native_units(self, motor): """ Return the native speed measurement required to achieve desired degrees-per-minute """ - assert abs(self.degrees_per_minute) <= motor.max_dpm,\ - "invalid degrees-per-minute: {} max DPM is {}, {} was requested".format( - motor, motor.max_dpm, self.degrees_per_minute) - return self.degrees_per_minute/motor.max_dpm * motor.max_speed + assert ( + abs(self.degrees_per_minute) <= motor.max_dpm + ), "invalid degrees-per-minute: {} max DPM is {}, {} was requested".format( + motor, motor.max_dpm, self.degrees_per_minute + ) + return self.degrees_per_minute / motor.max_dpm * motor.max_speed class Motor(Device): @@ -262,124 +285,124 @@ class Motor(Device): most common type of motor, so we just call it ``motor``. """ - SYSTEM_CLASS_NAME = 'tacho-motor' - SYSTEM_DEVICE_NAME_CONVENTION = '*' + SYSTEM_CLASS_NAME = "tacho-motor" + SYSTEM_DEVICE_NAME_CONVENTION = "*" __slots__ = [ - '_address', - '_command', - '_commands', - '_count_per_rot', - '_count_per_m', - '_driver_name', - '_duty_cycle', - '_duty_cycle_sp', - '_full_travel_count', - '_polarity', - '_position', - '_position_p', - '_position_i', - '_position_d', - '_position_sp', - '_max_speed', - '_speed', - '_speed_sp', - '_ramp_up_sp', - '_ramp_down_sp', - '_speed_p', - '_speed_i', - '_speed_d', - '_state', - '_stop_action', - '_stop_actions', - '_time_sp', - '_poll', - 'max_rps', - 'max_rpm', - 'max_dps', - 'max_dpm', + "_address", + "_command", + "_commands", + "_count_per_rot", + "_count_per_m", + "_driver_name", + "_duty_cycle", + "_duty_cycle_sp", + "_full_travel_count", + "_polarity", + "_position", + "_position_p", + "_position_i", + "_position_d", + "_position_sp", + "_max_speed", + "_speed", + "_speed_sp", + "_ramp_up_sp", + "_ramp_down_sp", + "_speed_p", + "_speed_i", + "_speed_d", + "_state", + "_stop_action", + "_stop_actions", + "_time_sp", + "_poll", + "max_rps", + "max_rpm", + "max_dps", + "max_dpm", ] #: Run the motor until another command is sent. - COMMAND_RUN_FOREVER = 'run-forever' + COMMAND_RUN_FOREVER = "run-forever" #: Run to an absolute position specified by ``position_sp`` and then #: stop using the action specified in ``stop_action``. - COMMAND_RUN_TO_ABS_POS = 'run-to-abs-pos' + COMMAND_RUN_TO_ABS_POS = "run-to-abs-pos" #: Run to a position relative to the current ``position`` value. #: The new position will be current ``position`` + ``position_sp``. #: When the new position is reached, the motor will stop using #: the action specified by ``stop_action``. - COMMAND_RUN_TO_REL_POS = 'run-to-rel-pos' + COMMAND_RUN_TO_REL_POS = "run-to-rel-pos" #: Run the motor for the amount of time specified in ``time_sp`` #: and then stop the motor using the action specified by ``stop_action``. - COMMAND_RUN_TIMED = 'run-timed' + COMMAND_RUN_TIMED = "run-timed" #: Run the motor at the duty cycle specified by ``duty_cycle_sp``. #: Unlike other run commands, changing ``duty_cycle_sp`` while running *will* #: take effect immediately. - COMMAND_RUN_DIRECT = 'run-direct' + COMMAND_RUN_DIRECT = "run-direct" #: Stop any of the run commands before they are complete using the #: action specified by ``stop_action``. - COMMAND_STOP = 'stop' + COMMAND_STOP = "stop" #: Reset all of the motor parameter attributes to their default value. #: This will also have the effect of stopping the motor. - COMMAND_RESET = 'reset' + COMMAND_RESET = "reset" #: Sets the normal polarity of the rotary encoder. - ENCODER_POLARITY_NORMAL = 'normal' + ENCODER_POLARITY_NORMAL = "normal" #: Sets the inversed polarity of the rotary encoder. - ENCODER_POLARITY_INVERSED = 'inversed' + ENCODER_POLARITY_INVERSED = "inversed" #: With ``normal`` polarity, a positive duty cycle will #: cause the motor to rotate clockwise. - POLARITY_NORMAL = 'normal' + POLARITY_NORMAL = "normal" #: With ``inversed`` polarity, a positive duty cycle will #: cause the motor to rotate counter-clockwise. - POLARITY_INVERSED = 'inversed' + POLARITY_INVERSED = "inversed" #: Power is being sent to the motor. - STATE_RUNNING = 'running' + STATE_RUNNING = "running" #: The motor is ramping up or down and has not yet reached a constant output level. - STATE_RAMPING = 'ramping' + STATE_RAMPING = "ramping" #: The motor is not turning, but rather attempting to hold a fixed position. - STATE_HOLDING = 'holding' + STATE_HOLDING = "holding" #: The motor is turning, but cannot reach its ``speed_sp``. - STATE_OVERLOADED = 'overloaded' + STATE_OVERLOADED = "overloaded" #: The motor is not turning when it should be. - STATE_STALLED = 'stalled' + STATE_STALLED = "stalled" #: Power will be removed from the motor and it will freely coast to a stop. - STOP_ACTION_COAST = 'coast' + STOP_ACTION_COAST = "coast" #: Power will be removed from the motor and a passive electrical load will #: be placed on the motor. This is usually done by shorting the motor terminals #: together. This load will absorb the energy from the rotation of the motors and #: cause the motor to stop more quickly than coasting. - STOP_ACTION_BRAKE = 'brake' + STOP_ACTION_BRAKE = "brake" #: Does not remove power from the motor. Instead it actively try to hold the motor #: at the current position. If an external force tries to turn the motor, the motor #: will ``push back`` to maintain its position. - STOP_ACTION_HOLD = 'hold' + STOP_ACTION_HOLD = "hold" def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - if platform in ('brickpi', 'brickpi3') and type(self).__name__ != 'Motor' and not isinstance(self, LargeMotor): + if platform in ("brickpi", "brickpi3") and type(self).__name__ != "Motor" and not isinstance(self, LargeMotor): raise Exception("{} is unaware of different motor types, use LargeMotor instead".format(platform)) if address is not None: - kwargs['address'] = address + kwargs["address"] = address super(Motor, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._address = None @@ -410,7 +433,7 @@ def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, nam self._stop_actions = None self._time_sp = None self._poll = None - self.max_rps = float(self.max_speed/self.count_per_rot) + self.max_rps = float(self.max_speed / self.count_per_rot) self.max_rpm = self.max_rps * 60 self.max_dps = self.max_rps * 360 self.max_dpm = self.max_rpm * 360 @@ -420,7 +443,7 @@ def address(self): """ Returns the name of the port that this motor is connected to. """ - self._address, value = self.get_attr_string(self._address, 'address') + self._address, value = self.get_attr_string(self._address, "address") return value @property @@ -433,7 +456,7 @@ def command(self): @command.setter def command(self, value): - self._command = self.set_attr_string(self._command, 'command', value) + self._command = self.set_attr_string(self._command, "command", value) @property def commands(self): @@ -458,7 +481,7 @@ def commands(self): - ``reset`` will reset all of the motor parameter attributes to their default value. This will also have the effect of stopping the motor. """ - (self._commands, value) = self.get_cached_attr_set(self._commands, 'commands') + (self._commands, value) = self.get_cached_attr_set(self._commands, "commands") return value @property @@ -468,7 +491,7 @@ def count_per_rot(self): are used by the position and speed attributes, so you can use this value to convert rotations or degrees to tacho counts. (rotation motors only) """ - (self._count_per_rot, value) = self.get_cached_attr_int(self._count_per_rot, 'count_per_rot') + (self._count_per_rot, value) = self.get_cached_attr_int(self._count_per_rot, "count_per_rot") return value @property @@ -478,7 +501,7 @@ def count_per_m(self): counts are used by the position and speed attributes, so you can use this value to convert from distance to tacho counts. (linear motors only) """ - (self._count_per_m, value) = self.get_cached_attr_int(self._count_per_m, 'count_per_m') + (self._count_per_m, value) = self.get_cached_attr_int(self._count_per_m, "count_per_m") return value @property @@ -486,7 +509,7 @@ def driver_name(self): """ Returns the name of the driver that provides this tacho motor device. """ - (self._driver_name, value) = self.get_cached_attr_string(self._driver_name, 'driver_name') + (self._driver_name, value) = self.get_cached_attr_string(self._driver_name, "driver_name") return value @property @@ -495,7 +518,7 @@ def duty_cycle(self): Returns the current duty cycle of the motor. Units are percent. Values are -100 to 100. """ - self._duty_cycle, value = self.get_attr_int(self._duty_cycle, 'duty_cycle') + self._duty_cycle, value = self.get_attr_int(self._duty_cycle, "duty_cycle") return value @property @@ -505,12 +528,12 @@ def duty_cycle_sp(self): Units are in percent. Valid values are -100 to 100. A negative value causes the motor to rotate in reverse. """ - self._duty_cycle_sp, value = self.get_attr_int(self._duty_cycle_sp, 'duty_cycle_sp') + self._duty_cycle_sp, value = self.get_attr_int(self._duty_cycle_sp, "duty_cycle_sp") return value @duty_cycle_sp.setter def duty_cycle_sp(self, value): - self._duty_cycle_sp = self.set_attr_int(self._duty_cycle_sp, 'duty_cycle_sp', value) + self._duty_cycle_sp = self.set_attr_int(self._duty_cycle_sp, "duty_cycle_sp", value) @property def full_travel_count(self): @@ -519,7 +542,7 @@ def full_travel_count(self): combined with the ``count_per_m`` atribute, you can use this value to calculate the maximum travel distance of the motor. (linear motors only) """ - (self._full_travel_count, value) = self.get_cached_attr_int(self._full_travel_count, 'full_travel_count') + (self._full_travel_count, value) = self.get_cached_attr_int(self._full_travel_count, "full_travel_count") return value @property @@ -530,12 +553,12 @@ def polarity(self): a positive duty cycle will cause the motor to rotate counter-clockwise. Valid values are ``normal`` and ``inversed``. """ - self._polarity, value = self.get_attr_string(self._polarity, 'polarity') + self._polarity, value = self.get_attr_string(self._polarity, "polarity") return value @polarity.setter def polarity(self, value): - self._polarity = self.set_attr_string(self._polarity, 'polarity', value) + self._polarity = self.set_attr_string(self._polarity, "polarity", value) @property def position(self): @@ -545,48 +568,48 @@ def position(self): Likewise, rotating counter-clockwise causes the position to decrease. Writing will set the position to that value. """ - self._position, value = self.get_attr_int(self._position, 'position') + self._position, value = self.get_attr_int(self._position, "position") return value @position.setter def position(self, value): - self._position = self.set_attr_int(self._position, 'position', value) + self._position = self.set_attr_int(self._position, "position", value) @property def position_p(self): """ The proportional constant for the position PID. """ - self._position_p, value = self.get_attr_int(self._position_p, 'hold_pid/Kp') + self._position_p, value = self.get_attr_int(self._position_p, "hold_pid/Kp") return value @position_p.setter def position_p(self, value): - self._position_p = self.set_attr_int(self._position_p, 'hold_pid/Kp', value) + self._position_p = self.set_attr_int(self._position_p, "hold_pid/Kp", value) @property def position_i(self): """ The integral constant for the position PID. """ - self._position_i, value = self.get_attr_int(self._position_i, 'hold_pid/Ki') + self._position_i, value = self.get_attr_int(self._position_i, "hold_pid/Ki") return value @position_i.setter def position_i(self, value): - self._position_i = self.set_attr_int(self._position_i, 'hold_pid/Ki', value) + self._position_i = self.set_attr_int(self._position_i, "hold_pid/Ki", value) @property def position_d(self): """ The derivative constant for the position PID. """ - self._position_d, value = self.get_attr_int(self._position_d, 'hold_pid/Kd') + self._position_d, value = self.get_attr_int(self._position_d, "hold_pid/Kd") return value @position_d.setter def position_d(self, value): - self._position_d = self.set_attr_int(self._position_d, 'hold_pid/Kd', value) + self._position_d = self.set_attr_int(self._position_d, "hold_pid/Kd", value) @property def position_sp(self): @@ -596,12 +619,12 @@ def position_sp(self): can use the value returned by ``count_per_rot`` to convert tacho counts to/from rotations or degrees. """ - self._position_sp, value = self.get_attr_int(self._position_sp, 'position_sp') + self._position_sp, value = self.get_attr_int(self._position_sp, "position_sp") return value @position_sp.setter def position_sp(self, value): - self._position_sp = self.set_attr_int(self._position_sp, 'position_sp', value) + self._position_sp = self.set_attr_int(self._position_sp, "position_sp", value) @property def max_speed(self): @@ -610,7 +633,7 @@ def max_speed(self): may be slightly different than the maximum speed that a particular motor can reach - it's the maximum theoretical speed. """ - (self._max_speed, value) = self.get_cached_attr_int(self._max_speed, 'max_speed') + (self._max_speed, value) = self.get_cached_attr_int(self._max_speed, "max_speed") return value @property @@ -620,7 +643,7 @@ def speed(self): not necessarily degrees (although it is for LEGO motors). Use the ``count_per_rot`` attribute to convert this value to RPM or deg/sec. """ - self._speed, value = self.get_attr_int(self._speed, 'speed') + self._speed, value = self.get_attr_int(self._speed, "speed") return value @property @@ -633,12 +656,12 @@ def speed_sp(self): RPM or deg/sec to tacho counts per second. Use the ``count_per_m`` attribute to convert m/s to tacho counts per second. """ - self._speed_sp, value = self.get_attr_int(self._speed_sp, 'speed_sp') + self._speed_sp, value = self.get_attr_int(self._speed_sp, "speed_sp") return value @speed_sp.setter def speed_sp(self, value): - self._speed_sp = self.set_attr_int(self._speed_sp, 'speed_sp', value) + self._speed_sp = self.set_attr_int(self._speed_sp, "speed_sp", value) @property def ramp_up_sp(self): @@ -649,12 +672,12 @@ def ramp_up_sp(self): setpoint. The actual ramp time is the ratio of the difference between the ``speed_sp`` and the current ``speed`` and max_speed multiplied by ``ramp_up_sp``. """ - self._ramp_up_sp, value = self.get_attr_int(self._ramp_up_sp, 'ramp_up_sp') + self._ramp_up_sp, value = self.get_attr_int(self._ramp_up_sp, "ramp_up_sp") return value @ramp_up_sp.setter def ramp_up_sp(self, value): - self._ramp_up_sp = self.set_attr_int(self._ramp_up_sp, 'ramp_up_sp', value) + self._ramp_up_sp = self.set_attr_int(self._ramp_up_sp, "ramp_up_sp", value) @property def ramp_down_sp(self): @@ -665,48 +688,48 @@ def ramp_down_sp(self): setpoint. The actual ramp time is the ratio of the difference between the ``speed_sp`` and the current ``speed`` and max_speed multiplied by ``ramp_down_sp``. """ - self._ramp_down_sp, value = self.get_attr_int(self._ramp_down_sp, 'ramp_down_sp') + self._ramp_down_sp, value = self.get_attr_int(self._ramp_down_sp, "ramp_down_sp") return value @ramp_down_sp.setter def ramp_down_sp(self, value): - self._ramp_down_sp = self.set_attr_int(self._ramp_down_sp, 'ramp_down_sp', value) + self._ramp_down_sp = self.set_attr_int(self._ramp_down_sp, "ramp_down_sp", value) @property def speed_p(self): """ The proportional constant for the speed regulation PID. """ - self._speed_p, value = self.get_attr_int(self._speed_p, 'speed_pid/Kp') + self._speed_p, value = self.get_attr_int(self._speed_p, "speed_pid/Kp") return value @speed_p.setter def speed_p(self, value): - self._speed_p = self.set_attr_int(self._speed_p, 'speed_pid/Kp', value) + self._speed_p = self.set_attr_int(self._speed_p, "speed_pid/Kp", value) @property def speed_i(self): """ The integral constant for the speed regulation PID. """ - self._speed_i, value = self.get_attr_int(self._speed_i, 'speed_pid/Ki') + self._speed_i, value = self.get_attr_int(self._speed_i, "speed_pid/Ki") return value @speed_i.setter def speed_i(self, value): - self._speed_i = self.set_attr_int(self._speed_i, 'speed_pid/Ki', value) + self._speed_i = self.set_attr_int(self._speed_i, "speed_pid/Ki", value) @property def speed_d(self): """ The derivative constant for the speed regulation PID. """ - self._speed_d, value = self.get_attr_int(self._speed_d, 'speed_pid/Kd') + self._speed_d, value = self.get_attr_int(self._speed_d, "speed_pid/Kd") return value @speed_d.setter def speed_d(self, value): - self._speed_d = self.set_attr_int(self._speed_d, 'speed_pid/Kd', value) + self._speed_d = self.set_attr_int(self._speed_d, "speed_pid/Kd", value) @property def state(self): @@ -714,7 +737,7 @@ def state(self): Reading returns a list of state flags. Possible flags are ``running``, ``ramping``, ``holding``, ``overloaded`` and ``stalled``. """ - self._state, value = self.get_attr_set(self._state, 'state') + self._state, value = self.get_attr_set(self._state, "state") return value @property @@ -725,12 +748,12 @@ def stop_action(self): Also, it determines the motors behavior when a run command completes. See ``stop_actions`` for a list of possible values. """ - self._stop_action, value = self.get_attr_string(self._stop_action, 'stop_action') + self._stop_action, value = self.get_attr_string(self._stop_action, "stop_action") return value @stop_action.setter def stop_action(self, value): - self._stop_action = self.set_attr_string(self._stop_action, 'stop_action', value) + self._stop_action = self.set_attr_string(self._stop_action, "stop_action", value) @property def stop_actions(self): @@ -746,7 +769,7 @@ def stop_actions(self): position. If an external force tries to turn the motor, the motor will 'push back' to maintain its position. """ - (self._stop_actions, value) = self.get_cached_attr_set(self._stop_actions, 'stop_actions') + (self._stop_actions, value) = self.get_cached_attr_set(self._stop_actions, "stop_actions") return value @property @@ -756,12 +779,12 @@ def time_sp(self): ``run-timed`` command. Reading returns the current value. Units are in milliseconds. """ - self._time_sp, value = self.get_attr_int(self._time_sp, 'time_sp') + self._time_sp, value = self.get_attr_int(self._time_sp, "time_sp") return value @time_sp.setter def time_sp(self, value): - self._time_sp = self.set_attr_int(self._time_sp, 'time_sp', value) + self._time_sp = self.set_attr_int(self._time_sp, "time_sp", value) def run_forever(self, **kwargs): """ @@ -877,7 +900,7 @@ def wait(self, cond, timeout=None): if self._poll is None: if self._state is None: - self._state = self._attribute_file_open('state') + self._state = self._attribute_file_open("state") self._poll = select.poll() self._poll.register(self._state, select.POLLPRI) @@ -949,8 +972,11 @@ def _speed_native_units(self, speed, label=None): # If speed is not a SpeedValue object we treat it as a percentage if not isinstance(speed, SpeedValue): - assert -100 <= speed <= 100,\ - "{}{} is an invalid speed percentage, must be between -100 and 100 (inclusive)".format("" if label is None else (label + ": ") , speed) + assert ( + -100 <= speed <= 100 + ), "{}{} is an invalid speed percentage, must be between -100 and 100 (inclusive)".format( + "" if label is None else (label + ": "), speed + ) speed = SpeedPercent(speed) return speed.to_native_units(self) @@ -959,7 +985,7 @@ def _set_rel_position_degrees_and_speed_sp(self, degrees, speed): degrees = degrees if speed >= 0 else -degrees speed = abs(speed) - position_delta = int(round((degrees * self.count_per_rot)/360)) + position_delta = int(round((degrees * self.count_per_rot) / 360)) speed_sp = int(round(speed)) self.position_sp = position_delta @@ -984,7 +1010,7 @@ def on_for_rotations(self, speed, rotations, brake=True, block=True): self.run_to_rel_pos() if block: - self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT) + self.wait_until("running", timeout=WAIT_RUNNING_TIMEOUT) self.wait_until_not_moving() def on_for_degrees(self, speed, degrees, brake=True, block=True): @@ -1000,7 +1026,7 @@ def on_for_degrees(self, speed, degrees, brake=True, block=True): self.run_to_rel_pos() if block: - self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT) + self.wait_until("running", timeout=WAIT_RUNNING_TIMEOUT) self.wait_until_not_moving() def on_to_position(self, speed, position, brake=True, block=True): @@ -1017,7 +1043,7 @@ def on_to_position(self, speed, position, brake=True, block=True): self.run_to_abs_pos() if block: - self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT) + self.wait_until("running", timeout=WAIT_RUNNING_TIMEOUT) self.wait_until_not_moving() def on_for_seconds(self, speed, seconds, brake=True, block=True): @@ -1038,7 +1064,7 @@ def on_for_seconds(self, speed, seconds, brake=True, block=True): self.run_timed() if block: - self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT) + self.wait_until("running", timeout=WAIT_RUNNING_TIMEOUT) self.wait_until_not_moving() def on(self, speed, brake=True, block=False): @@ -1057,7 +1083,7 @@ def on(self, speed, brake=True, block=False): self.run_forever() if block: - self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT) + self.wait_until("running", timeout=WAIT_RUNNING_TIMEOUT) self.wait_until_not_moving() def off(self, brake=True): @@ -1087,10 +1113,10 @@ def list_motors(name_pattern=Motor.SYSTEM_DEVICE_NAME_CONVENTION, **kwargs): is a list, then a match against any entry of the list is enough. """ - class_path = abspath(Device.DEVICE_ROOT_PATH + '/' + Motor.SYSTEM_CLASS_NAME) + class_path = abspath(Device.DEVICE_ROOT_PATH + "/" + Motor.SYSTEM_CLASS_NAME) + + return (Motor(name_pattern=name, name_exact=True) for name in list_device_names(class_path, name_pattern, **kwargs)) - return (Motor(name_pattern=name, name_exact=True) - for name in list_device_names(class_path, name_pattern, **kwargs)) class LargeMotor(Motor): @@ -1101,12 +1127,14 @@ class LargeMotor(Motor): """ SYSTEM_CLASS_NAME = Motor.SYSTEM_CLASS_NAME - SYSTEM_DEVICE_NAME_CONVENTION = '*' + SYSTEM_DEVICE_NAME_CONVENTION = "*" __slots__ = [] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(LargeMotor, self).__init__(address, name_pattern, name_exact, driver_name=['lego-ev3-l-motor', 'lego-nxt-motor'], **kwargs) + super(LargeMotor, self).__init__( + address, name_pattern, name_exact, driver_name=["lego-ev3-l-motor", "lego-nxt-motor"], **kwargs + ) class MediumMotor(Motor): @@ -1118,12 +1146,12 @@ class MediumMotor(Motor): """ SYSTEM_CLASS_NAME = Motor.SYSTEM_CLASS_NAME - SYSTEM_DEVICE_NAME_CONVENTION = '*' + SYSTEM_DEVICE_NAME_CONVENTION = "*" __slots__ = [] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(MediumMotor, self).__init__(address, name_pattern, name_exact, driver_name=['lego-ev3-m-motor'], **kwargs) + super(MediumMotor, self).__init__(address, name_pattern, name_exact, driver_name=["lego-ev3-m-motor"], **kwargs) class ActuonixL1250Motor(Motor): @@ -1135,12 +1163,14 @@ class ActuonixL1250Motor(Motor): """ SYSTEM_CLASS_NAME = Motor.SYSTEM_CLASS_NAME - SYSTEM_DEVICE_NAME_CONVENTION = 'linear*' + SYSTEM_DEVICE_NAME_CONVENTION = "linear*" __slots__ = [] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(ActuonixL1250Motor, self).__init__(address, name_pattern, name_exact, driver_name=['act-l12-ev3-50'], **kwargs) + super(ActuonixL1250Motor, self).__init__( + address, name_pattern, name_exact, driver_name=["act-l12-ev3-50"], **kwargs + ) class ActuonixL12100Motor(Motor): @@ -1152,12 +1182,14 @@ class ActuonixL12100Motor(Motor): """ SYSTEM_CLASS_NAME = Motor.SYSTEM_CLASS_NAME - SYSTEM_DEVICE_NAME_CONVENTION = 'linear*' + SYSTEM_DEVICE_NAME_CONVENTION = "linear*" __slots__ = [] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(ActuonixL12100Motor, self).__init__(address, name_pattern, name_exact, driver_name=['act-l12-ev3-100'], **kwargs) + super(ActuonixL12100Motor, self).__init__( + address, name_pattern, name_exact, driver_name=["act-l12-ev3-100"], **kwargs + ) class DcMotor(Device): @@ -1168,28 +1200,28 @@ class DcMotor(Device): and LEGO Power Functions motors. """ - SYSTEM_CLASS_NAME = 'dc-motor' - SYSTEM_DEVICE_NAME_CONVENTION = 'motor*' + SYSTEM_CLASS_NAME = "dc-motor" + SYSTEM_DEVICE_NAME_CONVENTION = "motor*" __slots__ = [ - '_address', - '_command', - '_commands', - '_driver_name', - '_duty_cycle', - '_duty_cycle_sp', - '_polarity', - '_ramp_down_sp', - '_ramp_up_sp', - '_state', - '_stop_action', - '_stop_actions', - '_time_sp', + "_address", + "_command", + "_commands", + "_driver_name", + "_duty_cycle", + "_duty_cycle_sp", + "_polarity", + "_ramp_down_sp", + "_ramp_up_sp", + "_state", + "_stop_action", + "_stop_actions", + "_time_sp", ] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): if address is not None: - kwargs['address'] = address + kwargs["address"] = address super(DcMotor, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._address = None @@ -1211,7 +1243,7 @@ def address(self): """ Returns the name of the port that this motor is connected to. """ - self._address, value = self.get_attr_string(self._address, 'address') + self._address, value = self.get_attr_string(self._address, "address") return value @property @@ -1225,7 +1257,7 @@ def command(self): @command.setter def command(self, value): - self._command = self.set_attr_string(self._command, 'command', value) + self._command = self.set_attr_string(self._command, "command", value) @property def commands(self): @@ -1233,7 +1265,7 @@ def commands(self): Returns a list of commands supported by the motor controller. """ - self._commands, value = self.get_attr_set(self._commands, 'commands') + self._commands, value = self.get_attr_set(self._commands, "commands") return value @property @@ -1242,7 +1274,7 @@ def driver_name(self): Returns the name of the motor driver that loaded this device. See the list of [supported devices] for a list of drivers. """ - self._driver_name, value = self.get_attr_string(self._driver_name, 'driver_name') + self._driver_name, value = self.get_attr_string(self._driver_name, "driver_name") return value @property @@ -1251,7 +1283,7 @@ def duty_cycle(self): Shows the current duty cycle of the PWM signal sent to the motor. Values are -100 to 100 (-100% to 100%). """ - self._duty_cycle, value = self.get_attr_int(self._duty_cycle, 'duty_cycle') + self._duty_cycle, value = self.get_attr_int(self._duty_cycle, "duty_cycle") return value @property @@ -1261,24 +1293,24 @@ def duty_cycle_sp(self): Valid values are -100 to 100 (-100% to 100%). Reading returns the current setpoint. """ - self._duty_cycle_sp, value = self.get_attr_int(self._duty_cycle_sp, 'duty_cycle_sp') + self._duty_cycle_sp, value = self.get_attr_int(self._duty_cycle_sp, "duty_cycle_sp") return value @duty_cycle_sp.setter def duty_cycle_sp(self, value): - self._duty_cycle_sp = self.set_attr_int(self._duty_cycle_sp, 'duty_cycle_sp', value) + self._duty_cycle_sp = self.set_attr_int(self._duty_cycle_sp, "duty_cycle_sp", value) @property def polarity(self): """ Sets the polarity of the motor. Valid values are ``normal`` and ``inversed``. """ - self._polarity, value = self.get_attr_string(self._polarity, 'polarity') + self._polarity, value = self.get_attr_string(self._polarity, "polarity") return value @polarity.setter def polarity(self, value): - self._polarity = self.set_attr_string(self._polarity, 'polarity', value) + self._polarity = self.set_attr_string(self._polarity, "polarity", value) @property def ramp_down_sp(self): @@ -1286,12 +1318,12 @@ def ramp_down_sp(self): Sets the time in milliseconds that it take the motor to ramp down from 100% to 0%. Valid values are 0 to 10000 (10 seconds). Default is 0. """ - self._ramp_down_sp, value = self.get_attr_int(self._ramp_down_sp, 'ramp_down_sp') + self._ramp_down_sp, value = self.get_attr_int(self._ramp_down_sp, "ramp_down_sp") return value @ramp_down_sp.setter def ramp_down_sp(self, value): - self._ramp_down_sp = self.set_attr_int(self._ramp_down_sp, 'ramp_down_sp', value) + self._ramp_down_sp = self.set_attr_int(self._ramp_down_sp, "ramp_down_sp", value) @property def ramp_up_sp(self): @@ -1299,12 +1331,12 @@ def ramp_up_sp(self): Sets the time in milliseconds that it take the motor to up ramp from 0% to 100%. Valid values are 0 to 10000 (10 seconds). Default is 0. """ - self._ramp_up_sp, value = self.get_attr_int(self._ramp_up_sp, 'ramp_up_sp') + self._ramp_up_sp, value = self.get_attr_int(self._ramp_up_sp, "ramp_up_sp") return value @ramp_up_sp.setter def ramp_up_sp(self, value): - self._ramp_up_sp = self.set_attr_int(self._ramp_up_sp, 'ramp_up_sp', value) + self._ramp_up_sp = self.set_attr_int(self._ramp_up_sp, "ramp_up_sp", value) @property def state(self): @@ -1314,7 +1346,7 @@ def state(self): powered. ``ramping`` indicates that the motor has not yet reached the ``duty_cycle_sp``. """ - self._state, value = self.get_attr_set(self._state, 'state') + self._state, value = self.get_attr_set(self._state, "state") return value @property @@ -1327,7 +1359,7 @@ def stop_action(self): @stop_action.setter def stop_action(self, value): - self._stop_action = self.set_attr_string(self._stop_action, 'stop_action', value) + self._stop_action = self.set_attr_string(self._stop_action, "stop_action", value) @property def stop_actions(self): @@ -1335,7 +1367,7 @@ def stop_actions(self): Gets a list of stop actions. Valid values are ``coast`` and ``brake``. """ - self._stop_actions, value = self.get_attr_set(self._stop_actions, 'stop_actions') + self._stop_actions, value = self.get_attr_set(self._stop_actions, "stop_actions") return value @property @@ -1345,45 +1377,45 @@ def time_sp(self): ``run-timed`` command. Reading returns the current value. Units are in milliseconds. """ - self._time_sp, value = self.get_attr_int(self._time_sp, 'time_sp') + self._time_sp, value = self.get_attr_int(self._time_sp, "time_sp") return value @time_sp.setter def time_sp(self, value): - self._time_sp = self.set_attr_int(self._time_sp, 'time_sp', value) + self._time_sp = self.set_attr_int(self._time_sp, "time_sp", value) #: Run the motor until another command is sent. - COMMAND_RUN_FOREVER = 'run-forever' + COMMAND_RUN_FOREVER = "run-forever" #: Run the motor for the amount of time specified in ``time_sp`` #: and then stop the motor using the action specified by ``stop_action``. - COMMAND_RUN_TIMED = 'run-timed' + COMMAND_RUN_TIMED = "run-timed" #: Run the motor at the duty cycle specified by ``duty_cycle_sp``. #: Unlike other run commands, changing ``duty_cycle_sp`` while running *will* #: take effect immediately. - COMMAND_RUN_DIRECT = 'run-direct' + COMMAND_RUN_DIRECT = "run-direct" #: Stop any of the run commands before they are complete using the #: action specified by ``stop_action``. - COMMAND_STOP = 'stop' + COMMAND_STOP = "stop" #: With ``normal`` polarity, a positive duty cycle will #: cause the motor to rotate clockwise. - POLARITY_NORMAL = 'normal' + POLARITY_NORMAL = "normal" #: With ``inversed`` polarity, a positive duty cycle will #: cause the motor to rotate counter-clockwise. - POLARITY_INVERSED = 'inversed' + POLARITY_INVERSED = "inversed" #: Power will be removed from the motor and it will freely coast to a stop. - STOP_ACTION_COAST = 'coast' + STOP_ACTION_COAST = "coast" #: Power will be removed from the motor and a passive electrical load will #: be placed on the motor. This is usually done by shorting the motor terminals #: together. This load will absorb the energy from the rotation of the motors and #: cause the motor to stop more quickly than coasting. - STOP_ACTION_BRAKE = 'brake' + STOP_ACTION_BRAKE = "brake" def run_forever(self, **kwargs): """ @@ -1429,25 +1461,25 @@ class ServoMotor(Device): servo motors. """ - SYSTEM_CLASS_NAME = 'servo-motor' - SYSTEM_DEVICE_NAME_CONVENTION = 'motor*' + SYSTEM_CLASS_NAME = "servo-motor" + SYSTEM_DEVICE_NAME_CONVENTION = "motor*" __slots__ = [ - '_address', - '_command', - '_driver_name', - '_max_pulse_sp', - '_mid_pulse_sp', - '_min_pulse_sp', - '_polarity', - '_position_sp', - '_rate_sp', - '_state', + "_address", + "_command", + "_driver_name", + "_max_pulse_sp", + "_mid_pulse_sp", + "_min_pulse_sp", + "_polarity", + "_position_sp", + "_rate_sp", + "_state", ] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): if address is not None: - kwargs['address'] = address + kwargs["address"] = address super(ServoMotor, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._address = None @@ -1466,7 +1498,7 @@ def address(self): """ Returns the name of the port that this motor is connected to. """ - self._address, value = self.get_attr_string(self._address, 'address') + self._address, value = self.get_attr_string(self._address, "address") return value @property @@ -1480,7 +1512,7 @@ def command(self): @command.setter def command(self, value): - self._command = self.set_attr_string(self._command, 'command', value) + self._command = self.set_attr_string(self._command, "command", value) @property def driver_name(self): @@ -1488,7 +1520,7 @@ def driver_name(self): Returns the name of the motor driver that loaded this device. See the list of [supported devices] for a list of drivers. """ - self._driver_name, value = self.get_attr_string(self._driver_name, 'driver_name') + self._driver_name, value = self.get_attr_string(self._driver_name, "driver_name") return value @property @@ -1499,12 +1531,12 @@ def max_pulse_sp(self): Valid values are 2300 to 2700. You must write to the position_sp attribute for changes to this attribute to take effect. """ - self._max_pulse_sp, value = self.get_attr_int(self._max_pulse_sp, 'max_pulse_sp') + self._max_pulse_sp, value = self.get_attr_int(self._max_pulse_sp, "max_pulse_sp") return value @max_pulse_sp.setter def max_pulse_sp(self, value): - self._max_pulse_sp = self.set_attr_int(self._max_pulse_sp, 'max_pulse_sp', value) + self._max_pulse_sp = self.set_attr_int(self._max_pulse_sp, "max_pulse_sp", value) @property def mid_pulse_sp(self): @@ -1516,12 +1548,12 @@ def mid_pulse_sp(self): where the motor does not turn. You must write to the position_sp attribute for changes to this attribute to take effect. """ - self._mid_pulse_sp, value = self.get_attr_int(self._mid_pulse_sp, 'mid_pulse_sp') + self._mid_pulse_sp, value = self.get_attr_int(self._mid_pulse_sp, "mid_pulse_sp") return value @mid_pulse_sp.setter def mid_pulse_sp(self, value): - self._mid_pulse_sp = self.set_attr_int(self._mid_pulse_sp, 'mid_pulse_sp', value) + self._mid_pulse_sp = self.set_attr_int(self._mid_pulse_sp, "mid_pulse_sp", value) @property def min_pulse_sp(self): @@ -1531,12 +1563,12 @@ def min_pulse_sp(self): is 600. Valid values are 300 to 700. You must write to the position_sp attribute for changes to this attribute to take effect. """ - self._min_pulse_sp, value = self.get_attr_int(self._min_pulse_sp, 'min_pulse_sp') + self._min_pulse_sp, value = self.get_attr_int(self._min_pulse_sp, "min_pulse_sp") return value @min_pulse_sp.setter def min_pulse_sp(self, value): - self._min_pulse_sp = self.set_attr_int(self._min_pulse_sp, 'min_pulse_sp', value) + self._min_pulse_sp = self.set_attr_int(self._min_pulse_sp, "min_pulse_sp", value) @property def polarity(self): @@ -1546,12 +1578,12 @@ def polarity(self): inversed. i.e ``-100`` will correspond to ``max_pulse_sp``, and ``100`` will correspond to ``min_pulse_sp``. """ - self._polarity, value = self.get_attr_string(self._polarity, 'polarity') + self._polarity, value = self.get_attr_string(self._polarity, "polarity") return value @polarity.setter def polarity(self, value): - self._polarity = self.set_attr_string(self._polarity, 'polarity', value) + self._polarity = self.set_attr_string(self._polarity, "polarity", value) @property def position_sp(self): @@ -1561,12 +1593,12 @@ def position_sp(self): are -100 to 100 (-100% to 100%) where ``-100`` corresponds to ``min_pulse_sp``, ``0`` corresponds to ``mid_pulse_sp`` and ``100`` corresponds to ``max_pulse_sp``. """ - self._position_sp, value = self.get_attr_int(self._position_sp, 'position_sp') + self._position_sp, value = self.get_attr_int(self._position_sp, "position_sp") return value @position_sp.setter def position_sp(self, value): - self._position_sp = self.set_attr_int(self._position_sp, 'position_sp', value) + self._position_sp = self.set_attr_int(self._position_sp, "position_sp", value) @property def rate_sp(self): @@ -1578,12 +1610,12 @@ def rate_sp(self): case reading and writing will fail with ``-EOPNOTSUPP``. In continuous rotation servos, this value will affect the rate_sp at which the speed ramps up or down. """ - self._rate_sp, value = self.get_attr_int(self._rate_sp, 'rate_sp') + self._rate_sp, value = self.get_attr_int(self._rate_sp, "rate_sp") return value @rate_sp.setter def rate_sp(self, value): - self._rate_sp = self.set_attr_int(self._rate_sp, 'rate_sp', value) + self._rate_sp = self.set_attr_int(self._rate_sp, "rate_sp", value) @property def state(self): @@ -1592,22 +1624,22 @@ def state(self): Possible values are: * ``running``: Indicates that the motor is powered. """ - self._state, value = self.get_attr_set(self._state, 'state') + self._state, value = self.get_attr_set(self._state, "state") return value #: Drive servo to the position set in the ``position_sp`` attribute. - COMMAND_RUN = 'run' + COMMAND_RUN = "run" #: Remove power from the motor. - COMMAND_FLOAT = 'float' + COMMAND_FLOAT = "float" #: With ``normal`` polarity, a positive duty cycle will #: cause the motor to rotate clockwise. - POLARITY_NORMAL = 'normal' + POLARITY_NORMAL = "normal" #: With ``inversed`` polarity, a positive duty cycle will #: cause the motor to rotate counter-clockwise. - POLARITY_INVERSED = 'inversed' + POLARITY_INVERSED = "inversed" def run(self, **kwargs): """ @@ -1627,7 +1659,6 @@ def float(self, **kwargs): class MotorSet(object): - def __init__(self, motor_specs, desc=None): """ motor_specs is a dictionary such as @@ -1652,58 +1683,60 @@ def __str__(self): return self.__class__.__name__ def set_args(self, **kwargs): - motors = kwargs.get('motors', self.motors.values()) + motors = kwargs.get("motors", self.motors.values()) for motor in motors: for key in kwargs: - if key != 'motors': + if key != "motors": try: setattr(motor, key, kwargs[key]) except AttributeError as e: - #log.error("%s %s cannot set %s to %s" % (self, motor, key, kwargs[key])) + # log.error("%s %s cannot set %s to %s" % (self, motor, key, kwargs[key])) raise e def set_polarity(self, polarity, motors=None): valid_choices = (LargeMotor.POLARITY_NORMAL, LargeMotor.POLARITY_INVERSED) - assert polarity in valid_choices,\ - "%s is an invalid polarity choice, must be %s" % (polarity, ', '.join(valid_choices)) + assert polarity in valid_choices, "%s is an invalid polarity choice, must be %s" % ( + polarity, + ", ".join(valid_choices), + ) motors = motors if motors is not None else self.motors.values() for motor in motors: motor.polarity = polarity def _run_command(self, **kwargs): - motors = kwargs.get('motors', self.motors.values()) + motors = kwargs.get("motors", self.motors.values()) for motor in motors: for key in kwargs: - if key not in ('motors', 'commands'): - #log.debug("%s: %s set %s to %s" % (self, motor, key, kwargs[key])) + if key not in ("motors", "commands"): + # log.debug("%s: %s set %s to %s" % (self, motor, key, kwargs[key])) setattr(motor, key, kwargs[key]) for motor in motors: - motor.command = kwargs['command'] - #log.debug("%s: %s command %s" % (self, motor, kwargs['command'])) + motor.command = kwargs["command"] + # log.debug("%s: %s command %s" % (self, motor, kwargs['command'])) def run_forever(self, **kwargs): - kwargs['command'] = LargeMotor.COMMAND_RUN_FOREVER + kwargs["command"] = LargeMotor.COMMAND_RUN_FOREVER self._run_command(**kwargs) def run_to_abs_pos(self, **kwargs): - kwargs['command'] = LargeMotor.COMMAND_RUN_TO_ABS_POS + kwargs["command"] = LargeMotor.COMMAND_RUN_TO_ABS_POS self._run_command(**kwargs) def run_to_rel_pos(self, **kwargs): - kwargs['command'] = LargeMotor.COMMAND_RUN_TO_REL_POS + kwargs["command"] = LargeMotor.COMMAND_RUN_TO_REL_POS self._run_command(**kwargs) def run_timed(self, **kwargs): - kwargs['command'] = LargeMotor.COMMAND_RUN_TIMED + kwargs["command"] = LargeMotor.COMMAND_RUN_TIMED self._run_command(**kwargs) def run_direct(self, **kwargs): - kwargs['command'] = LargeMotor.COMMAND_RUN_DIRECT + kwargs["command"] = LargeMotor.COMMAND_RUN_DIRECT self._run_command(**kwargs) def reset(self, motors=None): @@ -1786,7 +1819,7 @@ def wait_while(self, s, timeout=None, motors=None): motor.wait_while(s, timeout) def _block(self): - self.wait_until('running', timeout=WAIT_RUNNING_TIMEOUT) + self.wait_until("running", timeout=WAIT_RUNNING_TIMEOUT) self.wait_until_not_moving() @@ -1796,6 +1829,7 @@ class FollowGyroAngleErrorTooFast(Exception): Raised when a gyro following robot has been asked to follow an angle at an unrealistic speed """ + pass @@ -1804,6 +1838,7 @@ class LineFollowErrorLostLine(Exception): """ Raised when a line following robot has lost the line """ + pass @@ -1812,6 +1847,7 @@ class LineFollowErrorTooFast(Exception): Raised when a line following robot has been asked to follow a line at an unrealistic speed """ + pass @@ -1828,7 +1864,7 @@ def follow_for_ms(tank, ms): ``tank``: the MoveTank object that is following a line ``ms`` : the number of milliseconds to follow the line """ - if not hasattr(tank, 'stopwatch') or tank.stopwatch is None: + if not hasattr(tank, "stopwatch") or tank.stopwatch is None: tank.stopwatch = StopWatch() tank.stopwatch.start() @@ -1853,10 +1889,7 @@ class MoveTank(MotorSet): """ def __init__(self, left_motor_port, right_motor_port, desc=None, motor_class=LargeMotor): - motor_specs = { - left_motor_port : motor_class, - right_motor_port : motor_class, - } + motor_specs = {left_motor_port: motor_class, right_motor_port: motor_class} MotorSet.__init__(self, motor_specs, desc) self.left_motor = self.motors[left_motor_port] @@ -1867,7 +1900,7 @@ def __init__(self, left_motor_port, right_motor_port, desc=None, motor_class=Lar @property def cs(self): return self._cs - + @cs.setter def cs(self, cs): self._cs = cs @@ -1876,7 +1909,7 @@ def cs(self, cs): @property def gyro(self): return self._gyro - + @gyro.setter def gyro(self, gyro): self._gyro = gyro @@ -1885,10 +1918,7 @@ def _unpack_speeds_to_native_units(self, left_speed, right_speed): left_speed = self.left_motor._speed_native_units(left_speed, "left_speed") right_speed = self.right_motor._speed_native_units(right_speed, "right_speed") - return ( - left_speed, - right_speed - ) + return (left_speed, right_speed) def on_for_degrees(self, left_speed, right_speed, degrees, brake=True, block=True): """ @@ -1900,7 +1930,9 @@ def on_for_degrees(self, left_speed, right_speed, degrees, brake=True, block=Tru ``degrees`` while the motor on the inside will have its requested distance calculated according to the expected turn. """ - (left_speed_native_units, right_speed_native_units) = self._unpack_speeds_to_native_units(left_speed, right_speed) + (left_speed_native_units, right_speed_native_units) = self._unpack_speeds_to_native_units( + left_speed, right_speed + ) # proof of the following distance calculation: consider the circle formed by each wheel's path # v_l = d_l/t, v_r = d_r/t @@ -1964,7 +1996,9 @@ def on_for_seconds(self, left_speed, right_speed, seconds, brake=True, block=Tru if seconds < 0: raise ValueError("seconds is negative ({})".format(seconds)) - (left_speed_native_units, right_speed_native_units) = self._unpack_speeds_to_native_units(left_speed, right_speed) + (left_speed_native_units, right_speed_native_units) = self._unpack_speeds_to_native_units( + left_speed, right_speed + ) # Set all parameters self.left_motor.speed_sp = int(round(left_speed_native_units)) @@ -1974,8 +2008,7 @@ def on_for_seconds(self, left_speed, right_speed, seconds, brake=True, block=Tru self.right_motor.time_sp = int(seconds * 1000) self.right_motor._set_brake(brake) - log.debug("%s: on_for_seconds %ss at left-speed %s, right-speed %s" % - (self, seconds, left_speed, right_speed)) + log.debug("%s: on_for_seconds %ss at left-speed %s, right-speed %s" % (self, seconds, left_speed, right_speed)) # Start the motors self.left_motor.run_timed() @@ -1989,7 +2022,9 @@ def on(self, left_speed, right_speed): Start rotating the motors according to ``left_speed`` and ``right_speed`` forever. Speeds can be percentages or any SpeedValue implementation. """ - (left_speed_native_units, right_speed_native_units) = self._unpack_speeds_to_native_units(left_speed, right_speed) + (left_speed_native_units, right_speed_native_units) = self._unpack_speeds_to_native_units( + left_speed, right_speed + ) # Set all parameters self.left_motor.speed_sp = int(round(left_speed_native_units)) @@ -2004,17 +2039,20 @@ def on(self, left_speed, right_speed): self.left_motor.run_forever() self.right_motor.run_forever() - def follow_line(self, - kp, ki, kd, - speed, - target_light_intensity=None, - follow_left_edge=True, - white=60, - off_line_count_max=20, - sleep_time=0.01, - follow_for=follow_for_forever, - **kwargs - ): + def follow_line( + self, + kp, + ki, + kd, + speed, + target_light_intensity=None, + follow_left_edge=True, + white=60, + off_line_count_max=20, + sleep_time=0.01, + follow_for=follow_for_forever, + **kwargs + ): """ PID line follower @@ -2097,12 +2135,12 @@ def follow_line(self, right_speed = SpeedNativeUnits(speed_native_units + turn_native_units) if left_speed > MAX_SPEED: - log.info("%s: left_speed %s is greater than MAX_SPEED %s" % (self, left_speed, MAX_SPEED)) + log.info("%s: left_speed %s is greater than MAX_SPEED %s" % (self, left_speed, MAX_SPEED)) self.stop() raise LineFollowErrorTooFast("The robot is moving too fast to follow the line") if right_speed > MAX_SPEED: - log.info("%s: right_speed %s is greater than MAX_SPEED %s" % (self, right_speed, MAX_SPEED)) + log.info("%s: right_speed %s is greater than MAX_SPEED %s" % (self, right_speed, MAX_SPEED)) self.stop() raise LineFollowErrorTooFast("The robot is moving too fast to follow the line") @@ -2132,18 +2170,13 @@ def calibrate_gyro(self): assert self._gyro, "GyroSensor must be defined" for x in range(2): - self._gyro.mode = 'GYRO-RATE' - self._gyro.mode = 'GYRO-ANG' + self._gyro.mode = "GYRO-RATE" + self._gyro.mode = "GYRO-ANG" time.sleep(0.5) - def follow_gyro_angle(self, - kp, ki, kd, - speed, - target_angle=0, - sleep_time=0.01, - follow_for=follow_for_forever, - **kwargs - ): + def follow_gyro_angle( + self, kp, ki, kd, speed, target_angle=0, sleep_time=0.01, follow_for=follow_for_forever, **kwargs + ): """ PID gyro angle follower @@ -2217,18 +2250,14 @@ def follow_gyro_angle(self, right_speed = SpeedNativeUnits(speed_native_units + turn_native_units) if abs(left_speed) > MAX_SPEED: - log.info("%s: left_speed %s is greater than MAX_SPEED %s" % - (self, left_speed, MAX_SPEED)) + log.info("%s: left_speed %s is greater than MAX_SPEED %s" % (self, left_speed, MAX_SPEED)) self.stop() - raise FollowGyroAngleErrorTooFast( - "The robot is moving too fast to follow the angle") + raise FollowGyroAngleErrorTooFast("The robot is moving too fast to follow the angle") if abs(right_speed) > MAX_SPEED: - log.info("%s: right_speed %s is greater than MAX_SPEED %s" % - (self, right_speed, MAX_SPEED)) + log.info("%s: right_speed %s is greater than MAX_SPEED %s" % (self, right_speed, MAX_SPEED)) self.stop() - raise FollowGyroAngleErrorTooFast( - "The robot is moving too fast to follow the angle") + raise FollowGyroAngleErrorTooFast("The robot is moving too fast to follow the angle") if sleep_time: time.sleep(sleep_time) @@ -2237,12 +2266,7 @@ def follow_gyro_angle(self, self.stop() - def turn_to_angle_gyro(self, - speed, - target_angle=0, - wiggle_room=2, - sleep_time=0.01 - ): + def turn_to_angle_gyro(self, speed, target_angle=0, wiggle_room=2, sleep_time=0.01): """ Pivot Turn @@ -2289,7 +2313,7 @@ def turn_to_angle_gyro(self, if abs(current_angle - target_angle) <= wiggle_room: target_reached = True self.stop() - elif (current_angle > target_angle): + elif current_angle > target_angle: left_speed = SpeedNativeUnits(-1 * speed_native_units) right_speed = SpeedNativeUnits(speed_native_units) else: @@ -2301,6 +2325,7 @@ def turn_to_angle_gyro(self, self.on(left_speed, right_speed) + class MoveSteering(MoveTank): """ Controls a pair of motors simultaneously, via a single "steering" value and a speed. @@ -2320,6 +2345,7 @@ class MoveSteering(MoveTank): # drive in a turn for 10 rotations of the outer motor steering_drive.on_for_rotations(-20, SpeedPercent(75), 10) """ + def on_for_rotations(self, steering, speed, rotations, brake=True, block=True): """ Rotate the motors according to the provided ``steering``. @@ -2327,7 +2353,9 @@ def on_for_rotations(self, steering, speed, rotations, brake=True, block=True): The distance each motor will travel follows the rules of :meth:`MoveTank.on_for_rotations`. """ (left_speed, right_speed) = self.get_speed_steering(steering, speed) - MoveTank.on_for_rotations(self, SpeedNativeUnits(left_speed), SpeedNativeUnits(right_speed), rotations, brake, block) + MoveTank.on_for_rotations( + self, SpeedNativeUnits(left_speed), SpeedNativeUnits(right_speed), rotations, brake, block + ) def on_for_degrees(self, steering, speed, degrees, brake=True, block=True): """ @@ -2336,14 +2364,18 @@ def on_for_degrees(self, steering, speed, degrees, brake=True, block=True): The distance each motor will travel follows the rules of :meth:`MoveTank.on_for_degrees`. """ (left_speed, right_speed) = self.get_speed_steering(steering, speed) - MoveTank.on_for_degrees(self, SpeedNativeUnits(left_speed), SpeedNativeUnits(right_speed), degrees, brake, block) + MoveTank.on_for_degrees( + self, SpeedNativeUnits(left_speed), SpeedNativeUnits(right_speed), degrees, brake, block + ) def on_for_seconds(self, steering, speed, seconds, brake=True, block=True): """ Rotate the motors according to the provided ``steering`` for ``seconds``. """ (left_speed, right_speed) = self.get_speed_steering(steering, speed) - MoveTank.on_for_seconds(self, SpeedNativeUnits(left_speed), SpeedNativeUnits(right_speed), seconds, brake, block) + MoveTank.on_for_seconds( + self, SpeedNativeUnits(left_speed), SpeedNativeUnits(right_speed), seconds, brake, block + ) def on(self, steering, speed): """ @@ -2371,8 +2403,9 @@ def get_speed_steering(self, steering, speed): automatically. """ - assert steering >= -100 and steering <= 100,\ - "{} is an invalid steering, must be between -100 and 100 (inclusive)".format(steering) + assert ( + steering >= -100 and steering <= 100 + ), "{} is an invalid steering, must be between -100 and 100 (inclusive)".format(steering) # We don't have a good way to make this generic for the pair... so we # assume that the left motor's speed stats are the same as the right @@ -2465,9 +2498,9 @@ class MoveDifferential(MoveTank): mdiff.odometry_stop() """ - def __init__(self, left_motor_port, right_motor_port, - wheel_class, wheel_distance_mm, - desc=None, motor_class=LargeMotor): + def __init__( + self, left_motor_port, right_motor_port, wheel_class, wheel_distance_mm, desc=None, motor_class=LargeMotor + ): MoveTank.__init__(self, left_motor_port, right_motor_port, desc, motor_class) self.wheel = wheel_class() @@ -2500,8 +2533,11 @@ def _on_arc(self, speed, radius_mm, distance_mm, brake, block, arc_right): """ if radius_mm < self.min_circle_radius_mm: - raise ValueError("{}: radius_mm {} is less than min_circle_radius_mm {}" .format( - self, radius_mm, self.min_circle_radius_mm)) + raise ValueError( + "{}: radius_mm {} is less than min_circle_radius_mm {}".format( + self, radius_mm, self.min_circle_radius_mm + ) + ) # The circle formed at the halfway point between the two wheels is the # circle that must have a radius of radius_mm @@ -2513,18 +2549,26 @@ def _on_arc(self, speed, radius_mm, distance_mm, brake, block, arc_right): # The left wheel is making the larger circle and will move at 'speed' # The right wheel is making a smaller circle so its speed will be a fraction of the left motor's speed left_speed = speed - right_speed = float(circle_inner_mm/circle_outer_mm) * left_speed + right_speed = float(circle_inner_mm / circle_outer_mm) * left_speed else: # The right wheel is making the larger circle and will move at 'speed' # The left wheel is making a smaller circle so its speed will be a fraction of the right motor's speed right_speed = speed - left_speed = float(circle_inner_mm/circle_outer_mm) * right_speed - - log.debug("%s: arc %s, radius %s, distance %s, left-speed %s, right-speed %s, circle_outer_mm %s, circle_middle_mm %s, circle_inner_mm %s" % - (self, "right" if arc_right else "left", - radius_mm, distance_mm, left_speed, right_speed, - circle_outer_mm, circle_middle_mm, circle_inner_mm + left_speed = float(circle_inner_mm / circle_outer_mm) * right_speed + + log.debug( + "%s: arc %s, radius %s, distance %s, left-speed %s, right-speed %s, circle_outer_mm %s, circle_middle_mm %s, circle_inner_mm %s" + % ( + self, + "right" if arc_right else "left", + radius_mm, + distance_mm, + left_speed, + right_speed, + circle_outer_mm, + circle_middle_mm, + circle_inner_mm, ) ) @@ -2540,10 +2584,15 @@ def _on_arc(self, speed, radius_mm, distance_mm, brake, block, arc_right): outer_wheel_rotations = float(circle_outer_final_mm / self.wheel.circumference_mm) outer_wheel_degrees = outer_wheel_rotations * 360 - log.debug("%s: arc %s, circle_middle_percentage %s, circle_outer_final_mm %s, outer_wheel_rotations %s, outer_wheel_degrees %s" % - (self, "right" if arc_right else "left", - circle_middle_percentage, circle_outer_final_mm, - outer_wheel_rotations, outer_wheel_degrees + log.debug( + "%s: arc %s, circle_middle_percentage %s, circle_outer_final_mm %s, outer_wheel_rotations %s, outer_wheel_degrees %s" + % ( + self, + "right" if arc_right else "left", + circle_middle_percentage, + circle_outer_final_mm, + outer_wheel_rotations, + outer_wheel_degrees, ) ) @@ -2571,10 +2620,12 @@ def _turn(self, speed, degrees, brake=True, block=True): distance_mm = (abs(degrees) / 360) * self.circumference_mm # The number of rotations to move distance_mm - rotations = distance_mm/self.wheel.circumference_mm + rotations = distance_mm / self.wheel.circumference_mm - log.debug("%s: turn() degrees %s, distance_mm %s, rotations %s, degrees %s" % - (self, degrees, distance_mm, rotations, degrees)) + log.debug( + "%s: turn() degrees %s, distance_mm %s, rotations %s, degrees %s" + % (self, degrees, distance_mm, rotations, degrees) + ) # If degrees is positive rotate clockwise if degrees > 0: @@ -2598,12 +2649,9 @@ def turn_left(self, speed, degrees, brake=True, block=True): self._turn(speed, abs(degrees) * -1, brake, block) def odometry_coordinates_log(self): - log.debug("%s: odometry angle %s at (%d, %d)" % - (self, math.degrees(self.theta), self.x_pos_mm, self.y_pos_mm)) + log.debug("%s: odometry angle %s at (%d, %d)" % (self, math.degrees(self.theta), self.x_pos_mm, self.y_pos_mm)) - def odometry_start(self, theta_degrees_start=90.0, - x_pos_start=0.0, y_pos_start=0.0, - sleep_time=0.005): # 5ms + def odometry_start(self, theta_degrees_start=90.0, x_pos_start=0.0, y_pos_start=0.0, sleep_time=0.005): # 5ms """ Ported from: http://seattlerobotics.org/encoder/200610/Article3/IMU%20Odometry,%20by%20David%20Anderson.htm @@ -2661,7 +2709,7 @@ def _odometry_monitor(): self.theta += (right_mm - left_mm) / self.wheel_distance_mm # and clip the rotation to plus or minus 360 degrees - self.theta -= float(int(self.theta/TWO_PI) * TWO_PI) + self.theta -= float(int(self.theta / TWO_PI) * TWO_PI) # now calculate and accumulate our position in mm self.x_pos_mm += mm * math.cos(self.theta) @@ -2714,8 +2762,10 @@ def turn_to_angle(self, speed, angle_target_degrees, brake=True, block=True): angle_delta = 360 - angle_delta turn_right = not turn_right - log.debug("%s: turn_to_angle %s, current angle %s, delta %s, turn_right %s" % - (self, angle_target_degrees, angle_current_degrees, angle_delta, turn_right)) + log.debug( + "%s: turn_to_angle %s, current angle %s, delta %s, turn_right %s" + % (self, angle_target_degrees, angle_current_degrees, angle_delta, turn_right) + ) self.odometry_coordinates_log() if turn_right: @@ -2732,7 +2782,7 @@ def on_to_coordinates(self, speed, x_target_mm, y_target_mm, brake=True, block=T assert self.odometry_thread_id, "odometry_start() must be called to track robot coordinates" # stop moving - self.off(brake='hold') + self.off(brake="hold") # rotate in place so we are pointed straight at our target x_delta = x_target_mm - self.x_pos_mm @@ -2792,22 +2842,20 @@ def on(self, x, y, radius=100.0): left_speed_percentage = (init_left_speed_percentage * vector_length) / radius right_speed_percentage = (init_right_speed_percentage * vector_length) / radius - # log.debug(""" - # x, y : %s, %s - # radius : %s - # angle : %s - # vector length : %s - # init left_speed_percentage : %s - # init right_speed_percentage : %s - # final left_speed_percentage : %s - # final right_speed_percentage : %s - # """ % (x, y, radius, angle, vector_length, - # init_left_speed_percentage, init_right_speed_percentage, - # left_speed_percentage, right_speed_percentage)) - - MoveTank.on(self, - SpeedPercent(left_speed_percentage), - SpeedPercent(right_speed_percentage)) + # log.debug(""" + # x, y : %s, %s + # radius : %s + # angle : %s + # vector length : %s + # init left_speed_percentage : %s + # init right_speed_percentage : %s + # final left_speed_percentage : %s + # final right_speed_percentage : %s + # """ % (x, y, radius, angle, vector_length, + # init_left_speed_percentage, init_right_speed_percentage, + # left_speed_percentage, right_speed_percentage)) + + MoveTank.on(self, SpeedPercent(left_speed_percentage), SpeedPercent(right_speed_percentage)) @staticmethod def angle_to_speed_percentage(angle): @@ -2868,7 +2916,7 @@ def angle_to_speed_percentage(angle): left_speed_percentage = 1 # right motor transitions from -1 to 0 - right_speed_percentage = -1 + (angle/45.0) + right_speed_percentage = -1 + (angle / 45.0) elif 45 < angle <= 90: @@ -2958,6 +3006,8 @@ def angle_to_speed_percentage(angle): right_speed_percentage = -1 * percentage_from_315_to_360 else: - raise Exception('You created a circle with more than 360 degrees ({})...that is quite the trick'.format(angle)) + raise Exception( + "You created a circle with more than 360 degrees ({})...that is quite the trick".format(angle) + ) return (left_speed_percentage * 100, right_speed_percentage * 100) diff --git a/ev3dev2/port.py b/ev3dev2/port.py index 4bb5f3a..e159c6f 100644 --- a/ev3dev2/port.py +++ b/ev3dev2/port.py @@ -26,8 +26,8 @@ import sys from . import Device -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") class LegoPort(Device): @@ -59,21 +59,14 @@ class LegoPort(Device): a specific port. """ - SYSTEM_CLASS_NAME = 'lego-port' - SYSTEM_DEVICE_NAME_CONVENTION = '*' - __slots__ = [ - '_address', - '_driver_name', - '_modes', - '_mode', - '_set_device', - '_status', - ] + SYSTEM_CLASS_NAME = "lego-port" + SYSTEM_DEVICE_NAME_CONVENTION = "*" + __slots__ = ["_address", "_driver_name", "_modes", "_mode", "_set_device", "_status"] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): if address is not None: - kwargs['address'] = address + kwargs["address"] = address super(LegoPort, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._address = None @@ -89,7 +82,7 @@ def address(self): Returns the name of the port. See individual driver documentation for the name that will be returned. """ - self._address, value = self.get_attr_string(self._address, 'address') + self._address, value = self.get_attr_string(self._address, "address") return value @property @@ -98,7 +91,7 @@ def driver_name(self): Returns the name of the driver that loaded this device. You can find the complete list of drivers in the [list of port drivers]. """ - (self._driver_name, value) = self.get_cached_attr_string(self._driver_name, 'driver_name') + (self._driver_name, value) = self.get_cached_attr_string(self._driver_name, "driver_name") return value @property @@ -106,7 +99,7 @@ def modes(self): """ Returns a list of the available modes of the port. """ - (self._modes, value) = self.get_cached_attr_set(self._modes, 'modes') + (self._modes, value) = self.get_cached_attr_set(self._modes, "modes") return value @property @@ -117,12 +110,12 @@ def mode(self): associated with the port will be removed new ones loaded, however this this will depend on the individual driver implementing this class. """ - self._mode, value = self.get_attr_string(self._mode, 'mode') + self._mode, value = self.get_attr_string(self._mode, "mode") return value @mode.setter def mode(self, value): - self._mode = self.set_attr_string(self._mode, 'mode', value) + self._mode = self.set_attr_string(self._mode, "mode", value) @property def set_device(self): @@ -137,7 +130,7 @@ def set_device(self): @set_device.setter def set_device(self, value): - self._set_device = self.set_attr_string(self._set_device, 'set_device', value) + self._set_device = self.set_attr_string(self._set_device, "set_device", value) @property def status(self): @@ -147,5 +140,5 @@ def status(self): such as ``no-device`` or ``error``. See individual port driver documentation for the full list of possible values. """ - self._status, value = self.get_attr_string(self._status, 'status') + self._status, value = self.get_attr_string(self._status, "status") return value diff --git a/ev3dev2/power.py b/ev3dev2/power.py index 6c3e3a8..d48a199 100644 --- a/ev3dev2/power.py +++ b/ev3dev2/power.py @@ -25,8 +25,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") from ev3dev2 import Device @@ -37,21 +37,14 @@ class PowerSupply(Device): Uses the built-in legoev3-battery if none is specified. """ - SYSTEM_CLASS_NAME = 'power_supply' - SYSTEM_DEVICE_NAME_CONVENTION = '*' - __slots__ = [ - '_measured_current', - '_measured_voltage', - '_max_voltage', - '_min_voltage', - '_technology', - '_type', - ] + SYSTEM_CLASS_NAME = "power_supply" + SYSTEM_DEVICE_NAME_CONVENTION = "*" + __slots__ = ["_measured_current", "_measured_voltage", "_max_voltage", "_min_voltage", "_technology", "_type"] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): if address is not None: - kwargs['address'] = address + kwargs["address"] = address super(PowerSupply, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._measured_current = None @@ -66,7 +59,7 @@ def measured_current(self): """ The measured current that the battery is supplying (in microamps) """ - self._measured_current, value = self.get_attr_int(self._measured_current, 'current_now') + self._measured_current, value = self.get_attr_int(self._measured_current, "current_now") return value @property @@ -74,27 +67,27 @@ def measured_voltage(self): """ The measured voltage that the battery is supplying (in microvolts) """ - self._measured_voltage, value = self.get_attr_int(self._measured_voltage, 'voltage_now') + self._measured_voltage, value = self.get_attr_int(self._measured_voltage, "voltage_now") return value @property def max_voltage(self): - self._max_voltage, value = self.get_attr_int(self._max_voltage, 'voltage_max_design') + self._max_voltage, value = self.get_attr_int(self._max_voltage, "voltage_max_design") return value @property def min_voltage(self): - self._min_voltage, value = self.get_attr_int(self._min_voltage, 'voltage_min_design') + self._min_voltage, value = self.get_attr_int(self._min_voltage, "voltage_min_design") return value @property def technology(self): - self._technology, value = self.get_attr_string(self._technology, 'technology') + self._technology, value = self.get_attr_string(self._technology, "technology") return value @property def type(self): - self._type, value = self.get_attr_string(self._type, 'type') + self._type, value = self.get_attr_string(self._type, "type") return value @property diff --git a/ev3dev2/sensor/__init__.py b/ev3dev2/sensor/__init__.py index ffe2b92..385e1d0 100644 --- a/ev3dev2/sensor/__init__.py +++ b/ev3dev2/sensor/__init__.py @@ -25,8 +25,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") import numbers from os.path import abspath @@ -37,25 +37,39 @@ # INPUT ports have platform specific values that we must import platform = get_current_platform() -if platform == 'ev3': +if platform == "ev3": from ev3dev2._platform.ev3 import INPUT_1, INPUT_2, INPUT_3, INPUT_4 -elif platform == 'evb': +elif platform == "evb": from ev3dev2._platform.evb import INPUT_1, INPUT_2, INPUT_3, INPUT_4 -elif platform == 'pistorms': +elif platform == "pistorms": from ev3dev2._platform.pistorms import INPUT_1, INPUT_2, INPUT_3, INPUT_4 -elif platform == 'brickpi': +elif platform == "brickpi": from ev3dev2._platform.brickpi import INPUT_1, INPUT_2, INPUT_3, INPUT_4 -elif platform == 'brickpi3': - from ev3dev2._platform.brickpi3 import INPUT_1, INPUT_2, INPUT_3, INPUT_4, \ - INPUT_5, INPUT_6, INPUT_7, INPUT_8, \ - INPUT_9, INPUT_10, INPUT_11, INPUT_12, \ - INPUT_13, INPUT_14, INPUT_15, INPUT_16 - -elif platform == 'fake': +elif platform == "brickpi3": + from ev3dev2._platform.brickpi3 import ( + INPUT_1, + INPUT_2, + INPUT_3, + INPUT_4, + INPUT_5, + INPUT_6, + INPUT_7, + INPUT_8, + INPUT_9, + INPUT_10, + INPUT_11, + INPUT_12, + INPUT_13, + INPUT_14, + INPUT_15, + INPUT_16, + ) + +elif platform == "fake": from ev3dev2._platform.fake import INPUT_1, INPUT_2, INPUT_3, INPUT_4 else: @@ -68,29 +82,29 @@ class Sensor(Device): sensors available for the EV3. """ - SYSTEM_CLASS_NAME = 'lego-sensor' - SYSTEM_DEVICE_NAME_CONVENTION = 'sensor*' + SYSTEM_CLASS_NAME = "lego-sensor" + SYSTEM_DEVICE_NAME_CONVENTION = "sensor*" __slots__ = [ - '_address', - '_command', - '_commands', - '_decimals', - '_driver_name', - '_mode', - '_modes', - '_num_values', - '_units', - '_value', - '_bin_data_format', - '_bin_data_size', - '_bin_data', - '_mode_scale' + "_address", + "_command", + "_commands", + "_decimals", + "_driver_name", + "_mode", + "_modes", + "_num_values", + "_units", + "_value", + "_bin_data_format", + "_bin_data_size", + "_bin_data", + "_mode_scale", ] def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): if address is not None: - kwargs['address'] = address + kwargs["address"] = address super(Sensor, self).__init__(self.SYSTEM_CLASS_NAME, name_pattern, name_exact, **kwargs) self._address = None @@ -102,7 +116,7 @@ def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, nam self._modes = None self._num_values = None self._units = None - self._value = [None,None,None,None,None,None,None,None] + self._value = [None, None, None, None, None, None, None, None] self._bin_data_format = None self._bin_data_size = None @@ -116,7 +130,7 @@ def _scale(self, mode): if mode in self._mode_scale: scale = self._mode_scale[mode] else: - scale = 10**(-self.decimals) + scale = 10 ** (-self.decimals) self._mode_scale[mode] = scale return scale @@ -127,7 +141,7 @@ def address(self): Returns the name of the port that the sensor is connected to, e.g. ``ev3:in1``. I2C sensors also include the I2C address (decimal), e.g. ``ev3:in1:i2c8``. """ - self._address, value = self.get_attr_string(self._address, 'address') + self._address, value = self.get_attr_string(self._address, "address") return value @property @@ -139,7 +153,7 @@ def command(self): @command.setter def command(self, value): - self._command = self.set_attr_string(self._command, 'command', value) + self._command = self.set_attr_string(self._command, "command", value) @property def commands(self): @@ -147,7 +161,7 @@ def commands(self): Returns a list of the valid commands for the sensor. Returns -EOPNOTSUPP if no commands are supported. """ - (self._commands, value) = self.get_cached_attr_set(self._commands, 'commands') + (self._commands, value) = self.get_cached_attr_set(self._commands, "commands") return value @property @@ -156,7 +170,7 @@ def decimals(self): Returns the number of decimal places for the values in the ``value`` attributes of the current mode. """ - self._decimals, value = self.get_attr_int(self._decimals, 'decimals') + self._decimals, value = self.get_attr_int(self._decimals, "decimals") return value @property @@ -165,7 +179,7 @@ def driver_name(self): Returns the name of the sensor device/driver. See the list of [supported sensors] for a complete list of drivers. """ - (self._driver_name, value) = self.get_cached_attr_string(self._driver_name, 'driver_name') + (self._driver_name, value) = self.get_cached_attr_string(self._driver_name, "driver_name") return value @property @@ -174,19 +188,19 @@ def mode(self): Returns the current mode. Writing one of the values returned by ``modes`` sets the sensor to that mode. """ - self._mode, value = self.get_attr_string(self._mode, 'mode') + self._mode, value = self.get_attr_string(self._mode, "mode") return value @mode.setter def mode(self, value): - self._mode = self.set_attr_string(self._mode, 'mode', value) + self._mode = self.set_attr_string(self._mode, "mode", value) @property def modes(self): """ Returns a list of the valid modes for the sensor. """ - (self._modes, value) = self.get_cached_attr_set(self._modes, 'modes') + (self._modes, value) = self.get_cached_attr_set(self._modes, "modes") return value @property @@ -195,7 +209,7 @@ def num_values(self): Returns the number of ``value`` attributes that will return a valid value for the current mode. """ - self._num_values, value = self.get_attr_int(self._num_values, 'num_values') + self._num_values, value = self.get_attr_int(self._num_values, "num_values") return value @property @@ -204,7 +218,7 @@ def units(self): Returns the units of the measured value for the current mode. May return empty string """ - self._units, value = self.get_attr_string(self._units, 'units') + self._units, value = self.get_attr_string(self._units, "units") return value def value(self, n=0): @@ -216,7 +230,7 @@ def value(self, n=0): """ n = int(n) - self._value[n], value = self.get_attr_int(self._value[n], 'value'+str(n)) + self._value[n], value = self.get_attr_int(self._value[n], "value" + str(n)) return value @property @@ -233,7 +247,7 @@ def bin_data_format(self): - ``s32``: Signed 32-bit integer (int) - ``float``: IEEE 754 32-bit floating point (float) """ - self._bin_data_format, value = self.get_attr_string(self._bin_data_format, 'bin_data_format') + self._bin_data_format, value = self.get_attr_string(self._bin_data_format, "bin_data_format") return value def bin_data(self, fmt=None): @@ -255,30 +269,26 @@ def bin_data(self, fmt=None): """ if self._bin_data_size == None: - self._bin_data_size = { - "u8": 1, - "s8": 1, - "u16": 2, - "s16": 2, - "s16_be": 2, - "s32": 4, - "float": 4 - }.get(self.bin_data_format, 1) * self.num_values + self._bin_data_size = {"u8": 1, "s8": 1, "u16": 2, "s16": 2, "s16_be": 2, "s32": 4, "float": 4}.get( + self.bin_data_format, 1 + ) * self.num_values if None == self._bin_data: - self._bin_data = self._attribute_file_open( 'bin_data' ) + self._bin_data = self._attribute_file_open("bin_data") self._bin_data.seek(0) raw = bytearray(self._bin_data.read(self._bin_data_size)) - if fmt is None: return raw + if fmt is None: + return raw return unpack(fmt, raw) - + def _ensure_mode(self, mode): if self.mode != mode: self.mode = mode + def list_sensors(name_pattern=Sensor.SYSTEM_DEVICE_NAME_CONVENTION, **kwargs): """ This is a generator function that enumerates all sensors that match the @@ -292,9 +302,10 @@ def list_sensors(name_pattern=Sensor.SYSTEM_DEVICE_NAME_CONVENTION, **kwargs): address=['in1', 'in3']. When argument value is a list, then a match against any entry of the list is enough. """ - class_path = abspath(Device.DEVICE_ROOT_PATH + '/' + Sensor.SYSTEM_CLASS_NAME) - return (Sensor(name_pattern=name, name_exact=True) - for name in list_device_names(class_path, name_pattern, **kwargs)) + class_path = abspath(Device.DEVICE_ROOT_PATH + "/" + Sensor.SYSTEM_CLASS_NAME) + return ( + Sensor(name_pattern=name, name_exact=True) for name in list_device_names(class_path, name_pattern, **kwargs) + ) class I2cSensor(Sensor): @@ -303,10 +314,10 @@ class I2cSensor(Sensor): """ SYSTEM_CLASS_NAME = Sensor.SYSTEM_CLASS_NAME - SYSTEM_DEVICE_NAME_CONVENTION = 'sensor*' + SYSTEM_DEVICE_NAME_CONVENTION = "sensor*" def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(I2cSensor, self).__init__(address, name_pattern, name_exact, driver_name=['nxt-i2c-sensor'], **kwargs) + super(I2cSensor, self).__init__(address, name_pattern, name_exact, driver_name=["nxt-i2c-sensor"], **kwargs) self._fw_version = None self._poll_ms = None @@ -316,7 +327,7 @@ def fw_version(self): Returns the firmware version of the sensor if available. Currently only I2C/NXT sensors support this. """ - (self._fw_version, value) = self.get_cached_attr_string(self._fw_version, 'fw_version') + (self._fw_version, value) = self.get_cached_attr_string(self._fw_version, "fw_version") return value @property @@ -327,9 +338,9 @@ def poll_ms(self): coded as 50 msec. Returns -EOPNOTSUPP if changing polling is not supported. Currently only I2C/NXT sensors support changing the polling period. """ - self._poll_ms, value = self.get_attr_int(self._poll_ms, 'poll_ms') + self._poll_ms, value = self.get_attr_int(self._poll_ms, "poll_ms") return value @poll_ms.setter def poll_ms(self, value): - self._poll_ms = self.set_attr_int(self._poll_ms, 'poll_ms', value) + self._poll_ms = self.set_attr_int(self._poll_ms, "poll_ms", value) diff --git a/ev3dev2/sensor/lego.py b/ev3dev2/sensor/lego.py index b5c7301..33ea8c4 100644 --- a/ev3dev2/sensor/lego.py +++ b/ev3dev2/sensor/lego.py @@ -25,8 +25,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") import time from ev3dev2.button import ButtonBase @@ -42,11 +42,13 @@ class TouchSensor(Sensor): SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Button state - MODE_TOUCH = 'TOUCH' + MODE_TOUCH = "TOUCH" MODES = (MODE_TOUCH,) def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(TouchSensor, self).__init__(address, name_pattern, name_exact, driver_name=['lego-ev3-touch', 'lego-nxt-touch'], **kwargs) + super(TouchSensor, self).__init__( + address, name_pattern, name_exact, driver_name=["lego-ev3-touch", "lego-nxt-touch"], **kwargs + ) @property def is_pressed(self): @@ -65,7 +67,7 @@ def _wait(self, wait_for_press, timeout_ms, sleep_ms): tic = time.time() if sleep_ms: - sleep_ms = float(sleep_ms/1000) + sleep_ms = float(sleep_ms / 1000) # The kernel does not supoort POLLPRI or POLLIN for sensors so we have # to drop into a loop and check often @@ -112,25 +114,25 @@ class ColorSensor(Sensor): LEGO EV3 color sensor. """ - __slots__ = ['red_max', 'green_max', 'blue_max'] + __slots__ = ["red_max", "green_max", "blue_max"] SYSTEM_CLASS_NAME = Sensor.SYSTEM_CLASS_NAME SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Reflected light. Red LED on. - MODE_COL_REFLECT = 'COL-REFLECT' + MODE_COL_REFLECT = "COL-REFLECT" #: Ambient light. Blue LEDs on. - MODE_COL_AMBIENT = 'COL-AMBIENT' + MODE_COL_AMBIENT = "COL-AMBIENT" #: Color. All LEDs rapidly cycling, appears white. - MODE_COL_COLOR = 'COL-COLOR' + MODE_COL_COLOR = "COL-COLOR" #: Raw reflected. Red LED on - MODE_REF_RAW = 'REF-RAW' + MODE_REF_RAW = "REF-RAW" #: Raw Color Components. All LEDs rapidly cycling, appears white. - MODE_RGB_RAW = 'RGB-RAW' + MODE_RGB_RAW = "RGB-RAW" #: No color. COLOR_NOCOLOR = 0 @@ -156,27 +158,12 @@ class ColorSensor(Sensor): #: Brown color. COLOR_BROWN = 7 - MODES = ( - MODE_COL_REFLECT, - MODE_COL_AMBIENT, - MODE_COL_COLOR, - MODE_REF_RAW, - MODE_RGB_RAW - ) - - COLORS = ( - 'NoColor', - 'Black', - 'Blue', - 'Green', - 'Yellow', - 'Red', - 'White', - 'Brown', - ) + MODES = (MODE_COL_REFLECT, MODE_COL_AMBIENT, MODE_COL_COLOR, MODE_REF_RAW, MODE_RGB_RAW) + + COLORS = ("NoColor", "Black", "Blue", "Green", "Yellow", "Red", "White", "Brown") def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(ColorSensor, self).__init__(address, name_pattern, name_exact, driver_name='lego-ev3-color', **kwargs) + super(ColorSensor, self).__init__(address, name_pattern, name_exact, driver_name="lego-ev3-color", **kwargs) # See calibrate_white() for more details self.red_max = 300 @@ -265,9 +252,11 @@ def rgb(self): """ (red, green, blue) = self.raw - return (min(int((red * 255) / self.red_max), 255), - min(int((green * 255) / self.green_max), 255), - min(int((blue * 255) / self.blue_max), 255)) + return ( + min(int((red * 255) / self.red_max), 255), + min(int((green * 255) / self.green_max), 255), + min(int((blue * 255) / self.blue_max), 255), + ) @property def lab(self): @@ -294,8 +283,8 @@ def lab(self): Y = (RGB[0] * 0.2126729) + (RGB[1] * 0.7151522) + (RGB[2] * 0.0721750) Z = (RGB[0] * 0.0193339) + (RGB[1] * 0.1191920) + (RGB[2] * 0.9503041) - XYZ[0] = X / 95.047 # ref_X = 95.047 - XYZ[1] = Y / 100.0 # ref_Y = 100.000 + XYZ[0] = X / 95.047 # ref_X = 95.047 + XYZ[1] = Y / 100.0 # ref_Y = 100.000 XYZ[2] = Z / 108.883 # ref_Z = 108.883 for (num, value) in enumerate(XYZ): @@ -332,19 +321,19 @@ def hsv(self): if minc == maxc: return 0.0, 0.0, v - s = (maxc-minc) / maxc - rc = (maxc-r) / (maxc-minc) - gc = (maxc-g) / (maxc-minc) - bc = (maxc-b) / (maxc-minc) + s = (maxc - minc) / maxc + rc = (maxc - r) / (maxc - minc) + gc = (maxc - g) / (maxc - minc) + bc = (maxc - b) / (maxc - minc) if r == maxc: - h = bc-gc + h = bc - gc elif g == maxc: - h = 2.0+rc-bc + h = 2.0 + rc - bc else: - h = 4.0+gc-rc + h = 4.0 + gc - rc - h = (h/6.0) % 1.0 + h = (h / 6.0) % 1.0 return (h, s, v) @@ -359,31 +348,31 @@ def hls(self): (r, g, b) = self.rgb maxc = max(r, g, b) minc = min(r, g, b) - l = (minc+maxc)/2.0 + l = (minc + maxc) / 2.0 if minc == maxc: return 0.0, l, 0.0 if l <= 0.5: - s = (maxc-minc) / (maxc+minc) + s = (maxc - minc) / (maxc + minc) else: - if 2.0-maxc-minc == 0: + if 2.0 - maxc - minc == 0: s = 0 else: - s = (maxc-minc) / (2.0-maxc-minc) + s = (maxc - minc) / (2.0 - maxc - minc) - rc = (maxc-r) / (maxc-minc) - gc = (maxc-g) / (maxc-minc) - bc = (maxc-b) / (maxc-minc) + rc = (maxc - r) / (maxc - minc) + gc = (maxc - g) / (maxc - minc) + bc = (maxc - b) / (maxc - minc) if r == maxc: - h = bc-gc + h = bc - gc elif g == maxc: - h = 2.0+rc-bc + h = 2.0 + rc - bc else: - h = 4.0+gc-rc + h = 4.0 + gc - rc - h = (h/6.0) % 1.0 + h = (h / 6.0) % 1.0 return (h, l, s) @@ -421,30 +410,26 @@ class UltrasonicSensor(Sensor): SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Continuous measurement in centimeters. - MODE_US_DIST_CM = 'US-DIST-CM' + MODE_US_DIST_CM = "US-DIST-CM" #: Continuous measurement in inches. - MODE_US_DIST_IN = 'US-DIST-IN' + MODE_US_DIST_IN = "US-DIST-IN" #: Listen. - MODE_US_LISTEN = 'US-LISTEN' + MODE_US_LISTEN = "US-LISTEN" #: Single measurement in centimeters. - MODE_US_SI_CM = 'US-SI-CM' + MODE_US_SI_CM = "US-SI-CM" #: Single measurement in inches. - MODE_US_SI_IN = 'US-SI-IN' + MODE_US_SI_IN = "US-SI-IN" - MODES = ( - MODE_US_DIST_CM, - MODE_US_DIST_IN, - MODE_US_LISTEN, - MODE_US_SI_CM, - MODE_US_SI_IN, - ) + MODES = (MODE_US_DIST_CM, MODE_US_DIST_IN, MODE_US_LISTEN, MODE_US_SI_CM, MODE_US_SI_IN) def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(UltrasonicSensor, self).__init__(address, name_pattern, name_exact, driver_name=['lego-ev3-us', 'lego-nxt-us'], **kwargs) + super(UltrasonicSensor, self).__init__( + address, name_pattern, name_exact, driver_name=["lego-ev3-us", "lego-nxt-us"], **kwargs + ) @property def distance_centimeters_continuous(self): @@ -458,7 +443,7 @@ def distance_centimeters_continuous(self): Prefer using the equivalent :meth:`UltrasonicSensor.distance_centimeters` property. """ self._ensure_mode(self.MODE_US_DIST_CM) - return self.value(0) * self._scale('US_DIST_CM') + return self.value(0) * self._scale("US_DIST_CM") @property def distance_centimeters_ping(self): @@ -478,7 +463,7 @@ def distance_centimeters_ping(self): # This mode is special; setting the mode causes the sensor to send out # a "ping", but the mode isn't actually changed. self.mode = self.MODE_US_SI_CM - return self.value(0) * self._scale('US_DIST_CM') + return self.value(0) * self._scale("US_DIST_CM") @property def distance_centimeters(self): @@ -502,7 +487,7 @@ def distance_inches_continuous(self): Prefer using the equivalent :meth:`UltrasonicSensor.distance_inches` property. """ self._ensure_mode(self.MODE_US_DIST_IN) - return self.value(0) * self._scale('US_DIST_IN') + return self.value(0) * self._scale("US_DIST_IN") @property def distance_inches_ping(self): @@ -522,7 +507,7 @@ def distance_inches_ping(self): # This mode is special; setting the mode causes the sensor to send out # a "ping", but the mode isn't actually changed. self.mode = self.MODE_US_SI_IN - return self.value(0) * self._scale('US_DIST_IN') + return self.value(0) * self._scale("US_DIST_IN") @property def distance_inches(self): @@ -553,38 +538,30 @@ class GyroSensor(Sensor): SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Angle - MODE_GYRO_ANG = 'GYRO-ANG' + MODE_GYRO_ANG = "GYRO-ANG" #: Rotational speed - MODE_GYRO_RATE = 'GYRO-RATE' + MODE_GYRO_RATE = "GYRO-RATE" #: Raw sensor value - MODE_GYRO_FAS = 'GYRO-FAS' + MODE_GYRO_FAS = "GYRO-FAS" #: Angle and rotational speed - MODE_GYRO_G_A = 'GYRO-G&A' + MODE_GYRO_G_A = "GYRO-G&A" #: Calibration ??? - MODE_GYRO_CAL = 'GYRO-CAL' + MODE_GYRO_CAL = "GYRO-CAL" # Newer versions of the Gyro sensor also have an additional second axis # accessible via the TILT-ANG and TILT-RATE modes that is not usable # using the official EV3-G blocks - MODE_TILT_ANG = 'TILT-ANG' - MODE_TILT_RATE = 'TILT-RATE' - - MODES = ( - MODE_GYRO_ANG, - MODE_GYRO_RATE, - MODE_GYRO_FAS, - MODE_GYRO_G_A, - MODE_GYRO_CAL, - MODE_TILT_ANG, - MODE_TILT_RATE, - ) + MODE_TILT_ANG = "TILT-ANG" + MODE_TILT_RATE = "TILT-RATE" + + MODES = (MODE_GYRO_ANG, MODE_GYRO_RATE, MODE_GYRO_FAS, MODE_GYRO_G_A, MODE_GYRO_CAL, MODE_TILT_ANG, MODE_TILT_RATE) def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(GyroSensor, self).__init__(address, name_pattern, name_exact, driver_name='lego-ev3-gyro', **kwargs) + super(GyroSensor, self).__init__(address, name_pattern, name_exact, driver_name="lego-ev3-gyro", **kwargs) self._direct = None @property @@ -631,7 +608,7 @@ def reset(self): PiStorms, or with any sensor multiplexors. """ # 17 comes from inspecting the .vix file of the Gyro sensor block in EV3-G - self._direct = self.set_attr_raw(self._direct, 'direct', b'\x11') + self._direct = self.set_attr_raw(self._direct, "direct", b"\x11") def wait_until_angle_changed_by(self, delta, direction_sensitive=False): """ @@ -643,9 +620,11 @@ def wait_until_angle_changed_by(self, delta, direction_sensitive=False): If ``direction_sensitive`` is False (default) we will wait until angle has changed by ``delta`` in either direction. """ - assert self.mode in (self.MODE_GYRO_G_A, self.MODE_GYRO_ANG, - self.MODE_TILT_ANG),\ - 'Gyro mode should be MODE_GYRO_ANG, MODE_GYRO_G_A or MODE_TILT_ANG' + assert self.mode in ( + self.MODE_GYRO_G_A, + self.MODE_GYRO_ANG, + self.MODE_TILT_ANG, + ), "Gyro mode should be MODE_GYRO_ANG, MODE_GYRO_G_A or MODE_TILT_ANG" start_angle = self.value(0) if direction_sensitive: @@ -670,48 +649,42 @@ class InfraredSensor(Sensor, ButtonBase): SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Proximity - MODE_IR_PROX = 'IR-PROX' + MODE_IR_PROX = "IR-PROX" #: IR Seeker - MODE_IR_SEEK = 'IR-SEEK' + MODE_IR_SEEK = "IR-SEEK" #: IR Remote Control - MODE_IR_REMOTE = 'IR-REMOTE' + MODE_IR_REMOTE = "IR-REMOTE" #: IR Remote Control. State of the buttons is coded in binary - MODE_IR_REM_A = 'IR-REM-A' + MODE_IR_REM_A = "IR-REM-A" #: Calibration ??? - MODE_IR_CAL = 'IR-CAL' + MODE_IR_CAL = "IR-CAL" - MODES = ( - MODE_IR_PROX, - MODE_IR_SEEK, - MODE_IR_REMOTE, - MODE_IR_REM_A, - MODE_IR_CAL - ) + MODES = (MODE_IR_PROX, MODE_IR_SEEK, MODE_IR_REMOTE, MODE_IR_REM_A, MODE_IR_CAL) # The following are all of the various combinations of button presses for # the remote control. The key/index is the number that will be written in # the attribute file to indicate what combination of buttons are currently # pressed. _BUTTON_VALUES = { - 0: [], - 1: ['top_left'], - 2: ['bottom_left'], - 3: ['top_right'], - 4: ['bottom_right'], - 5: ['top_left', 'top_right'], - 6: ['top_left', 'bottom_right'], - 7: ['bottom_left', 'top_right'], - 8: ['bottom_left', 'bottom_right'], - 9: ['beacon'], - 10: ['top_left', 'bottom_left'], - 11: ['top_right', 'bottom_right'] - } - - _BUTTONS = ('top_left', 'bottom_left', 'top_right', 'bottom_right', 'beacon') + 0: [], + 1: ["top_left"], + 2: ["bottom_left"], + 3: ["top_right"], + 4: ["bottom_right"], + 5: ["top_left", "top_right"], + 6: ["top_left", "bottom_right"], + 7: ["bottom_left", "top_right"], + 8: ["bottom_left", "bottom_right"], + 9: ["beacon"], + 10: ["top_left", "bottom_left"], + 11: ["top_right", "bottom_right"], + } + + _BUTTONS = ("top_left", "bottom_left", "top_right", "bottom_right", "beacon") # Button codes for doing rapid check of remote status NO_BUTTON = 0 @@ -772,7 +745,7 @@ class InfraredSensor(Sensor, ButtonBase): on_channel4_beacon = None def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(InfraredSensor, self).__init__(address, name_pattern, name_exact, driver_name='lego-ev3-ir', **kwargs) + super(InfraredSensor, self).__init__(address, name_pattern, name_exact, driver_name="lego-ev3-ir", **kwargs) def _normalize_channel(self, channel): assert channel >= 1 and channel <= 4, "channel is %s, it must be 1, 2, 3, or 4" % channel @@ -819,31 +792,31 @@ def top_left(self, channel=1): """ Checks if ``top_left`` button is pressed. """ - return 'top_left' in self.buttons_pressed(channel) + return "top_left" in self.buttons_pressed(channel) def bottom_left(self, channel=1): """ Checks if ``bottom_left`` button is pressed. """ - return 'bottom_left' in self.buttons_pressed(channel) + return "bottom_left" in self.buttons_pressed(channel) def top_right(self, channel=1): """ Checks if ``top_right`` button is pressed. """ - return 'top_right' in self.buttons_pressed(channel) + return "top_right" in self.buttons_pressed(channel) def bottom_right(self, channel=1): """ Checks if ``bottom_right`` button is pressed. """ - return 'bottom_right' in self.buttons_pressed(channel) + return "bottom_right" in self.buttons_pressed(channel) def beacon(self, channel=1): """ Checks if ``beacon`` button is pressed. """ - return 'beacon' in self.buttons_pressed(channel) + return "beacon" in self.buttons_pressed(channel) def buttons_pressed(self, channel=1): """ @@ -882,7 +855,7 @@ def bottom_right_channel_4_action(state): new_state = [] state_diff = [] - for channel in range(1,5): + for channel in range(1, 5): for button in self.buttons_pressed(channel): new_state.append((button, channel)) @@ -900,7 +873,7 @@ def bottom_right_channel_4_action(state): self._state = new_state for (button, channel) in state_diff: - handler = getattr(self, 'on_channel' + str(channel) + '_' + button ) + handler = getattr(self, "on_channel" + str(channel) + "_" + button) if handler is not None: handler((button, channel) in new_state) @@ -918,18 +891,15 @@ class SoundSensor(Sensor): SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Sound pressure level. Flat weighting - MODE_DB = 'DB' + MODE_DB = "DB" #: Sound pressure level. A weighting - MODE_DBA = 'DBA' + MODE_DBA = "DBA" - MODES = ( - MODE_DB, - MODE_DBA, - ) + MODES = (MODE_DB, MODE_DBA) def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(SoundSensor, self).__init__(address, name_pattern, name_exact, driver_name='lego-nxt-sound', **kwargs) + super(SoundSensor, self).__init__(address, name_pattern, name_exact, driver_name="lego-nxt-sound", **kwargs) @property def sound_pressure(self): @@ -938,7 +908,7 @@ def sound_pressure(self): percent. Uses a flat weighting. """ self._ensure_mode(self.MODE_DB) - return self.value(0) * self._scale('DB') + return self.value(0) * self._scale("DB") @property def sound_pressure_low(self): @@ -947,7 +917,7 @@ def sound_pressure_low(self): percent. Uses A-weighting, which focuses on levels up to 55 dB. """ self._ensure_mode(self.MODE_DBA) - return self.value(0) * self._scale('DBA') + return self.value(0) * self._scale("DBA") class LightSensor(Sensor): @@ -959,18 +929,15 @@ class LightSensor(Sensor): SYSTEM_DEVICE_NAME_CONVENTION = Sensor.SYSTEM_DEVICE_NAME_CONVENTION #: Reflected light. LED on - MODE_REFLECT = 'REFLECT' + MODE_REFLECT = "REFLECT" #: Ambient light. LED off - MODE_AMBIENT = 'AMBIENT' + MODE_AMBIENT = "AMBIENT" - MODES = ( - MODE_REFLECT, - MODE_AMBIENT, - ) + MODES = (MODE_REFLECT, MODE_AMBIENT) def __init__(self, address=None, name_pattern=SYSTEM_DEVICE_NAME_CONVENTION, name_exact=False, **kwargs): - super(LightSensor, self).__init__(address, name_pattern, name_exact, driver_name='lego-nxt-light', **kwargs) + super(LightSensor, self).__init__(address, name_pattern, name_exact, driver_name="lego-nxt-light", **kwargs) @property def reflected_light_intensity(self): @@ -978,7 +945,7 @@ def reflected_light_intensity(self): A measurement of the reflected light intensity, as a percentage. """ self._ensure_mode(self.MODE_REFLECT) - return self.value(0) * self._scale('REFLECT') + return self.value(0) * self._scale("REFLECT") @property def ambient_light_intensity(self): @@ -986,4 +953,4 @@ def ambient_light_intensity(self): A measurement of the ambient light intensity, as a percentage. """ self._ensure_mode(self.MODE_AMBIENT) - return self.value(0) * self._scale('AMBIENT') + return self.value(0) * self._scale("AMBIENT") diff --git a/ev3dev2/sound.py b/ev3dev2/sound.py index 32e7780..5e2fa65 100644 --- a/ev3dev2/sound.py +++ b/ev3dev2/sound.py @@ -26,7 +26,7 @@ import sys if sys.version_info < (3, 4): - raise SystemError('Must be using Python 3.4 or higher') + raise SystemError("Must be using Python 3.4 or higher") from ev3dev2 import is_micropython import os @@ -43,12 +43,11 @@ def _make_scales(notes): res = dict() for note, freq in notes: freq = round(freq) - for n in note.split('/'): + for n in note.split("/"): res[n.upper()] = freq return res - def get_command_processes(command): """ :param string command: a string of command(s) to run that may include pipes @@ -105,19 +104,17 @@ class Sound(object): channel = None # play_types - PLAY_WAIT_FOR_COMPLETE = 0 #: Play the sound and block until it is complete - PLAY_NO_WAIT_FOR_COMPLETE = 1 #: Start playing the sound but return immediately - PLAY_LOOP = 2 #: Never return; start the sound immediately after it completes, until the program is killed - - PLAY_TYPES = ( - PLAY_WAIT_FOR_COMPLETE, - PLAY_NO_WAIT_FOR_COMPLETE, - PLAY_LOOP - ) + PLAY_WAIT_FOR_COMPLETE = 0 #: Play the sound and block until it is complete + PLAY_NO_WAIT_FOR_COMPLETE = 1 #: Start playing the sound but return immediately + PLAY_LOOP = 2 #: Never return; start the sound immediately after it completes, until the program is killed + + PLAY_TYPES = (PLAY_WAIT_FOR_COMPLETE, PLAY_NO_WAIT_FOR_COMPLETE, PLAY_LOOP) def _validate_play_type(self, play_type): - assert play_type in self.PLAY_TYPES, \ - "Invalid play_type %s, must be one of %s" % (play_type, ','.join(str(t) for t in self.PLAY_TYPES)) + assert play_type in self.PLAY_TYPES, "Invalid play_type %s, must be one of %s" % ( + play_type, + ",".join(str(t) for t in self.PLAY_TYPES), + ) def _audio_command(self, command, play_type): if is_micropython(): @@ -126,7 +123,7 @@ def _audio_command(self, command, play_type): os.system(command) elif play_type == Sound.PLAY_NO_WAIT_FOR_COMPLETE: - os.system('{} &'.format(command)) + os.system("{} &".format(command)) elif play_type == Sound.PLAY_LOOP: while True: @@ -138,7 +135,7 @@ def _audio_command(self, command, play_type): return None else: - with open(os.devnull, 'w') as n: + with open(os.devnull, "w") as n: if play_type == Sound.PLAY_WAIT_FOR_COMPLETE: processes = get_command_processes(command) @@ -159,7 +156,7 @@ def _audio_command(self, command, play_type): else: raise Exception("invalid play_type " % play_type) - def beep(self, args='', play_type=PLAY_WAIT_FOR_COMPLETE): + def beep(self, args="", play_type=PLAY_WAIT_FOR_COMPLETE): """ Call beep command with the provided arguments (if any). See `beep man page`_ and google `linux beep music`_ for inspiration. @@ -228,19 +225,20 @@ def tone(self, *args, play_type=PLAY_WAIT_FOR_COMPLETE): :return: When python3 is used and ``Sound.PLAY_NO_WAIT_FOR_COMPLETE`` is specified, returns the returns the spawn subprocess from ``subprocess.Popen``; ``None`` otherwise """ + def play_tone_sequence(tone_sequence): def beep_args(frequency=None, duration=None, delay=None): - args = '' + args = "" if frequency is not None: - args += '-f %s ' % frequency - if duration is not None: - args += '-l %s ' % duration - if delay is not None: - args += '-D %s ' % delay + args += "-f %s " % frequency + if duration is not None: + args += "-l %s " % duration + if delay is not None: + args += "-D %s " % delay return args - return self.beep(' -n '.join([beep_args(*t) for t in tone_sequence]), play_type=play_type) + return self.beep(" -n ".join([beep_args(*t) for t in tone_sequence]), play_type=play_type) if len(args) == 1: return play_tone_sequence(args[0]) @@ -249,8 +247,7 @@ def beep_args(frequency=None, duration=None, delay=None): else: raise Exception("Unsupported number of parameters in Sound.tone(): expected 1 or 2, got " + str(len(args))) - def play_tone(self, frequency, duration, delay=0.0, volume=100, - play_type=PLAY_WAIT_FOR_COMPLETE): + def play_tone(self, frequency, duration, delay=0.0, volume=100, play_type=PLAY_WAIT_FOR_COMPLETE): """ Play a single tone, specified by its frequency, duration, volume and final delay. :param int frequency: the tone frequency, in Hertz @@ -268,11 +265,11 @@ def play_tone(self, frequency, duration, delay=0.0, volume=100, self._validate_play_type(play_type) if duration <= 0: - raise ValueError('invalid duration (%s)' % duration) + raise ValueError("invalid duration (%s)" % duration) if delay < 0: - raise ValueError('invalid delay (%s)' % delay) + raise ValueError("invalid delay (%s)" % delay) if not 0 < volume <= 100: - raise ValueError('invalid volume (%s)' % volume) + raise ValueError("invalid volume (%s)" % volume) self.set_volume(volume) @@ -299,12 +296,12 @@ def play_note(self, note, duration, volume=100, play_type=PLAY_WAIT_FOR_COMPLETE try: freq = self._NOTE_FREQUENCIES.get(note.upper(), self._NOTE_FREQUENCIES[note]) except KeyError: - raise ValueError('invalid note (%s)' % note) + raise ValueError("invalid note (%s)" % note) if duration <= 0: - raise ValueError('invalid duration (%s)' % duration) + raise ValueError("invalid duration (%s)" % duration) if not 0 < volume <= 100: - raise ValueError('invalid volume (%s)' % volume) + raise ValueError("invalid volume (%s)" % volume) return self.play_tone(freq, duration=duration, volume=volume, play_type=play_type) @@ -320,10 +317,10 @@ def play_file(self, wav_file, volume=100, play_type=PLAY_WAIT_FOR_COMPLETE): :return: When python3 is used and ``Sound.PLAY_NO_WAIT_FOR_COMPLETE`` is specified, returns the spawn subprocess from ``subprocess.Popen``; ``None`` otherwise """ if not 0 < volume <= 100: - raise ValueError('invalid volume (%s)' % volume) + raise ValueError("invalid volume (%s)" % volume) if not wav_file.endswith(".wav"): - raise ValueError('invalid sound file (%s), only .wav files are supported' % wav_file) + raise ValueError("invalid sound file (%s), only .wav files are supported" % wav_file) if not os.path.exists(wav_file): raise ValueError("%s does not exist" % wav_file) @@ -332,7 +329,7 @@ def play_file(self, wav_file, volume=100, play_type=PLAY_WAIT_FOR_COMPLETE): self.set_volume(volume) return self._audio_command('/usr/bin/aplay -q "%s"' % wav_file, play_type) - def speak(self, text, espeak_opts='-a 200 -s 130', volume=100, play_type=PLAY_WAIT_FOR_COMPLETE): + def speak(self, text, espeak_opts="-a 200 -s 130", volume=100, play_type=PLAY_WAIT_FOR_COMPLETE): """ Speak the given text aloud. Uses the ``espeak`` external command. @@ -363,12 +360,12 @@ def _get_channel(self): # # Simple mixer control 'Master',0 # Simple mixer control 'Capture',0 - out = os.popen('/usr/bin/amixer scontrols').read() + out = os.popen("/usr/bin/amixer scontrols").read() m = re.search(r"'([^']+)'", out) if m: self.channel = m.group(1) else: - self.channel = 'Playback' + self.channel = "Playback" return self.channel @@ -384,7 +381,7 @@ def set_volume(self, pct, channel=None): if channel is None: channel = self._get_channel() - os.system('/usr/bin/amixer -q set {0} {1:d}%'.format(channel, pct)) + os.system("/usr/bin/amixer -q set {0} {1:d}%".format(channel, pct)) def get_volume(self, channel=None): """ @@ -398,12 +395,12 @@ def get_volume(self, channel=None): if channel is None: channel = self._get_channel() - out = os.popen(['/usr/bin/amixer', 'get', channel]).read() - m = re.search(r'\[(\d+)%\]', out) + out = os.popen(["/usr/bin/amixer", "get", channel]).read() + m = re.search(r"\[(\d+)%\]", out) if m: return int(m.group(1)) else: - raise Exception('Failed to parse output of ``amixer get {}``'.format(channel)) + raise Exception("Failed to parse output of ``amixer get {}``".format(channel)) def play_song(self, song, tempo=120, delay=0.05): """ Plays a song provided as a list of tuples containing the note name and its @@ -472,31 +469,31 @@ def play_song(self, song, tempo=120, delay=0.05): :raises ValueError: if invalid note in song or invalid play parameters """ if tempo <= 0: - raise ValueError('invalid tempo (%s)' % tempo) + raise ValueError("invalid tempo (%s)" % tempo) if delay < 0: - raise ValueError('invalid delay (%s)' % delay) + raise ValueError("invalid delay (%s)" % delay) delay_ms = int(delay * 1000) - meas_duration_ms = 60000 / tempo * 4 # we only support 4/4 bars, hence "* 4" + meas_duration_ms = 60000 / tempo * 4 # we only support 4/4 bars, hence "* 4" for (note, value) in song: value = value.lower() - if '/' in value: - base, factor = value.split('/') + if "/" in value: + base, factor = value.split("/") factor = float(factor) - elif '*' in value: - base, factor = value.split('*') + elif "*" in value: + base, factor = value.split("*") factor = float(factor) - elif value.endswith('.'): + elif value.endswith("."): base = value[:-1] factor = 1.5 - elif value.endswith('3'): + elif value.endswith("3"): base = value[:-1] - factor = float(2/3) + factor = float(2 / 3) else: base = value @@ -505,13 +502,13 @@ def play_song(self, song, tempo=120, delay=0.05): try: duration_ms = meas_duration_ms * self._NOTE_VALUES[base] * factor except KeyError: - raise ValueError('invalid note (%s)' % base) + raise ValueError("invalid note (%s)" % base) if note == "R": sleep(duration_ms / 1000 + delay) else: freq = self._NOTE_FREQUENCIES[note.upper()] - self.beep('-f %d -l %d -D %d' % (freq, duration_ms, delay_ms)) + self.beep("-f %d -l %d -D %d" % (freq, duration_ms, delay_ms)) #: Note frequencies. #: @@ -519,116 +516,118 @@ def play_song(self, song, tempo=120, delay=0.05): #: standard US abbreviation and its octave number (e.g. ``C3``). #: Alterations use the ``#`` and ``b`` symbols, respectively for #: *sharp* and *flat*, between the note code and the octave number (e.g. ``D#4``, ``Gb5``). - _NOTE_FREQUENCIES = _make_scales(( - ('C0', 16.35), - ('C#0/Db0', 17.32), - ('D0', 18.35), - ('D#0/Eb0', 19.45), # expanded in one entry per symbol by _make_scales - ('E0', 20.60), - ('F0', 21.83), - ('F#0/Gb0', 23.12), - ('G0', 24.50), - ('G#0/Ab0', 25.96), - ('A0', 27.50), - ('A#0/Bb0', 29.14), - ('B0', 30.87), - ('C1', 32.70), - ('C#1/Db1', 34.65), - ('D1', 36.71), - ('D#1/Eb1', 38.89), - ('E1', 41.20), - ('F1', 43.65), - ('F#1/Gb1', 46.25), - ('G1', 49.00), - ('G#1/Ab1', 51.91), - ('A1', 55.00), - ('A#1/Bb1', 58.27), - ('B1', 61.74), - ('C2', 65.41), - ('C#2/Db2', 69.30), - ('D2', 73.42), - ('D#2/Eb2', 77.78), - ('E2', 82.41), - ('F2', 87.31), - ('F#2/Gb2', 92.50), - ('G2', 98.00), - ('G#2/Ab2', 103.83), - ('A2', 110.00), - ('A#2/Bb2', 116.54), - ('B2', 123.47), - ('C3', 130.81), - ('C#3/Db3', 138.59), - ('D3', 146.83), - ('D#3/Eb3', 155.56), - ('E3', 164.81), - ('F3', 174.61), - ('F#3/Gb3', 185.00), - ('G3', 196.00), - ('G#3/Ab3', 207.65), - ('A3', 220.00), - ('A#3/Bb3', 233.08), - ('B3', 246.94), - ('C4', 261.63), - ('C#4/Db4', 277.18), - ('D4', 293.66), - ('D#4/Eb4', 311.13), - ('E4', 329.63), - ('F4', 349.23), - ('F#4/Gb4', 369.99), - ('G4', 392.00), - ('G#4/Ab4', 415.30), - ('A4', 440.00), - ('A#4/Bb4', 466.16), - ('B4', 493.88), - ('C5', 523.25), - ('C#5/Db5', 554.37), - ('D5', 587.33), - ('D#5/Eb5', 622.25), - ('E5', 659.25), - ('F5', 698.46), - ('F#5/Gb5', 739.99), - ('G5', 783.99), - ('G#5/Ab5', 830.61), - ('A5', 880.00), - ('A#5/Bb5', 932.33), - ('B5', 987.77), - ('C6', 1046.50), - ('C#6/Db6', 1108.73), - ('D6', 1174.66), - ('D#6/Eb6', 1244.51), - ('E6', 1318.51), - ('F6', 1396.91), - ('F#6/Gb6', 1479.98), - ('G6', 1567.98), - ('G#6/Ab6', 1661.22), - ('A6', 1760.00), - ('A#6/Bb6', 1864.66), - ('B6', 1975.53), - ('C7', 2093.00), - ('C#7/Db7', 2217.46), - ('D7', 2349.32), - ('D#7/Eb7', 2489.02), - ('E7', 2637.02), - ('F7', 2793.83), - ('F#7/Gb7', 2959.96), - ('G7', 3135.96), - ('G#7/Ab7', 3322.44), - ('A7', 3520.00), - ('A#7/Bb7', 3729.31), - ('B7', 3951.07), - ('C8', 4186.01), - ('C#8/Db8', 4434.92), - ('D8', 4698.63), - ('D#8/Eb8', 4978.03), - ('E8', 5274.04), - ('F8', 5587.65), - ('F#8/Gb8', 5919.91), - ('G8', 6271.93), - ('G#8/Ab8', 6644.88), - ('A8', 7040.00), - ('A#8/Bb8', 7458.62), - ('B8', 7902.13) - )) + _NOTE_FREQUENCIES = _make_scales( + ( + ("C0", 16.35), + ("C#0/Db0", 17.32), + ("D0", 18.35), + ("D#0/Eb0", 19.45), # expanded in one entry per symbol by _make_scales + ("E0", 20.60), + ("F0", 21.83), + ("F#0/Gb0", 23.12), + ("G0", 24.50), + ("G#0/Ab0", 25.96), + ("A0", 27.50), + ("A#0/Bb0", 29.14), + ("B0", 30.87), + ("C1", 32.70), + ("C#1/Db1", 34.65), + ("D1", 36.71), + ("D#1/Eb1", 38.89), + ("E1", 41.20), + ("F1", 43.65), + ("F#1/Gb1", 46.25), + ("G1", 49.00), + ("G#1/Ab1", 51.91), + ("A1", 55.00), + ("A#1/Bb1", 58.27), + ("B1", 61.74), + ("C2", 65.41), + ("C#2/Db2", 69.30), + ("D2", 73.42), + ("D#2/Eb2", 77.78), + ("E2", 82.41), + ("F2", 87.31), + ("F#2/Gb2", 92.50), + ("G2", 98.00), + ("G#2/Ab2", 103.83), + ("A2", 110.00), + ("A#2/Bb2", 116.54), + ("B2", 123.47), + ("C3", 130.81), + ("C#3/Db3", 138.59), + ("D3", 146.83), + ("D#3/Eb3", 155.56), + ("E3", 164.81), + ("F3", 174.61), + ("F#3/Gb3", 185.00), + ("G3", 196.00), + ("G#3/Ab3", 207.65), + ("A3", 220.00), + ("A#3/Bb3", 233.08), + ("B3", 246.94), + ("C4", 261.63), + ("C#4/Db4", 277.18), + ("D4", 293.66), + ("D#4/Eb4", 311.13), + ("E4", 329.63), + ("F4", 349.23), + ("F#4/Gb4", 369.99), + ("G4", 392.00), + ("G#4/Ab4", 415.30), + ("A4", 440.00), + ("A#4/Bb4", 466.16), + ("B4", 493.88), + ("C5", 523.25), + ("C#5/Db5", 554.37), + ("D5", 587.33), + ("D#5/Eb5", 622.25), + ("E5", 659.25), + ("F5", 698.46), + ("F#5/Gb5", 739.99), + ("G5", 783.99), + ("G#5/Ab5", 830.61), + ("A5", 880.00), + ("A#5/Bb5", 932.33), + ("B5", 987.77), + ("C6", 1046.50), + ("C#6/Db6", 1108.73), + ("D6", 1174.66), + ("D#6/Eb6", 1244.51), + ("E6", 1318.51), + ("F6", 1396.91), + ("F#6/Gb6", 1479.98), + ("G6", 1567.98), + ("G#6/Ab6", 1661.22), + ("A6", 1760.00), + ("A#6/Bb6", 1864.66), + ("B6", 1975.53), + ("C7", 2093.00), + ("C#7/Db7", 2217.46), + ("D7", 2349.32), + ("D#7/Eb7", 2489.02), + ("E7", 2637.02), + ("F7", 2793.83), + ("F#7/Gb7", 2959.96), + ("G7", 3135.96), + ("G#7/Ab7", 3322.44), + ("A7", 3520.00), + ("A#7/Bb7", 3729.31), + ("B7", 3951.07), + ("C8", 4186.01), + ("C#8/Db8", 4434.92), + ("D8", 4698.63), + ("D#8/Eb8", 4978.03), + ("E8", 5274.04), + ("F8", 5587.65), + ("F#8/Gb8", 5919.91), + ("G8", 6271.93), + ("G#8/Ab8", 6644.88), + ("A8", 7040.00), + ("A#8/Bb8", 7458.62), + ("B8", 7902.13), + ) + ) #: Common note values. #: @@ -650,10 +649,4 @@ def play_song(self, song, tempo=120, delay=0.05): #: For instance, the note value of a eight triplet will be ``NOTE_VALUE['e'] / 3``. #: It is simpler however to user the ``3`` modifier of notes, as supported by the #: :py:meth:`Sound.play_song` method. - _NOTE_VALUES = { - 'w': 1., - 'h': 1./2, - 'q': 1./4, - 'e': 1./8, - 's': 1./16, - } + _NOTE_VALUES = {"w": 1.0, "h": 1.0 / 2, "q": 1.0 / 4, "e": 1.0 / 8, "s": 1.0 / 16} diff --git a/ev3dev2/stopwatch.py b/ev3dev2/stopwatch.py index 35b804b..e23ae78 100644 --- a/ev3dev2/stopwatch.py +++ b/ev3dev2/stopwatch.py @@ -9,24 +9,29 @@ else: import datetime as dt + def get_ticks_ms(): if is_micropython(): return utime.ticks_ms() else: return int(dt.datetime.timestamp(dt.datetime.now()) * 1000) + class StopWatchAlreadyStartedException(Exception): """ Exception raised when start() is called on a StopWatch which was already start()ed and not yet stopped. """ + pass + class StopWatch(object): """ A timer class which lets you start timing and then check the amount of time elapsed. """ + def __init__(self, desc=None): """ Initializes the StopWatch but does not start it. @@ -70,14 +75,14 @@ def reset(self): """ self._start_time = None self._stopped_total_time = None - + def restart(self): """ Resets and then starts the timer. """ self.reset() self.start() - + @property def is_started(self): """ @@ -118,7 +123,7 @@ def hms_str(self): """ Returns the stringified value of the stopwatch in HH:MM:SS.msec format """ - return '%02d:%02d:%02d.%03d' % self.value_hms + return "%02d:%02d:%02d.%03d" % self.value_hms def is_elapsed_ms(self, duration_ms): """ @@ -136,4 +141,3 @@ def is_elapsed_secs(self, duration_secs): """ return duration_secs is not None and self.value_secs >= duration_secs - diff --git a/ev3dev2/unit.py b/ev3dev2/unit.py index 6d90849..b64610a 100644 --- a/ev3dev2/unit.py +++ b/ev3dev2/unit.py @@ -23,8 +23,8 @@ import sys -if sys.version_info < (3,4): - raise SystemError('Must be using Python 3.4 or higher') +if sys.version_info < (3, 4): + raise SystemError("Must be using Python 3.4 or higher") CENTIMETER_MM = 10 DECIMETER_MM = 100 diff --git a/ev3dev2/wheel.py b/ev3dev2/wheel.py index 0f01828..ddccf1f 100644 --- a/ev3dev2/wheel.py +++ b/ev3dev2/wheel.py @@ -44,6 +44,7 @@ class EV3Rim(Wheel): part number 56145 comes in set 31313 """ + def __init__(self): Wheel.__init__(self, 30, 20) diff --git a/git_version.py b/git_version.py index 4ca6b7d..dd50b82 100644 --- a/git_version.py +++ b/git_version.py @@ -3,7 +3,7 @@ pyver = sys.version_info -#---------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- # Get version string from git # # Author: Douglas Creager @@ -11,14 +11,13 @@ # # PEP 386 adaptation from # https://gist.github.com/ilogue/2567778/f6661ea2c12c070851b2dfb4da8840a6641914bc -#---------------------------------------------------------------------------- +# ---------------------------------------------------------------------------- def call_git_describe(abbrev=4): try: - p = Popen(['git', 'describe', '--exclude', 'ev3dev-*', '--abbrev=%d' % abbrev], - stdout=PIPE, stderr=PIPE) + p = Popen(["git", "describe", "--exclude", "ev3dev-*", "--abbrev=%d" % abbrev], stdout=PIPE, stderr=PIPE) p.stderr.close() line = p.stdout.readlines()[0] - return line.strip().decode('utf8') + return line.strip().decode("utf8") except: return None @@ -26,7 +25,7 @@ def call_git_describe(abbrev=4): def read_release_version(): try: - with open('{}/RELEASE-VERSION'.format(os.path.dirname(__file__)), 'r') as f: + with open("{}/RELEASE-VERSION".format(os.path.dirname(__file__)), "r") as f: version = f.readlines()[0] return version.strip() except: @@ -34,16 +33,16 @@ def read_release_version(): def write_release_version(version): - with open('{}/RELEASE-VERSION'.format(os.path.dirname(__file__)), 'w') as f: + with open("{}/RELEASE-VERSION".format(os.path.dirname(__file__)), "w") as f: f.write("%s\n" % version) def pep386adapt(version): # adapt git-describe version to be in line with PEP 386 - parts = version.split('-') + parts = version.split("-") if len(parts) > 1: - parts[-2] = 'post'+parts[-2] - version = '.'.join(parts[:-1]) + parts[-2] = "post" + parts[-2] + version = ".".join(parts[:-1]) return version @@ -59,7 +58,7 @@ def git_version(abbrev=4): if version is None: version = release_version else: - #adapt to PEP 386 compatible versioning scheme + # adapt to PEP 386 compatible versioning scheme version = pep386adapt(version) # If we still don't have anything, that's an error. @@ -72,9 +71,8 @@ def git_version(abbrev=4): write_release_version(version) # Update the ev3dev2/version.py - with open('{}/ev3dev2/version.py'.format(os.path.dirname(__file__)), 'w') as f: + with open("{}/ev3dev2/version.py".format(os.path.dirname(__file__)), "w") as f: f.write("__version__ = '{}'".format(version)) # Finally, return the current version. return version - diff --git a/setup.py b/setup.py index 9c697d6..cfc0fbf 100644 --- a/setup.py +++ b/setup.py @@ -3,25 +3,21 @@ from os import path this_directory = path.abspath(path.dirname(__file__)) -with open(path.join(this_directory, 'README.rst'), encoding='utf-8') as f: +with open(path.join(this_directory, "README.rst"), encoding="utf-8") as f: long_description = f.read() setup( - name='python-ev3dev2', + name="python-ev3dev2", version=git_version(), - description='v2.x Python language bindings for ev3dev', - author='ev3dev Python team', - author_email='python-team@ev3dev.org', - license='MIT', - url='https://github.com/ev3dev/ev3dev-lang-python', + description="v2.x Python language bindings for ev3dev", + author="ev3dev Python team", + author_email="python-team@ev3dev.org", + license="MIT", + url="https://github.com/ev3dev/ev3dev-lang-python", include_package_data=True, long_description=long_description, - long_description_content_type='text/x-rst', - packages=['ev3dev2', - 'ev3dev2.fonts', - 'ev3dev2.sensor', - 'ev3dev2.control', - 'ev3dev2._platform'], - package_data={'': ['*.pil', '*.pbm']}, - install_requires=['Pillow'] - ) + long_description_content_type="text/x-rst", + packages=["ev3dev2", "ev3dev2.fonts", "ev3dev2.sensor", "ev3dev2.control", "ev3dev2._platform"], + package_data={"": ["*.pil", "*.pbm"]}, + install_requires=["Pillow"], +) diff --git a/tests/api_tests.py b/tests/api_tests.py index 527b226..3dd11ea 100755 --- a/tests/api_tests.py +++ b/tests/api_tests.py @@ -2,22 +2,33 @@ import unittest, sys, os.path import os -FAKE_SYS = os.path.join(os.path.dirname(__file__), 'fake-sys') +FAKE_SYS = os.path.join(os.path.dirname(__file__), "fake-sys") os.environ["FAKE_SYS"] = "1" sys.path.append(FAKE_SYS) -sys.path.append(os.path.join(os.path.dirname(__file__), '..')) +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) from populate_arena import populate_arena -from clean_arena import clean_arena +from clean_arena import clean_arena import ev3dev2 from ev3dev2.sensor.lego import InfraredSensor -from ev3dev2.motor import \ - OUTPUT_A, OUTPUT_B, \ - Motor, MediumMotor, LargeMotor, \ - MoveTank, MoveSteering, MoveJoystick, \ - SpeedPercent, SpeedDPM, SpeedDPS, SpeedRPM, SpeedRPS, SpeedNativeUnits +from ev3dev2.motor import ( + OUTPUT_A, + OUTPUT_B, + Motor, + MediumMotor, + LargeMotor, + MoveTank, + MoveSteering, + MoveJoystick, + SpeedPercent, + SpeedDPM, + SpeedDPS, + SpeedRPM, + SpeedRPS, + SpeedNativeUnits, +) from ev3dev2.unit import ( DistanceMillimeters, @@ -27,53 +38,68 @@ DistanceInches, DistanceFeet, DistanceYards, - DistanceStuds + DistanceStuds, ) import ev3dev2.stopwatch from ev3dev2.stopwatch import StopWatch, StopWatchAlreadyStartedException -ev3dev2.Device.DEVICE_ROOT_PATH = os.path.join(FAKE_SYS, 'arena') +ev3dev2.Device.DEVICE_ROOT_PATH = os.path.join(FAKE_SYS, "arena") _internal_set_attribute = ev3dev2.Device._set_attribute + + def _set_attribute(self, attribute, name, value): # Follow the text with a newline to separate new content from stuff that # already existed in the buffer. On the real device we're writing to sysfs # attributes where there isn't any persistent buffer, but in the test # environment they're normal files on disk which retain previous data. attribute = _internal_set_attribute(self, attribute, name, value) - attribute.write(b'\n') + attribute.write(b"\n") return attribute + + ev3dev2.Device._set_attribute = _set_attribute _internal_get_attribute = ev3dev2.Device._get_attribute + + def _get_attribute(self, attribute, name): # Split on newline delimiter; see _set_attribute above attribute, value = _internal_get_attribute(self, attribute, name) - return attribute, value.split('\n', 1)[0] + return attribute, value.split("\n", 1)[0] + + ev3dev2.Device._get_attribute = _get_attribute + def dummy_wait(self, cond, timeout=None): pass + Motor.wait = dummy_wait # for StopWatch mock_ticks_ms = 0 + + def _mock_get_ticks_ms(): return mock_ticks_ms + + ev3dev2.stopwatch.get_ticks_ms = _mock_get_ticks_ms + def set_mock_ticks_ms(value): global mock_ticks_ms mock_ticks_ms = value -class TestAPI(unittest.TestCase): +class TestAPI(unittest.TestCase): def setUp(self): # micropython does not have _testMethodName try: - print("\n\n{}\n{}".format(self._testMethodName, "=" * len(self._testMethodName,))) + print("\n\n{}\n{}".format(self._testMethodName, "=" * len(self._testMethodName))) except AttributeError: pass @@ -82,33 +108,33 @@ def setUp(self): def test_device(self): clean_arena() - populate_arena([('medium_motor', 0, 'outA'), ('infrared_sensor', 0, 'in1')]) + populate_arena([("medium_motor", 0, "outA"), ("infrared_sensor", 0, "in1")]) - d = ev3dev2.Device('tacho-motor', 'motor*') + d = ev3dev2.Device("tacho-motor", "motor*") - d = ev3dev2.Device('tacho-motor', 'motor0') + d = ev3dev2.Device("tacho-motor", "motor0") - d = ev3dev2.Device('tacho-motor', 'motor*', driver_name='lego-ev3-m-motor') + d = ev3dev2.Device("tacho-motor", "motor*", driver_name="lego-ev3-m-motor") - d = ev3dev2.Device('tacho-motor', 'motor*', address='outA') + d = ev3dev2.Device("tacho-motor", "motor*", address="outA") with self.assertRaises(ev3dev2.DeviceNotFound): - d = ev3dev2.Device('tacho-motor', 'motor*', address='outA', driver_name='not-valid') + d = ev3dev2.Device("tacho-motor", "motor*", address="outA", driver_name="not-valid") with self.assertRaises(ev3dev2.DeviceNotFound): - d = ev3dev2.Device('tacho-motor', 'motor*', address='this-does-not-exist') + d = ev3dev2.Device("tacho-motor", "motor*", address="this-does-not-exist") - d = ev3dev2.Device('lego-sensor', 'sensor*') + d = ev3dev2.Device("lego-sensor", "sensor*") with self.assertRaises(ev3dev2.DeviceNotFound): - d = ev3dev2.Device('this-does-not-exist') + d = ev3dev2.Device("this-does-not-exist") def test_medium_motor(self): def dummy(self): pass clean_arena() - populate_arena([('medium_motor', 0, 'outA')]) + populate_arena([("medium_motor", 0, "outA")]) # Do not write motor.command on exit (so that fake tree stays intact) MediumMotor.__del__ = dummy @@ -118,56 +144,58 @@ def dummy(self): self.assertEqual(m.device_index, 0) # Check that reading twice works: - self.assertEqual(m.driver_name, 'lego-ev3-m-motor') - self.assertEqual(m.driver_name, 'lego-ev3-m-motor') - - self.assertEqual(m.count_per_rot, 360) - self.assertEqual(m.commands, ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset']) - self.assertEqual(m.duty_cycle, 0) - self.assertEqual(m.duty_cycle_sp, 42) - self.assertEqual(m.polarity, 'normal') - self.assertEqual(m.address, 'outA') - self.assertEqual(m.position, 42) - self.assertEqual(m.position_sp, 42) - self.assertEqual(m.ramp_down_sp, 0) - self.assertEqual(m.ramp_up_sp, 0) - self.assertEqual(m.speed, 0) - self.assertEqual(m.speed_sp, 0) - self.assertEqual(m.state, ['running']) - self.assertEqual(m.stop_action, 'coast') - self.assertEqual(m.time_sp, 1000) + self.assertEqual(m.driver_name, "lego-ev3-m-motor") + self.assertEqual(m.driver_name, "lego-ev3-m-motor") + + self.assertEqual(m.count_per_rot, 360) + self.assertEqual( + m.commands, ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"] + ) + self.assertEqual(m.duty_cycle, 0) + self.assertEqual(m.duty_cycle_sp, 42) + self.assertEqual(m.polarity, "normal") + self.assertEqual(m.address, "outA") + self.assertEqual(m.position, 42) + self.assertEqual(m.position_sp, 42) + self.assertEqual(m.ramp_down_sp, 0) + self.assertEqual(m.ramp_up_sp, 0) + self.assertEqual(m.speed, 0) + self.assertEqual(m.speed_sp, 0) + self.assertEqual(m.state, ["running"]) + self.assertEqual(m.stop_action, "coast") + self.assertEqual(m.time_sp, 1000) with self.assertRaises(Exception): c = m.command def test_infrared_sensor(self): clean_arena() - populate_arena([('infrared_sensor', 0, 'in1')]) + populate_arena([("infrared_sensor", 0, "in1")]) s = InfraredSensor() - self.assertEqual(s.device_index, 0) - self.assertEqual(s.bin_data_format, 's8') - self.assertEqual(s.bin_data(' toc - time.time(): + while 0.005 > toc - time.time(): toc += self.interval time.sleep(toc - time.time()) @@ -42,48 +42,49 @@ def join(self, timeout=None): super(LogThread, self).join(timeout) -test = json.loads( open( args.infile ).read() ) +test = json.loads(open(args.infile).read()) + def execute_actions(actions): - for p,c in actions['ports'].items(): + for p, c in actions["ports"].items(): for b in c: - for k,v in b.items(): - setattr( device[p], k, v ) - + for k, v in b.items(): + setattr(device[p], k, v) + + device = {} logs = {} -for p,v in test['meta']['ports'].items(): - device[p] = getattr( ev3, v['device_class'] )( p ) +for p, v in test["meta"]["ports"].items(): + device[p] = getattr(ev3, v["device_class"])(p) -if test['actions'][0]['time'] < 0: - execute_actions(test['actions'][0]) +if test["actions"][0]["time"] < 0: + execute_actions(test["actions"][0]) -for p,v in test['meta']['ports'].items(): - device[p] = getattr( ev3, v['device_class'] )( p ) +for p, v in test["meta"]["ports"].items(): + device[p] = getattr(ev3, v["device_class"])(p) - logs[p] = LogThread(test['meta']['interval'] * 1e-3, - device[p], - v['log_attributes'] ) + logs[p] = LogThread(test["meta"]["interval"] * 1e-3, device[p], v["log_attributes"]) logs[p].start() start = time.time() -end = start + test['meta']['max_time'] * 1e-3 +end = start + test["meta"]["max_time"] * 1e-3 -for a in test['actions']: - if a['time'] >= 0: - then = start + a['time'] * 1e-3 - while time.time() < then: pass +for a in test["actions"]: + if a["time"] >= 0: + then = start + a["time"] * 1e-3 + while time.time() < then: + pass execute_actions(a) while time.time() < end: pass -test['data'] = {} +test["data"] = {} -for p,v in test['meta']['ports'].items(): - logs[p].join() - test['data'][p] = logs[p].results +for p, v in test["meta"]["ports"].items(): + logs[p].join() + test["data"][p] = logs[p].results # Add a nice JSON formatter here - maybe? -print (json.dumps( test, indent = 4 )) +print(json.dumps(test, indent=4)) diff --git a/tests/motor/motor_info.py b/tests/motor/motor_info.py index 82b69e2..0e7ca93 100644 --- a/tests/motor/motor_info.py +++ b/tests/motor/motor_info.py @@ -1,56 +1,64 @@ motor_info = { - 'lego-ev3-l-motor': {'motion_type': 'rotation', - 'count_per_rot': 360, - 'max_speed': 1050, - 'position_p': 80000, - 'position_i': 0, - 'position_d': 0, - 'polarity': 'normal', - 'speed_p': 1000, - 'speed_i': 60, - 'speed_d': 0 }, - 'lego-ev3-m-motor': {'motion_type': 'rotation', - 'count_per_rot': 360, - 'max_speed': 1560, - 'position_p': 160000, - 'position_i': 0, - 'position_d': 0, - 'polarity': 'normal', - 'speed_p': 1000, - 'speed_i': 60, - 'speed_d': 0 }, - 'lego-nxt-motor': {'motion_type': 'rotation', - 'count_per_rot': 360, - 'max_speed': 1020, - 'position_p': 80000, - 'position_i': 0, - 'position_d': 0, - 'polarity': 'normal', - 'speed_p': 1000, - 'speed_i': 60, - 'speed_d': 0 }, - 'fi-l12-ev3-50': {'motion_type': 'linear', - 'count_per_m': 2000, - 'full_travel_count': 100, - 'max_speed': 24, - 'position_p': 40000, - 'position_i': 0, - 'position_d': 0, - 'polarity': 'normal', - 'speed_p': 1000, - 'speed_i': 60, - 'speed_d': 0, - }, - 'fi-l12-ev3-100': {'motion_type': 'linear', - 'count_per_m': 2000, - 'full_travel_count': 200, - 'max_speed': 24, - 'position_p': 40000, - 'position_i': 0, - 'position_d': 0, - 'polarity': 'normal', - 'speed_p': 1000, - 'speed_i': 60, - 'speed_d': 0, - } + "lego-ev3-l-motor": { + "motion_type": "rotation", + "count_per_rot": 360, + "max_speed": 1050, + "position_p": 80000, + "position_i": 0, + "position_d": 0, + "polarity": "normal", + "speed_p": 1000, + "speed_i": 60, + "speed_d": 0, + }, + "lego-ev3-m-motor": { + "motion_type": "rotation", + "count_per_rot": 360, + "max_speed": 1560, + "position_p": 160000, + "position_i": 0, + "position_d": 0, + "polarity": "normal", + "speed_p": 1000, + "speed_i": 60, + "speed_d": 0, + }, + "lego-nxt-motor": { + "motion_type": "rotation", + "count_per_rot": 360, + "max_speed": 1020, + "position_p": 80000, + "position_i": 0, + "position_d": 0, + "polarity": "normal", + "speed_p": 1000, + "speed_i": 60, + "speed_d": 0, + }, + "fi-l12-ev3-50": { + "motion_type": "linear", + "count_per_m": 2000, + "full_travel_count": 100, + "max_speed": 24, + "position_p": 40000, + "position_i": 0, + "position_d": 0, + "polarity": "normal", + "speed_p": 1000, + "speed_i": 60, + "speed_d": 0, + }, + "fi-l12-ev3-100": { + "motion_type": "linear", + "count_per_m": 2000, + "full_travel_count": 200, + "max_speed": 24, + "position_p": 40000, + "position_i": 0, + "position_d": 0, + "polarity": "normal", + "speed_p": 1000, + "speed_i": 60, + "speed_d": 0, + }, } diff --git a/tests/motor/motor_motion_unittest.py b/tests/motor/motor_motion_unittest.py index 8bfac60..30c7a8e 100644 --- a/tests/motor/motor_motion_unittest.py +++ b/tests/motor/motor_motion_unittest.py @@ -12,8 +12,8 @@ from motor_info import motor_info -class TestMotorMotion(ptc.ParameterizedTestCase): +class TestMotorMotion(ptc.ParameterizedTestCase): @classmethod def setUpClass(cls): pass @@ -23,117 +23,127 @@ def tearDownClass(cls): pass def initialize_motor(self): - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - def run_to_positions(self,stop_action,command,speed_sp,positions,tolerance): - self._param['motor'].stop_action = stop_action - self._param['motor'].speed_sp = speed_sp + def run_to_positions(self, stop_action, command, speed_sp, positions, tolerance): + self._param["motor"].stop_action = stop_action + self._param["motor"].speed_sp = speed_sp - target = self._param['motor'].position + target = self._param["motor"].position for i in positions: - self._param['motor'].position_sp = i - if 'run-to-rel-pos' == command: + self._param["motor"].position_sp = i + if "run-to-rel-pos" == command: target += i else: target = i - print( "PRE position = {0} i = {1} target = {2}".format(self._param['motor'].position, i, target)) - self._param['motor'].command = command - while 'running' in self._param['motor'].state: + print("PRE position = {0} i = {1} target = {2}".format(self._param["motor"].position, i, target)) + self._param["motor"].command = command + while "running" in self._param["motor"].state: pass - print( "POS position = {0} i = {1} target = {2}".format(self._param['motor'].position, i, target)) - self.assertGreaterEqual(tolerance, abs(self._param['motor'].position - target)) + print("POS position = {0} i = {1} target = {2}".format(self._param["motor"].position, i, target)) + self.assertGreaterEqual(tolerance, abs(self._param["motor"].position - target)) time.sleep(0.2) - self._param['motor'].command = 'stop' + self._param["motor"].command = "stop" def test_stop_brake_no_ramp_med_speed_relative(self): - if not self._param['has_brake']: - self.skipTest('brake not supported by this motor controller') + if not self._param["has_brake"]: + self.skipTest("brake not supported by this motor controller") self.initialize_motor() - self.run_to_positions('brake','run-to-rel-pos',400,[0,90,180,360,720,-720,-360,-180,-90,0],20) + self.run_to_positions("brake", "run-to-rel-pos", 400, [0, 90, 180, 360, 720, -720, -360, -180, -90, 0], 20) def test_stop_hold_no_ramp_med_speed_relative(self): self.initialize_motor() - self.run_to_positions('hold','run-to-rel-pos',400,[0,90,180,360,720,-720,-360,-180,-90,0],5) + self.run_to_positions("hold", "run-to-rel-pos", 400, [0, 90, 180, 360, 720, -720, -360, -180, -90, 0], 5) def test_stop_brake_no_ramp_low_speed_relative(self): - if not self._param['has_brake']: - self.skipTest('brake not supported by this motor controller') + if not self._param["has_brake"]: + self.skipTest("brake not supported by this motor controller") self.initialize_motor() - self.run_to_positions('brake','run-to-rel-pos',100,[0,90,180,360,720,-720,-360,-180,-90,0],20) + self.run_to_positions("brake", "run-to-rel-pos", 100, [0, 90, 180, 360, 720, -720, -360, -180, -90, 0], 20) def test_stop_hold_no_ramp_low_speed_relative(self): self.initialize_motor() - self.run_to_positions('hold','run-to-rel-pos',100,[0,90,180,360,720,-720,-360,-180,-90,0],5) + self.run_to_positions("hold", "run-to-rel-pos", 100, [0, 90, 180, 360, 720, -720, -360, -180, -90, 0], 5) def test_stop_brake_no_ramp_high_speed_relative(self): - if not self._param['has_brake']: - self.skipTest('brake not supported by this motor controller') + if not self._param["has_brake"]: + self.skipTest("brake not supported by this motor controller") self.initialize_motor() - self.run_to_positions('brake','run-to-rel-pos',900,[0,90,180,360,720,-720,-360,-180,-90,0],50) + self.run_to_positions("brake", "run-to-rel-pos", 900, [0, 90, 180, 360, 720, -720, -360, -180, -90, 0], 50) def test_stop_hold_no_ramp_high_speed_relative(self): self.initialize_motor() - self.run_to_positions('hold','run-to-rel-pos',100,[0,90,180,360,720,-720,-360,-180,-90,0],5) + self.run_to_positions("hold", "run-to-rel-pos", 100, [0, 90, 180, 360, 720, -720, -360, -180, -90, 0], 5) def test_stop_brake_no_ramp_med_speed_absolute(self): - if not self._param['has_brake']: - self.skipTest('brake not supported by this motor controller') + if not self._param["has_brake"]: + self.skipTest("brake not supported by this motor controller") self.initialize_motor() - self.run_to_positions('brake','run-to-abs-pos',400,[0,90,180,360,180,90,0,-90,-180,-360,-180,-90,0],20) + self.run_to_positions( + "brake", "run-to-abs-pos", 400, [0, 90, 180, 360, 180, 90, 0, -90, -180, -360, -180, -90, 0], 20 + ) def test_stop_hold_no_ramp_med_speed_absolute(self): self.initialize_motor() - self.run_to_positions('hold','run-to-abs-pos',400,[0,90,180,360,180,90,0,-90,-180,-360,-180,-90,0],5) + self.run_to_positions( + "hold", "run-to-abs-pos", 400, [0, 90, 180, 360, 180, 90, 0, -90, -180, -360, -180, -90, 0], 5 + ) def test_stop_brake_no_ramp_low_speed_absolute(self): - if not self._param['has_brake']: - self.skipTest('brake not supported by this motor controller') + if not self._param["has_brake"]: + self.skipTest("brake not supported by this motor controller") self.initialize_motor() - self.run_to_positions('brake','run-to-abs-pos',100,[0,90,180,360,180,90,0,-90,-180,-360,-180,-90,0],20) + self.run_to_positions( + "brake", "run-to-abs-pos", 100, [0, 90, 180, 360, 180, 90, 0, -90, -180, -360, -180, -90, 0], 20 + ) def test_stop_hold_no_ramp_low_speed_absolute(self): self.initialize_motor() - self.run_to_positions('hold','run-to-abs-pos',100,[0,90,180,360,180,90,0,-90,-180,-360,-180,-90,0],5) + self.run_to_positions( + "hold", "run-to-abs-pos", 100, [0, 90, 180, 360, 180, 90, 0, -90, -180, -360, -180, -90, 0], 5 + ) def test_stop_brake_no_ramp_high_speed_absolute(self): - if not self._param['has_brake']: - self.skipTest('brake not supported by this motor controller') + if not self._param["has_brake"]: + self.skipTest("brake not supported by this motor controller") self.initialize_motor() - self.run_to_positions('brake','run-to-abs-pos',900,[0,90,180,360,180,90,0,-90,-180,-360,-180,-90,0],50) + self.run_to_positions( + "brake", "run-to-abs-pos", 900, [0, 90, 180, 360, 180, 90, 0, -90, -180, -360, -180, -90, 0], 50 + ) def test_stop_hold_no_ramp_high_speed_absolute(self): self.initialize_motor() - self.run_to_positions('hold','run-to-abs-pos',100,[0,90,180,360,180,90,0,-90,-180,-360,-180,-90,0],5) + self.run_to_positions( + "hold", "run-to-abs-pos", 100, [0, 90, 180, 360, 180, 90, 0, -90, -180, -360, -180, -90, 0], 5 + ) + # Add all the tests to the suite - some tests apply only to certain drivers! + def AddTachoMotorMotionTestsToSuite(suite, params): suite.addTest(ptc.ParameterizedTestCase.parameterize(TestMotorMotion, param=params)) -if __name__ == '__main__': - ev3_params = { - 'motor': ev3.Motor('outA'), - 'port': 'outA', - 'driver_name': 'lego-ev3-l-motor', - 'has_brake': True, - } + +if __name__ == "__main__": + ev3_params = {"motor": ev3.Motor("outA"), "port": "outA", "driver_name": "lego-ev3-l-motor", "has_brake": True} brickpi_params = { - 'motor': ev3.Motor('ttyAMA0:MA'), - 'port': 'ttyAMA0:MA', - 'driver_name': 'lego-nxt-motor', - 'has_brake': False, + "motor": ev3.Motor("ttyAMA0:MA"), + "port": "ttyAMA0:MA", + "driver_name": "lego-nxt-motor", + "has_brake": False, } pistorms_params = { - 'motor': ev3.Motor('pistorms:BAM1'), - 'port': 'pistorms:BAM1', - 'driver_name': 'lego-nxt-motor', - 'has_brake': True, + "motor": ev3.Motor("pistorms:BAM1"), + "port": "pistorms:BAM1", + "driver_name": "lego-nxt-motor", + "has_brake": True, } suite = unittest.TestSuite() AddTachoMotorMotionTestsToSuite(suite, ev3_params) - unittest.TextTestRunner(verbosity=1,buffer=True ).run(suite) + unittest.TextTestRunner(verbosity=1, buffer=True).run(suite) diff --git a/tests/motor/motor_param_unittest.py b/tests/motor/motor_param_unittest.py index ad2c3b4..a5bbe69 100644 --- a/tests/motor/motor_param_unittest.py +++ b/tests/motor/motor_param_unittest.py @@ -13,559 +13,563 @@ from motor_info import motor_info -class TestTachoMotorAddressValue(ptc.ParameterizedTestCase): +class TestTachoMotorAddressValue(ptc.ParameterizedTestCase): def test_address_value(self): - self.assertEqual(self._param['motor'].address, self._param['port']) + self.assertEqual(self._param["motor"].address, self._param["port"]) def test_address_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].address = "ThisShouldNotWork" + self._param["motor"].address = "ThisShouldNotWork" -class TestTachoMotorCommandsValue(ptc.ParameterizedTestCase): +class TestTachoMotorCommandsValue(ptc.ParameterizedTestCase): def test_commands_value(self): - self.assertTrue(self._param['motor'].commands == self._param['commands']) + self.assertTrue(self._param["motor"].commands == self._param["commands"]) def test_commands_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].commands = "ThisShouldNotWork" + self._param["motor"].commands = "ThisShouldNotWork" -class TestTachoMotorCountPerRotValue(ptc.ParameterizedTestCase): +class TestTachoMotorCountPerRotValue(ptc.ParameterizedTestCase): def test_count_per_rot_value(self): - self.assertEqual(self._param['motor'].count_per_rot, motor_info[self._param['motor'].driver_name]['count_per_rot']) + self.assertEqual( + self._param["motor"].count_per_rot, motor_info[self._param["motor"].driver_name]["count_per_rot"] + ) def test_count_per_rot_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].count_per_rot = "ThisShouldNotWork" + self._param["motor"].count_per_rot = "ThisShouldNotWork" -class TestTachoMotorCountPerMValue(ptc.ParameterizedTestCase): +class TestTachoMotorCountPerMValue(ptc.ParameterizedTestCase): def test_count_per_m_value(self): - self.assertEqual(self._param['motor'].count_per_m, motor_info[self._param['motor'].driver_name]['count_per_m']) + self.assertEqual(self._param["motor"].count_per_m, motor_info[self._param["motor"].driver_name]["count_per_m"]) def test_count_per_m_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].count_per_m = "ThisShouldNotWork" + self._param["motor"].count_per_m = "ThisShouldNotWork" -class TestTachoMotorFullTravelCountValue(ptc.ParameterizedTestCase): +class TestTachoMotorFullTravelCountValue(ptc.ParameterizedTestCase): def test_full_travel_count_value(self): - self.assertEqual(self._param['motor'].full_travel_count, motor_info[self._param['motor'].driver_name]['full_travel_count']) + self.assertEqual( + self._param["motor"].full_travel_count, motor_info[self._param["motor"].driver_name]["full_travel_count"] + ) def test_full_travel_count_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].count_per_m = "ThisShouldNotWork" + self._param["motor"].count_per_m = "ThisShouldNotWork" -class TestTachoMotorDriverNameValue(ptc.ParameterizedTestCase): +class TestTachoMotorDriverNameValue(ptc.ParameterizedTestCase): def test_driver_name_value(self): - self.assertEqual(self._param['motor'].driver_name, self._param['driver_name']) + self.assertEqual(self._param["motor"].driver_name, self._param["driver_name"]) def test_driver_name_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].driver_name = "ThisShouldNotWork" + self._param["motor"].driver_name = "ThisShouldNotWork" -class TestTachoMotorDutyCycleValue(ptc.ParameterizedTestCase): +class TestTachoMotorDutyCycleValue(ptc.ParameterizedTestCase): def test_duty_cycle_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].duty_cycle = "ThisShouldNotWork" + self._param["motor"].duty_cycle = "ThisShouldNotWork" def test_duty_cycle_value_after_reset(self): - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].duty_cycle, 0) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].duty_cycle, 0) -class TestTachoMotorDutyCycleSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorDutyCycleSpValue(ptc.ParameterizedTestCase): def test_duty_cycle_sp_large_negative(self): with self.assertRaises(IOError): - self._param['motor'].duty_cycle_sp = -101 + self._param["motor"].duty_cycle_sp = -101 def test_duty_cycle_sp_max_negative(self): - self._param['motor'].duty_cycle_sp = -100 - self.assertEqual(self._param['motor'].duty_cycle_sp, -100) + self._param["motor"].duty_cycle_sp = -100 + self.assertEqual(self._param["motor"].duty_cycle_sp, -100) def test_duty_cycle_sp_min_negative(self): - self._param['motor'].duty_cycle_sp = -1 - self.assertEqual(self._param['motor'].duty_cycle_sp, -1) + self._param["motor"].duty_cycle_sp = -1 + self.assertEqual(self._param["motor"].duty_cycle_sp, -1) def test_duty_cycle_sp_zero(self): - self._param['motor'].duty_cycle_sp = 0 - self.assertEqual(self._param['motor'].duty_cycle_sp, 0) + self._param["motor"].duty_cycle_sp = 0 + self.assertEqual(self._param["motor"].duty_cycle_sp, 0) def test_duty_cycle_sp_min_positive(self): - self._param['motor'].duty_cycle_sp = 1 - self.assertEqual(self._param['motor'].duty_cycle_sp, 1) + self._param["motor"].duty_cycle_sp = 1 + self.assertEqual(self._param["motor"].duty_cycle_sp, 1) def test_duty_cycle_sp_max_positive(self): - self._param['motor'].duty_cycle_sp = 100 - self.assertEqual(self._param['motor'].duty_cycle_sp, 100) + self._param["motor"].duty_cycle_sp = 100 + self.assertEqual(self._param["motor"].duty_cycle_sp, 100) def test_duty_cycle_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].duty_cycle_sp = 101 + self._param["motor"].duty_cycle_sp = 101 def test_duty_cycle_sp_after_reset(self): - self._param['motor'].duty_cycle_sp = 100 - self.assertEqual(self._param['motor'].duty_cycle_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].duty_cycle_sp, 0) + self._param["motor"].duty_cycle_sp = 100 + self.assertEqual(self._param["motor"].duty_cycle_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].duty_cycle_sp, 0) class TestTachoMotorMaxSpeedValue(ptc.ParameterizedTestCase): - def test_max_speed_value(self): - self.assertEqual(self._param['motor'].max_speed, motor_info[self._param['motor'].driver_name]['max_speed']) + self.assertEqual(self._param["motor"].max_speed, motor_info[self._param["motor"].driver_name]["max_speed"]) def test_max_speed_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].max_speed = "ThisShouldNotWork" + self._param["motor"].max_speed = "ThisShouldNotWork" -class TestTachoMotorPositionPValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionPValue(ptc.ParameterizedTestCase): def test_position_p_negative(self): with self.assertRaises(IOError): - self._param['motor'].position_p = -1 + self._param["motor"].position_p = -1 def test_position_p_zero(self): - self._param['motor'].position_p = 0 - self.assertEqual(self._param['motor'].position_p, 0) + self._param["motor"].position_p = 0 + self.assertEqual(self._param["motor"].position_p, 0) def test_position_p_positive(self): - self._param['motor'].position_p = 1 - self.assertEqual(self._param['motor'].position_p, 1) + self._param["motor"].position_p = 1 + self.assertEqual(self._param["motor"].position_p, 1) def test_position_p_after_reset(self): - self._param['motor'].position_p = 1 - self._param['motor'].command = 'reset' + self._param["motor"].position_p = 1 + self._param["motor"].command = "reset" - if self._param['hold_pid']: - expected = self._param['hold_pid']['kP'] + if self._param["hold_pid"]: + expected = self._param["hold_pid"]["kP"] else: - expected = motor_info[self._param['motor'].driver_name]['position_p'] - self.assertEqual(self._param['motor'].position_p, expected) + expected = motor_info[self._param["motor"].driver_name]["position_p"] + self.assertEqual(self._param["motor"].position_p, expected) -class TestTachoMotorPositionIValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionIValue(ptc.ParameterizedTestCase): def test_position_i_negative(self): with self.assertRaises(IOError): - self._param['motor'].position_i = -1 + self._param["motor"].position_i = -1 def test_position_i_zero(self): - self._param['motor'].position_i = 0 - self.assertEqual(self._param['motor'].position_i, 0) + self._param["motor"].position_i = 0 + self.assertEqual(self._param["motor"].position_i, 0) def test_position_i_positive(self): - self._param['motor'].position_i = 1 - self.assertEqual(self._param['motor'].position_i, 1) + self._param["motor"].position_i = 1 + self.assertEqual(self._param["motor"].position_i, 1) def test_position_i_after_reset(self): - self._param['motor'].position_i = 1 - self._param['motor'].command = 'reset' + self._param["motor"].position_i = 1 + self._param["motor"].command = "reset" - if self._param['hold_pid']: - expected = self._param['hold_pid']['kI'] + if self._param["hold_pid"]: + expected = self._param["hold_pid"]["kI"] else: - expected = motor_info[self._param['motor'].driver_name]['position_i'] - self.assertEqual(self._param['motor'].position_i, expected) + expected = motor_info[self._param["motor"].driver_name]["position_i"] + self.assertEqual(self._param["motor"].position_i, expected) -class TestTachoMotorPositionDValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionDValue(ptc.ParameterizedTestCase): def test_position_d_negative(self): with self.assertRaises(IOError): - self._param['motor'].position_d = -1 + self._param["motor"].position_d = -1 def test_position_d_zero(self): - self._param['motor'].position_d = 0 - self.assertEqual(self._param['motor'].position_d, 0) + self._param["motor"].position_d = 0 + self.assertEqual(self._param["motor"].position_d, 0) def test_position_d_positive(self): - self._param['motor'].position_d = 1 - self.assertEqual(self._param['motor'].position_d, 1) + self._param["motor"].position_d = 1 + self.assertEqual(self._param["motor"].position_d, 1) def test_position_d_after_reset(self): - self._param['motor'].position_d = 1 - self._param['motor'].command = 'reset' + self._param["motor"].position_d = 1 + self._param["motor"].command = "reset" - if self._param['hold_pid']: - expected = self._param['hold_pid']['kD'] + if self._param["hold_pid"]: + expected = self._param["hold_pid"]["kD"] else: - expected = motor_info[self._param['motor'].driver_name]['position_d'] - self.assertEqual(self._param['motor'].position_d, expected) + expected = motor_info[self._param["motor"].driver_name]["position_d"] + self.assertEqual(self._param["motor"].position_d, expected) -class TestTachoMotorPolarityValue(ptc.ParameterizedTestCase): +class TestTachoMotorPolarityValue(ptc.ParameterizedTestCase): def test_polarity_normal_value(self): - self._param['motor'].polarity = 'normal' - self.assertEqual(self._param['motor'].polarity, 'normal') + self._param["motor"].polarity = "normal" + self.assertEqual(self._param["motor"].polarity, "normal") def test_polarity_inversed_value(self): - self._param['motor'].polarity = 'inversed' - self.assertEqual(self._param['motor'].polarity, 'inversed') + self._param["motor"].polarity = "inversed" + self.assertEqual(self._param["motor"].polarity, "inversed") def test_polarity_illegal_value(self): with self.assertRaises(IOError): - self._param['motor'].polarity = "ThisShouldNotWork" + self._param["motor"].polarity = "ThisShouldNotWork" def test_polarity_after_reset(self): - if 'normal' == motor_info[self._param['motor'].driver_name]['polarity']: - self._param['motor'].polarity = 'inversed' + if "normal" == motor_info[self._param["motor"].driver_name]["polarity"]: + self._param["motor"].polarity = "inversed" else: - self._param['motor'].polarity = 'normal' - self._param['motor'].command = 'reset' + self._param["motor"].polarity = "normal" + self._param["motor"].command = "reset" - if 'normal' == motor_info[self._param['motor'].driver_name]['polarity']: - self.assertEqual(self._param['motor'].polarity, 'normal') + if "normal" == motor_info[self._param["motor"].driver_name]["polarity"]: + self.assertEqual(self._param["motor"].polarity, "normal") else: - self.assertEqual(self._param['motor'].polarity, 'inversed') + self.assertEqual(self._param["motor"].polarity, "inversed") -class TestTachoMotorPositionValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionValue(ptc.ParameterizedTestCase): def test_position_large_negative(self): - self._param['motor'].position = -1000000 - self.assertEqual(self._param['motor'].position, -1000000) + self._param["motor"].position = -1000000 + self.assertEqual(self._param["motor"].position, -1000000) def test_position_min_negative(self): - self._param['motor'].position = -1 - self.assertEqual(self._param['motor'].position, -1) + self._param["motor"].position = -1 + self.assertEqual(self._param["motor"].position, -1) def test_position_zero(self): - self._param['motor'].position = 0 - self.assertEqual(self._param['motor'].position, 0) + self._param["motor"].position = 0 + self.assertEqual(self._param["motor"].position, 0) def test_position_min_positive(self): - self._param['motor'].position = 1 - self.assertEqual(self._param['motor'].position, 1) + self._param["motor"].position = 1 + self.assertEqual(self._param["motor"].position, 1) def test_position_large_positive(self): - self._param['motor'].position = 1000000 - self.assertEqual(self._param['motor'].position, 1000000) + self._param["motor"].position = 1000000 + self.assertEqual(self._param["motor"].position, 1000000) def test_position_after_reset(self): - self._param['motor'].position = 100 - self.assertEqual(self._param['motor'].position, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].position, 0) + self._param["motor"].position = 100 + self.assertEqual(self._param["motor"].position, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].position, 0) -class TestTachoMotorPositionSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionSpValue(ptc.ParameterizedTestCase): def test_position_sp_large_negative(self): - self._param['motor'].position_sp = -1000000 - self.assertEqual(self._param['motor'].position_sp, -1000000) + self._param["motor"].position_sp = -1000000 + self.assertEqual(self._param["motor"].position_sp, -1000000) def test_position_sp_min_negative(self): - self._param['motor'].position_sp = -1 - self.assertEqual(self._param['motor'].position_sp, -1) + self._param["motor"].position_sp = -1 + self.assertEqual(self._param["motor"].position_sp, -1) def test_position_sp_zero(self): - self._param['motor'].position_sp = 0 - self.assertEqual(self._param['motor'].position_sp, 0) + self._param["motor"].position_sp = 0 + self.assertEqual(self._param["motor"].position_sp, 0) def test_position_sp_min_positive(self): - self._param['motor'].position_sp = 1 - self.assertEqual(self._param['motor'].position_sp, 1) + self._param["motor"].position_sp = 1 + self.assertEqual(self._param["motor"].position_sp, 1) def test_position_sp_large_positive(self): - self._param['motor'].position_sp = 1000000 - self.assertEqual(self._param['motor'].position_sp, 1000000) + self._param["motor"].position_sp = 1000000 + self.assertEqual(self._param["motor"].position_sp, 1000000) def test_position_sp_after_reset(self): - self._param['motor'].position_sp = 100 - self.assertEqual(self._param['motor'].position_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].position_sp, 0) + self._param["motor"].position_sp = 100 + self.assertEqual(self._param["motor"].position_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].position_sp, 0) -class TestTachoMotorRampDownSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorRampDownSpValue(ptc.ParameterizedTestCase): def test_ramp_down_sp_negative_value(self): with self.assertRaises(IOError): - self._param['motor'].ramp_down_sp = -1 + self._param["motor"].ramp_down_sp = -1 def test_ramp_down_sp_zero(self): - self._param['motor'].ramp_down_sp = 0 - self.assertEqual(self._param['motor'].ramp_down_sp, 0) + self._param["motor"].ramp_down_sp = 0 + self.assertEqual(self._param["motor"].ramp_down_sp, 0) def test_ramp_down_sp_min_positive(self): - self._param['motor'].ramp_down_sp = 1 - self.assertEqual(self._param['motor'].ramp_down_sp, 1) + self._param["motor"].ramp_down_sp = 1 + self.assertEqual(self._param["motor"].ramp_down_sp, 1) def test_ramp_down_sp_max_positive(self): - self._param['motor'].ramp_down_sp = 60000 - self.assertEqual(self._param['motor'].ramp_down_sp, 60000) + self._param["motor"].ramp_down_sp = 60000 + self.assertEqual(self._param["motor"].ramp_down_sp, 60000) def test_ramp_down_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].ramp_down_sp = 60001 + self._param["motor"].ramp_down_sp = 60001 def test_ramp_down_sp_after_reset(self): - self._param['motor'].ramp_down_sp = 100 - self.assertEqual(self._param['motor'].ramp_down_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].ramp_down_sp, 0) + self._param["motor"].ramp_down_sp = 100 + self.assertEqual(self._param["motor"].ramp_down_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].ramp_down_sp, 0) -class TestTachoMotorRampUpSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorRampUpSpValue(ptc.ParameterizedTestCase): def test_ramp_up_negative_value(self): with self.assertRaises(IOError): - self._param['motor'].ramp_up_sp = -1 + self._param["motor"].ramp_up_sp = -1 def test_ramp_up_sp_zero(self): - self._param['motor'].ramp_up_sp = 0 - self.assertEqual(self._param['motor'].ramp_up_sp, 0) + self._param["motor"].ramp_up_sp = 0 + self.assertEqual(self._param["motor"].ramp_up_sp, 0) def test_ramp_up_sp_min_positive(self): - self._param['motor'].ramp_up_sp = 1 - self.assertEqual(self._param['motor'].ramp_up_sp, 1) + self._param["motor"].ramp_up_sp = 1 + self.assertEqual(self._param["motor"].ramp_up_sp, 1) def test_ramp_up_sp_max_positive(self): - self._param['motor'].ramp_up_sp = 60000 - self.assertEqual(self._param['motor'].ramp_up_sp, 60000) + self._param["motor"].ramp_up_sp = 60000 + self.assertEqual(self._param["motor"].ramp_up_sp, 60000) def test_ramp_up_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].ramp_up_sp = 60001 + self._param["motor"].ramp_up_sp = 60001 def test_ramp_up_sp_after_reset(self): - self._param['motor'].ramp_up_sp = 100 - self.assertEqual(self._param['motor'].ramp_up_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].ramp_up_sp, 0) + self._param["motor"].ramp_up_sp = 100 + self.assertEqual(self._param["motor"].ramp_up_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].ramp_up_sp, 0) -class TestTachoMotorSpeedValue(ptc.ParameterizedTestCase): +class TestTachoMotorSpeedValue(ptc.ParameterizedTestCase): def test_speed_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].speed = 1 + self._param["motor"].speed = 1 def test_speed_value_after_reset(self): - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].speed, 0) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].speed, 0) -class TestTachoMotorSpeedSpValue(ptc.ParameterizedTestCase): - def test_speed_sp_large_negative(self): +class TestTachoMotorSpeedSpValue(ptc.ParameterizedTestCase): + def test_speed_sp_large_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_sp = -(motor_info[self._param['motor'].driver_name]['max_speed']+1) + self._param["motor"].speed_sp = -(motor_info[self._param["motor"].driver_name]["max_speed"] + 1) - def test_speed_sp_max_negative(self): - self._param['motor'].speed_sp = -motor_info[self._param['motor'].driver_name]['max_speed'] - self.assertEqual(self._param['motor'].speed_sp, -motor_info[self._param['motor'].driver_name]['max_speed']) + def test_speed_sp_max_negative(self): + self._param["motor"].speed_sp = -motor_info[self._param["motor"].driver_name]["max_speed"] + self.assertEqual(self._param["motor"].speed_sp, -motor_info[self._param["motor"].driver_name]["max_speed"]) - def test_speed_sp_min_negative(self): - self._param['motor'].speed_sp = -1 - self.assertEqual(self._param['motor'].speed_sp, -1) + def test_speed_sp_min_negative(self): + self._param["motor"].speed_sp = -1 + self.assertEqual(self._param["motor"].speed_sp, -1) - def test_speed_sp_zero(self): - self._param['motor'].speed_sp = 0 - self.assertEqual(self._param['motor'].speed_sp, 0) + def test_speed_sp_zero(self): + self._param["motor"].speed_sp = 0 + self.assertEqual(self._param["motor"].speed_sp, 0) - def test_speed_sp_min_positive(self): - self._param['motor'].speed_sp = 1 - self.assertEqual(self._param['motor'].speed_sp, 1) + def test_speed_sp_min_positive(self): + self._param["motor"].speed_sp = 1 + self.assertEqual(self._param["motor"].speed_sp, 1) - def test_speed_sp_max_positive(self): - self._param['motor'].speed_sp = (motor_info[self._param['motor'].driver_name]['max_speed']) - self.assertEqual(self._param['motor'].speed_sp, motor_info[self._param['motor'].driver_name]['max_speed']) + def test_speed_sp_max_positive(self): + self._param["motor"].speed_sp = motor_info[self._param["motor"].driver_name]["max_speed"] + self.assertEqual(self._param["motor"].speed_sp, motor_info[self._param["motor"].driver_name]["max_speed"]) - def test_speed_sp_large_positive(self): + def test_speed_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].speed_sp = motor_info[self._param['motor'].driver_name]['max_speed'] + 1 + self._param["motor"].speed_sp = motor_info[self._param["motor"].driver_name]["max_speed"] + 1 - def test_speed_sp_after_reset(self): - self._param['motor'].speed_sp = motor_info[self._param['motor'].driver_name]['max_speed']/2 - self.assertEqual(self._param['motor'].speed_sp, motor_info[self._param['motor'].driver_name]['max_speed']/2) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].speed_sp, 0) + def test_speed_sp_after_reset(self): + self._param["motor"].speed_sp = motor_info[self._param["motor"].driver_name]["max_speed"] / 2 + self.assertEqual(self._param["motor"].speed_sp, motor_info[self._param["motor"].driver_name]["max_speed"] / 2) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].speed_sp, 0) -class TestTachoMotorSpeedPValue(ptc.ParameterizedTestCase): +class TestTachoMotorSpeedPValue(ptc.ParameterizedTestCase): def test_speed_i_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_p = -1 + self._param["motor"].speed_p = -1 def test_speed_p_zero(self): - self._param['motor'].speed_p = 0 - self.assertEqual(self._param['motor'].speed_p, 0) + self._param["motor"].speed_p = 0 + self.assertEqual(self._param["motor"].speed_p, 0) def test_speed_p_positive(self): - self._param['motor'].speed_p = 1 - self.assertEqual(self._param['motor'].speed_p, 1) + self._param["motor"].speed_p = 1 + self.assertEqual(self._param["motor"].speed_p, 1) def test_speed_p_after_reset(self): - self._param['motor'].speed_p = 1 - self._param['motor'].command = 'reset' + self._param["motor"].speed_p = 1 + self._param["motor"].command = "reset" - if self._param['speed_pid']: - expected = self._param['speed_pid']['kP'] + if self._param["speed_pid"]: + expected = self._param["speed_pid"]["kP"] else: - expected = motor_info[self._param['motor'].driver_name]['speed_p'] - self.assertEqual(self._param['motor'].speed_p, expected) + expected = motor_info[self._param["motor"].driver_name]["speed_p"] + self.assertEqual(self._param["motor"].speed_p, expected) -class TestTachoMotorSpeedIValue(ptc.ParameterizedTestCase): +class TestTachoMotorSpeedIValue(ptc.ParameterizedTestCase): def test_speed_i_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_i = -1 + self._param["motor"].speed_i = -1 def test_speed_i_zero(self): - self._param['motor'].speed_i = 0 - self.assertEqual(self._param['motor'].speed_i, 0) + self._param["motor"].speed_i = 0 + self.assertEqual(self._param["motor"].speed_i, 0) def test_speed_i_positive(self): - self._param['motor'].speed_i = 1 - self.assertEqual(self._param['motor'].speed_i, 1) + self._param["motor"].speed_i = 1 + self.assertEqual(self._param["motor"].speed_i, 1) def test_speed_i_after_reset(self): - self._param['motor'].speed_i = 1 - self._param['motor'].command = 'reset' + self._param["motor"].speed_i = 1 + self._param["motor"].command = "reset" - if self._param['speed_pid']: - expected = self._param['speed_pid']['kI'] + if self._param["speed_pid"]: + expected = self._param["speed_pid"]["kI"] else: - expected = motor_info[self._param['motor'].driver_name]['speed_i'] - self.assertEqual(self._param['motor'].speed_i, expected) + expected = motor_info[self._param["motor"].driver_name]["speed_i"] + self.assertEqual(self._param["motor"].speed_i, expected) -class TestTachoMotorSpeedDValue(ptc.ParameterizedTestCase): +class TestTachoMotorSpeedDValue(ptc.ParameterizedTestCase): def test_speed_d_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_d = -1 + self._param["motor"].speed_d = -1 def test_speed_d_zero(self): - self._param['motor'].speed_d = 0 - self.assertEqual(self._param['motor'].speed_d, 0) + self._param["motor"].speed_d = 0 + self.assertEqual(self._param["motor"].speed_d, 0) def test_speed_d_positive(self): - self._param['motor'].speed_d = 1 - self.assertEqual(self._param['motor'].speed_d, 1) + self._param["motor"].speed_d = 1 + self.assertEqual(self._param["motor"].speed_d, 1) def test_speed_d_after_reset(self): - self._param['motor'].speed_d = 1 - self._param['motor'].command = 'reset' + self._param["motor"].speed_d = 1 + self._param["motor"].command = "reset" - if self._param['speed_pid']: - expected = self._param['speed_pid']['kD'] + if self._param["speed_pid"]: + expected = self._param["speed_pid"]["kD"] else: - expected = motor_info[self._param['motor'].driver_name]['speed_d'] - self.assertEqual(self._param['motor'].speed_d, expected) + expected = motor_info[self._param["motor"].driver_name]["speed_d"] + self.assertEqual(self._param["motor"].speed_d, expected) -class TestTachoMotorStateValue(ptc.ParameterizedTestCase): +class TestTachoMotorStateValue(ptc.ParameterizedTestCase): def test_state_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].state = 'ThisShouldNotWork' + self._param["motor"].state = "ThisShouldNotWork" def test_state_value_after_reset(self): - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].state, []) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].state, []) -class TestTachoMotorStopActionValue(ptc.ParameterizedTestCase): +class TestTachoMotorStopActionValue(ptc.ParameterizedTestCase): def test_stop_action_illegal(self): with self.assertRaises(IOError): - self._param['motor'].stop_action = 'ThisShouldNotWork' + self._param["motor"].stop_action = "ThisShouldNotWork" def test_stop_action_coast(self): - if 'coast' in self._param['stop_actions']: - self._param['motor'].stop_action = 'coast' - self.assertEqual(self._param['motor'].stop_action, 'coast') + if "coast" in self._param["stop_actions"]: + self._param["motor"].stop_action = "coast" + self.assertEqual(self._param["motor"].stop_action, "coast") else: with self.assertRaises(IOError): - self._param['motor'].stop_action = 'coast' + self._param["motor"].stop_action = "coast" def test_stop_action_brake(self): - if 'brake' in self._param['stop_actions']: - self._param['motor'].stop_action = 'brake' - self.assertEqual(self._param['motor'].stop_action, 'brake') + if "brake" in self._param["stop_actions"]: + self._param["motor"].stop_action = "brake" + self.assertEqual(self._param["motor"].stop_action, "brake") else: with self.assertRaises(IOError): - self._param['motor'].stop_action = 'brake' + self._param["motor"].stop_action = "brake" def test_stop_action_hold(self): - if 'hold' in self._param['stop_actions']: - self._param['motor'].stop_action = 'hold' - self.assertEqual(self._param['motor'].stop_action, 'hold') + if "hold" in self._param["stop_actions"]: + self._param["motor"].stop_action = "hold" + self.assertEqual(self._param["motor"].stop_action, "hold") else: with self.assertRaises(IOError): - self._param['motor'].stop_action = 'hold' + self._param["motor"].stop_action = "hold" def test_stop_action_after_reset(self): action = 1 # controller may only support one stop action - if len(self._param['stop_actions']) < 2: + if len(self._param["stop_actions"]) < 2: action = 0 - self._param['motor'].stop_action = self._param['stop_actions'][action] - self._param['motor'].action = 'reset' - self.assertEqual(self._param['motor'].stop_action, self._param['stop_actions'][0]) + self._param["motor"].stop_action = self._param["stop_actions"][action] + self._param["motor"].action = "reset" + self.assertEqual(self._param["motor"].stop_action, self._param["stop_actions"][0]) -class TestTachoMotorStopActionsValue(ptc.ParameterizedTestCase): +class TestTachoMotorStopActionsValue(ptc.ParameterizedTestCase): def test_stop_actions_value(self): - self.assertTrue(self._param['motor'].stop_actions == self._param['stop_actions']) + self.assertTrue(self._param["motor"].stop_actions == self._param["stop_actions"]) def test_stop_actions_value_is_read_only(self): with self.assertRaises(AttributeError): - self._param['motor'].stop_actions = "ThisShouldNotWork" + self._param["motor"].stop_actions = "ThisShouldNotWork" -class TestTachoMotorTimeSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorTimeSpValue(ptc.ParameterizedTestCase): def test_time_sp_negative(self): with self.assertRaises(IOError): - self._param['motor'].time_sp = -1 + self._param["motor"].time_sp = -1 def test_time_sp_zero(self): - self._param['motor'].time_sp = 0 - self.assertEqual(self._param['motor'].time_sp, 0) + self._param["motor"].time_sp = 0 + self.assertEqual(self._param["motor"].time_sp, 0) def test_time_sp_min_positive(self): - self._param['motor'].time_sp = 1 - self.assertEqual(self._param['motor'].time_sp, 1) + self._param["motor"].time_sp = 1 + self.assertEqual(self._param["motor"].time_sp, 1) def test_time_sp_large_positive(self): - self._param['motor'].time_sp = 1000000 - self.assertEqual(self._param['motor'].time_sp, 1000000) + self._param["motor"].time_sp = 1000000 + self.assertEqual(self._param["motor"].time_sp, 1000000) def test_time_sp_after_reset(self): - self._param['motor'].time_sp = 1 - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].time_sp, 0) + self._param["motor"].time_sp = 1 + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].time_sp, 0) + ev3_params = { - 'motor': ev3.Motor('outA'), - 'port': 'outA', - 'driver_name': 'lego-ev3-l-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset'], - 'stop_actions': ['coast', 'brake', 'hold'], + "motor": ev3.Motor("outA"), + "port": "outA", + "driver_name": "lego-ev3-l-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"], + "stop_actions": ["coast", "brake", "hold"], } evb_params = { - 'motor': ev3.Motor('evb-ports:outA'), - 'port': 'evb-ports:outA', - 'driver_name': 'lego-ev3-l-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset'], - 'stop_actions': ['coast', 'brake', 'hold'], + "motor": ev3.Motor("evb-ports:outA"), + "port": "evb-ports:outA", + "driver_name": "lego-ev3-l-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"], + "stop_actions": ["coast", "brake", "hold"], } brickpi_params = { - 'motor': ev3.Motor('ttyAMA0:MA'), - 'port': 'ttyAMA0:MA', - 'driver_name': 'lego-nxt-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset'], - 'stop_actions': ['coast', 'hold'], - 'speed_pid': { 'kP': 1000, 'kI': 60, 'kD': 0 }, - 'hold_pid': { 'kP': 20000, 'kI': 0, 'kD': 0 }, + "motor": ev3.Motor("ttyAMA0:MA"), + "port": "ttyAMA0:MA", + "driver_name": "lego-nxt-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"], + "stop_actions": ["coast", "hold"], + "speed_pid": {"kP": 1000, "kI": 60, "kD": 0}, + "hold_pid": {"kP": 20000, "kI": 0, "kD": 0}, } pistorms_params = { - 'motor': ev3.Motor('pistorms:BAM1'), - 'port': 'pistorms:BAM1', - 'driver_name': 'lego-nxt-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'stop', 'reset'], - 'stop_actions': ['coast', 'brake', 'hold'], - 'speed_pid': { 'kP': 1000, 'kI': 60, 'kD': 0 }, - 'hold_pid': { 'kP': 20000, 'kI': 0, 'kD': 0 }, + "motor": ev3.Motor("pistorms:BAM1"), + "port": "pistorms:BAM1", + "driver_name": "lego-nxt-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "stop", "reset"], + "stop_actions": ["coast", "brake", "hold"], + "speed_pid": {"kP": 1000, "kI": 60, "kD": 0}, + "hold_pid": {"kP": 20000, "kI": 0, "kD": 0}, } paramsA = pistorms_params -paramsA['motor'].command = 'reset' +paramsA["motor"].command = "reset" suite = unittest.TestSuite() @@ -594,5 +598,5 @@ def test_time_sp_after_reset(self): suite.addTest(ptc.ParameterizedTestCase.parameterize(TestTachoMotorTimeSpValue, param=paramsA)) -if __name__ == '__main__': - unittest.main(verbosity=2,buffer=True ).run(suite) +if __name__ == "__main__": + unittest.main(verbosity=2, buffer=True).run(suite) diff --git a/tests/motor/motor_run_direct_unittest.py b/tests/motor/motor_run_direct_unittest.py index c0d63de..b6b1523 100755 --- a/tests/motor/motor_run_direct_unittest.py +++ b/tests/motor/motor_run_direct_unittest.py @@ -12,8 +12,8 @@ from motor_info import motor_info -class TestMotorRunDirect(ptc.ParameterizedTestCase): +class TestMotorRunDirect(ptc.ParameterizedTestCase): @classmethod def setUpClass(cls): pass @@ -23,32 +23,35 @@ def tearDownClass(cls): pass def initialize_motor(self): - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - def run_direct_duty_cycles(self,stop_action,duty_cycles): - self._param['motor'].stop_action = stop_action - self._param['motor'].command = 'run-direct' + def run_direct_duty_cycles(self, stop_action, duty_cycles): + self._param["motor"].stop_action = stop_action + self._param["motor"].command = "run-direct" for i in duty_cycles: - self._param['motor'].duty_cycle_sp = i + self._param["motor"].duty_cycle_sp = i time.sleep(0.5) - self._param['motor'].command = 'stop' + self._param["motor"].command = "stop" def test_stop_coast_duty_cycles(self): self.initialize_motor() - self.run_direct_duty_cycles('coast',[0,20,40,60,80,100,66,33,0,-20,-40,-60,-80,-100,-66,-33,0]) + self.run_direct_duty_cycles("coast", [0, 20, 40, 60, 80, 100, 66, 33, 0, -20, -40, -60, -80, -100, -66, -33, 0]) + # Add all the tests to the suite - some tests apply only to certain drivers! -def AddTachoMotorRunDirectTestsToSuite( suite, driver_name, params ): + +def AddTachoMotorRunDirectTestsToSuite(suite, driver_name, params): suite.addTest(ptc.ParameterizedTestCase.parameterize(TestMotorRunDirect, param=params)) -if __name__ == '__main__': - params = { 'motor': ev3.Motor('outA'), 'port': 'outA', 'driver_name': 'lego-ev3-l-motor' } + +if __name__ == "__main__": + params = {"motor": ev3.Motor("outA"), "port": "outA", "driver_name": "lego-ev3-l-motor"} suite = unittest.TestSuite() - AddTachoMotorRunDirectTestsToSuite( suite, 'lego-ev3-l-motor', params ) + AddTachoMotorRunDirectTestsToSuite(suite, "lego-ev3-l-motor", params) - unittest.TextTestRunner(verbosity=1,buffer=True ).run(suite) + unittest.TextTestRunner(verbosity=1, buffer=True).run(suite) diff --git a/tests/motor/motor_unittest.py b/tests/motor/motor_unittest.py index 5933ed3..a2cb110 100644 --- a/tests/motor/motor_unittest.py +++ b/tests/motor/motor_unittest.py @@ -10,564 +10,563 @@ import parameterizedtestcase as ptc from motor_info import motor_info -class TestTachoMotorAddressValue(ptc.ParameterizedTestCase): +class TestTachoMotorAddressValue(ptc.ParameterizedTestCase): def test_address_value(self): # Use the class variable - self.assertEqual(self._param['motor'].address, self._param['port']) + self.assertEqual(self._param["motor"].address, self._param["port"]) def test_address_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].address = "ThisShouldNotWork" + self._param["motor"].address = "ThisShouldNotWork" -class TestTachoMotorCommandsValue(ptc.ParameterizedTestCase): +class TestTachoMotorCommandsValue(ptc.ParameterizedTestCase): def test_commands_value(self): - self.assertTrue(self._param['motor'].commands == self._param['commands']) + self.assertTrue(self._param["motor"].commands == self._param["commands"]) def test_commands_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].commands = "ThisShouldNotWork" + self._param["motor"].commands = "ThisShouldNotWork" -class TestTachoMotorCountPerRotValue(ptc.ParameterizedTestCase): +class TestTachoMotorCountPerRotValue(ptc.ParameterizedTestCase): def test_count_per_rot_value(self): # This is not available for linear motors - move to driver specific tests? - self.assertEqual(self._param['motor'].count_per_rot, 360) + self.assertEqual(self._param["motor"].count_per_rot, 360) def test_count_per_rot_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].count_per_rot = "ThisShouldNotWork" + self._param["motor"].count_per_rot = "ThisShouldNotWork" -class TestTachoMotorDriverNameValue(ptc.ParameterizedTestCase): +class TestTachoMotorDriverNameValue(ptc.ParameterizedTestCase): def test_driver_name_value(self): # move to driver specific tests? - self.assertEqual(self._param['motor'].driver_name, self._param['driver_name']) + self.assertEqual(self._param["motor"].driver_name, self._param["driver_name"]) def test_driver_name_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].driver_name = "ThisShouldNotWork" + self._param["motor"].driver_name = "ThisShouldNotWork" -class TestTachoMotorDutyCycleValue(ptc.ParameterizedTestCase): +class TestTachoMotorDutyCycleValue(ptc.ParameterizedTestCase): def test_duty_cycle_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].duty_cycle = "ThisShouldNotWork" + self._param["motor"].duty_cycle = "ThisShouldNotWork" def test_duty_cycle_value_after_reset(self): - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].duty_cycle, 0) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].duty_cycle, 0) -class TestTachoMotorDutyCycleSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorDutyCycleSpValue(ptc.ParameterizedTestCase): def test_duty_cycle_sp_large_negative(self): with self.assertRaises(IOError): - self._param['motor'].duty_cycle_sp = -101 + self._param["motor"].duty_cycle_sp = -101 def test_duty_cycle_sp_max_negative(self): - self._param['motor'].duty_cycle_sp = -100 - self.assertEqual(self._param['motor'].duty_cycle_sp, -100) + self._param["motor"].duty_cycle_sp = -100 + self.assertEqual(self._param["motor"].duty_cycle_sp, -100) def test_duty_cycle_sp_min_negative(self): - self._param['motor'].duty_cycle_sp = -1 - self.assertEqual(self._param['motor'].duty_cycle_sp, -1) + self._param["motor"].duty_cycle_sp = -1 + self.assertEqual(self._param["motor"].duty_cycle_sp, -1) def test_duty_cycle_sp_zero(self): - self._param['motor'].duty_cycle_sp = 0 - self.assertEqual(self._param['motor'].duty_cycle_sp, 0) + self._param["motor"].duty_cycle_sp = 0 + self.assertEqual(self._param["motor"].duty_cycle_sp, 0) def test_duty_cycle_sp_min_positive(self): - self._param['motor'].duty_cycle_sp = 1 - self.assertEqual(self._param['motor'].duty_cycle_sp, 1) + self._param["motor"].duty_cycle_sp = 1 + self.assertEqual(self._param["motor"].duty_cycle_sp, 1) def test_duty_cycle_sp_max_positive(self): - self._param['motor'].duty_cycle_sp = 100 - self.assertEqual(self._param['motor'].duty_cycle_sp, 100) + self._param["motor"].duty_cycle_sp = 100 + self.assertEqual(self._param["motor"].duty_cycle_sp, 100) def test_duty_cycle_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].duty_cycle_sp = 101 + self._param["motor"].duty_cycle_sp = 101 def test_duty_cycle_sp_after_reset(self): - self._param['motor'].duty_cycle_sp = 100 - self.assertEqual(self._param['motor'].duty_cycle_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].duty_cycle_sp, 0) + self._param["motor"].duty_cycle_sp = 100 + self.assertEqual(self._param["motor"].duty_cycle_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].duty_cycle_sp, 0) class TestTachoMotorMaxSpeedValue(ptc.ParameterizedTestCase): - def test_max_speed_value(self): # This is not available for linear motors - move to driver specific tests? - self.assertEqual(self._param['motor'].max_speed, motor_info[self._param['motor'].driver_name]['max_speed']) + self.assertEqual(self._param["motor"].max_speed, motor_info[self._param["motor"].driver_name]["max_speed"]) def test_max_speed_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].max_speed = "ThisShouldNotWork" + self._param["motor"].max_speed = "ThisShouldNotWork" -class TestTachoMotorPositionPValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionPValue(ptc.ParameterizedTestCase): def test_position_p_negative(self): with self.assertRaises(IOError): - self._param['motor'].position_p = -1 + self._param["motor"].position_p = -1 def test_position_p_zero(self): - self._param['motor'].position_p = 0 - self.assertEqual(self._param['motor'].position_p, 0) + self._param["motor"].position_p = 0 + self.assertEqual(self._param["motor"].position_p, 0) def test_position_p_positive(self): - self._param['motor'].position_p = 1 - self.assertEqual(self._param['motor'].position_p, 1) + self._param["motor"].position_p = 1 + self.assertEqual(self._param["motor"].position_p, 1) def test_position_p_after_reset(self): - self._param['motor'].position_p = 1 + self._param["motor"].position_p = 1 - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'hold_pid' in self._param: - expected = self._param['hold_pid']['kP'] + if "hold_pid" in self._param: + expected = self._param["hold_pid"]["kP"] else: - expected = motor_info[self._param['motor'].driver_name]['position_p'] - self.assertEqual(self._param['motor'].position_p, expected) + expected = motor_info[self._param["motor"].driver_name]["position_p"] + self.assertEqual(self._param["motor"].position_p, expected) -class TestTachoMotorPositionIValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionIValue(ptc.ParameterizedTestCase): def test_position_i_negative(self): with self.assertRaises(IOError): - self._param['motor'].position_i = -1 + self._param["motor"].position_i = -1 def test_position_i_zero(self): - self._param['motor'].position_i = 0 - self.assertEqual(self._param['motor'].position_i, 0) + self._param["motor"].position_i = 0 + self.assertEqual(self._param["motor"].position_i, 0) def test_position_i_positive(self): - self._param['motor'].position_i = 1 - self.assertEqual(self._param['motor'].position_i, 1) + self._param["motor"].position_i = 1 + self.assertEqual(self._param["motor"].position_i, 1) def test_position_i_after_reset(self): - self._param['motor'].position_i = 1 + self._param["motor"].position_i = 1 - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'hold_pid' in self._param: - expected = self._param['hold_pid']['kI'] + if "hold_pid" in self._param: + expected = self._param["hold_pid"]["kI"] else: - expected = motor_info[self._param['motor'].driver_name]['position_i'] - self.assertEqual(self._param['motor'].position_i, expected) + expected = motor_info[self._param["motor"].driver_name]["position_i"] + self.assertEqual(self._param["motor"].position_i, expected) -class TestTachoMotorPositionDValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionDValue(ptc.ParameterizedTestCase): def test_position_d_negative(self): with self.assertRaises(IOError): - self._param['motor'].position_d = -1 + self._param["motor"].position_d = -1 def test_position_d_zero(self): - self._param['motor'].position_d = 0 - self.assertEqual(self._param['motor'].position_d, 0) + self._param["motor"].position_d = 0 + self.assertEqual(self._param["motor"].position_d, 0) def test_position_d_positive(self): - self._param['motor'].position_d = 1 - self.assertEqual(self._param['motor'].position_d, 1) + self._param["motor"].position_d = 1 + self.assertEqual(self._param["motor"].position_d, 1) def test_position_d_after_reset(self): - self._param['motor'].position_d = 1 + self._param["motor"].position_d = 1 - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'hold_pid' in self._param: - expected = self._param['hold_pid']['kD'] + if "hold_pid" in self._param: + expected = self._param["hold_pid"]["kD"] else: - expected = motor_info[self._param['motor'].driver_name]['position_d'] - self.assertEqual(self._param['motor'].position_d, expected) + expected = motor_info[self._param["motor"].driver_name]["position_d"] + self.assertEqual(self._param["motor"].position_d, expected) -class TestTachoMotorPolarityValue(ptc.ParameterizedTestCase): +class TestTachoMotorPolarityValue(ptc.ParameterizedTestCase): def test_polarity_normal_value(self): - self._param['motor'].polarity = 'normal' - self.assertEqual(self._param['motor'].polarity, 'normal') + self._param["motor"].polarity = "normal" + self.assertEqual(self._param["motor"].polarity, "normal") def test_polarity_inversed_value(self): - self._param['motor'].polarity = 'inversed' - self.assertEqual(self._param['motor'].polarity, 'inversed') + self._param["motor"].polarity = "inversed" + self.assertEqual(self._param["motor"].polarity, "inversed") def test_polarity_illegal_value(self): with self.assertRaises(IOError): - self._param['motor'].polarity = "ThisShouldNotWork" + self._param["motor"].polarity = "ThisShouldNotWork" def test_polarity_after_reset(self): - if 'normal' == motor_info[self._param['motor'].driver_name]['polarity']: - self._param['motor'].polarity = 'inversed' + if "normal" == motor_info[self._param["motor"].driver_name]["polarity"]: + self._param["motor"].polarity = "inversed" else: - self._param['motor'].polarity = 'normal' + self._param["motor"].polarity = "normal" - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'normal' == motor_info[self._param['motor'].driver_name]['polarity']: - self.assertEqual(self._param['motor'].polarity, 'normal') + if "normal" == motor_info[self._param["motor"].driver_name]["polarity"]: + self.assertEqual(self._param["motor"].polarity, "normal") else: - self.assertEqual(self._param['motor'].polarity, 'inversed') + self.assertEqual(self._param["motor"].polarity, "inversed") -class TestTachoMotorPositionValue(ptc.ParameterizedTestCase): +class TestTachoMotorPositionValue(ptc.ParameterizedTestCase): def test_position_large_negative(self): - self._param['motor'].position = -1000000 - self.assertEqual(self._param['motor'].position, -1000000) + self._param["motor"].position = -1000000 + self.assertEqual(self._param["motor"].position, -1000000) def test_position_min_negative(self): - self._param['motor'].position = -1 - self.assertEqual(self._param['motor'].position, -1) + self._param["motor"].position = -1 + self.assertEqual(self._param["motor"].position, -1) def test_position_zero(self): - self._param['motor'].position = 0 - self.assertEqual(self._param['motor'].position, 0) + self._param["motor"].position = 0 + self.assertEqual(self._param["motor"].position, 0) def test_position_min_positive(self): - self._param['motor'].position = 1 - self.assertEqual(self._param['motor'].position, 1) + self._param["motor"].position = 1 + self.assertEqual(self._param["motor"].position, 1) def test_position_large_positive(self): - self._param['motor'].position = 1000000 - self.assertEqual(self._param['motor'].position, 1000000) + self._param["motor"].position = 1000000 + self.assertEqual(self._param["motor"].position, 1000000) def test_position_after_reset(self): - self._param['motor'].position = 100 - self.assertEqual(self._param['motor'].position, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].position, 0) + self._param["motor"].position = 100 + self.assertEqual(self._param["motor"].position, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].position, 0) class TestTachoMotorPositionSpValue(ptc.ParameterizedTestCase): - def test_position_sp_large_negative(self): - self._param['motor'].position_sp = -1000000 - self.assertEqual(self._param['motor'].position_sp, -1000000) + self._param["motor"].position_sp = -1000000 + self.assertEqual(self._param["motor"].position_sp, -1000000) def test_position_sp_min_negative(self): - self._param['motor'].position_sp = -1 - self.assertEqual(self._param['motor'].position_sp, -1) + self._param["motor"].position_sp = -1 + self.assertEqual(self._param["motor"].position_sp, -1) def test_position_sp_zero(self): - self._param['motor'].position_sp = 0 - self.assertEqual(self._param['motor'].position_sp, 0) + self._param["motor"].position_sp = 0 + self.assertEqual(self._param["motor"].position_sp, 0) def test_position_sp_min_positive(self): - self._param['motor'].position_sp = 1 - self.assertEqual(self._param['motor'].position_sp, 1) + self._param["motor"].position_sp = 1 + self.assertEqual(self._param["motor"].position_sp, 1) def test_position_sp_large_positive(self): - self._param['motor'].position_sp = 1000000 - self.assertEqual(self._param['motor'].position_sp, 1000000) + self._param["motor"].position_sp = 1000000 + self.assertEqual(self._param["motor"].position_sp, 1000000) def test_position_sp_after_reset(self): - self._param['motor'].position_sp = 100 - self.assertEqual(self._param['motor'].position_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].position_sp, 0) + self._param["motor"].position_sp = 100 + self.assertEqual(self._param["motor"].position_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].position_sp, 0) -class TestTachoMotorRampDownSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorRampDownSpValue(ptc.ParameterizedTestCase): def test_ramp_down_sp_negative_value(self): with self.assertRaises(IOError): - self._param['motor'].ramp_down_sp = -1 + self._param["motor"].ramp_down_sp = -1 def test_ramp_down_sp_zero(self): - self._param['motor'].ramp_down_sp = 0 - self.assertEqual(self._param['motor'].ramp_down_sp, 0) + self._param["motor"].ramp_down_sp = 0 + self.assertEqual(self._param["motor"].ramp_down_sp, 0) def test_ramp_down_sp_min_positive(self): - self._param['motor'].ramp_down_sp = 1 - self.assertEqual(self._param['motor'].ramp_down_sp, 1) + self._param["motor"].ramp_down_sp = 1 + self.assertEqual(self._param["motor"].ramp_down_sp, 1) def test_ramp_down_sp_max_positive(self): - self._param['motor'].ramp_down_sp = 60000 - self.assertEqual(self._param['motor'].ramp_down_sp, 60000) + self._param["motor"].ramp_down_sp = 60000 + self.assertEqual(self._param["motor"].ramp_down_sp, 60000) def test_ramp_down_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].ramp_down_sp = 60001 + self._param["motor"].ramp_down_sp = 60001 def test_ramp_down_sp_after_reset(self): - self._param['motor'].ramp_down_sp = 100 - self.assertEqual(self._param['motor'].ramp_down_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].ramp_down_sp, 0) - -class TestTachoMotorRampUpSpValue(ptc.ParameterizedTestCase): + self._param["motor"].ramp_down_sp = 100 + self.assertEqual(self._param["motor"].ramp_down_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].ramp_down_sp, 0) + +class TestTachoMotorRampUpSpValue(ptc.ParameterizedTestCase): def test_ramp_up_negative_value(self): with self.assertRaises(IOError): - self._param['motor'].ramp_up_sp = -1 + self._param["motor"].ramp_up_sp = -1 def test_ramp_up_sp_zero(self): - self._param['motor'].ramp_up_sp = 0 - self.assertEqual(self._param['motor'].ramp_up_sp, 0) + self._param["motor"].ramp_up_sp = 0 + self.assertEqual(self._param["motor"].ramp_up_sp, 0) def test_ramp_up_sp_min_positive(self): - self._param['motor'].ramp_up_sp = 1 - self.assertEqual(self._param['motor'].ramp_up_sp, 1) + self._param["motor"].ramp_up_sp = 1 + self.assertEqual(self._param["motor"].ramp_up_sp, 1) def test_ramp_up_sp_max_positive(self): - self._param['motor'].ramp_up_sp = 60000 - self.assertEqual(self._param['motor'].ramp_up_sp, 60000) + self._param["motor"].ramp_up_sp = 60000 + self.assertEqual(self._param["motor"].ramp_up_sp, 60000) def test_ramp_up_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].ramp_up_sp = 60001 + self._param["motor"].ramp_up_sp = 60001 def test_ramp_up_sp_after_reset(self): - self._param['motor'].ramp_up_sp = 100 - self.assertEqual(self._param['motor'].ramp_up_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].ramp_up_sp, 0) - -class TestTachoMotorSpeedValue(ptc.ParameterizedTestCase): + self._param["motor"].ramp_up_sp = 100 + self.assertEqual(self._param["motor"].ramp_up_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].ramp_up_sp, 0) + +class TestTachoMotorSpeedValue(ptc.ParameterizedTestCase): def test_speed_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].speed = 1 + self._param["motor"].speed = 1 def test_speed_value_after_reset(self): - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].speed, 0) - -class TestTachoMotorSpeedSpValue(ptc.ParameterizedTestCase): + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].speed, 0) + - def test_speed_sp_large_negative(self): +class TestTachoMotorSpeedSpValue(ptc.ParameterizedTestCase): + def test_speed_sp_large_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_sp = -(motor_info[self._param['motor'].driver_name]['max_speed']+1) + self._param["motor"].speed_sp = -(motor_info[self._param["motor"].driver_name]["max_speed"] + 1) - def test_speed_sp_max_negative(self): - self._param['motor'].speed_sp = -motor_info[self._param['motor'].driver_name]['max_speed'] - self.assertEqual(self._param['motor'].speed_sp, -motor_info[self._param['motor'].driver_name]['max_speed']) + def test_speed_sp_max_negative(self): + self._param["motor"].speed_sp = -motor_info[self._param["motor"].driver_name]["max_speed"] + self.assertEqual(self._param["motor"].speed_sp, -motor_info[self._param["motor"].driver_name]["max_speed"]) - def test_speed_sp_min_negative(self): - self._param['motor'].speed_sp = -1 - self.assertEqual(self._param['motor'].speed_sp, -1) + def test_speed_sp_min_negative(self): + self._param["motor"].speed_sp = -1 + self.assertEqual(self._param["motor"].speed_sp, -1) - def test_speed_sp_zero(self): - self._param['motor'].speed_sp = 0 - self.assertEqual(self._param['motor'].speed_sp, 0) + def test_speed_sp_zero(self): + self._param["motor"].speed_sp = 0 + self.assertEqual(self._param["motor"].speed_sp, 0) - def test_speed_sp_min_positive(self): - self._param['motor'].speed_sp = 1 - self.assertEqual(self._param['motor'].speed_sp, 1) + def test_speed_sp_min_positive(self): + self._param["motor"].speed_sp = 1 + self.assertEqual(self._param["motor"].speed_sp, 1) - def test_speed_sp_max_positive(self): - self._param['motor'].speed_sp = (motor_info[self._param['motor'].driver_name]['max_speed']) - self.assertEqual(self._param['motor'].speed_sp, motor_info[self._param['motor'].driver_name]['max_speed']) + def test_speed_sp_max_positive(self): + self._param["motor"].speed_sp = motor_info[self._param["motor"].driver_name]["max_speed"] + self.assertEqual(self._param["motor"].speed_sp, motor_info[self._param["motor"].driver_name]["max_speed"]) - def test_speed_sp_large_positive(self): + def test_speed_sp_large_positive(self): with self.assertRaises(IOError): - self._param['motor'].speed_sp = motor_info[self._param['motor'].driver_name]['max_speed'] + 1 - - def test_speed_sp_after_reset(self): - self._param['motor'].speed_sp = 100 - self.assertEqual(self._param['motor'].speed_sp, 100) - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].speed_sp, 0) - -class TestTachoMotorSpeedPValue(ptc.ParameterizedTestCase): + self._param["motor"].speed_sp = motor_info[self._param["motor"].driver_name]["max_speed"] + 1 + + def test_speed_sp_after_reset(self): + self._param["motor"].speed_sp = 100 + self.assertEqual(self._param["motor"].speed_sp, 100) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].speed_sp, 0) + +class TestTachoMotorSpeedPValue(ptc.ParameterizedTestCase): def test_speed_i_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_p = -1 + self._param["motor"].speed_p = -1 def test_speed_p_zero(self): - self._param['motor'].speed_p = 0 - self.assertEqual(self._param['motor'].speed_p, 0) + self._param["motor"].speed_p = 0 + self.assertEqual(self._param["motor"].speed_p, 0) def test_speed_p_positive(self): - self._param['motor'].speed_p = 1 - self.assertEqual(self._param['motor'].speed_p, 1) + self._param["motor"].speed_p = 1 + self.assertEqual(self._param["motor"].speed_p, 1) def test_speed_p_after_reset(self): - self._param['motor'].speed_p = 1 + self._param["motor"].speed_p = 1 - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'speed_pid' in self._param: - expected = self._param['speed_pid']['kP'] + if "speed_pid" in self._param: + expected = self._param["speed_pid"]["kP"] else: - expected = motor_info[self._param['motor'].driver_name]['speed_p'] - self.assertEqual(self._param['motor'].speed_p, expected) + expected = motor_info[self._param["motor"].driver_name]["speed_p"] + self.assertEqual(self._param["motor"].speed_p, expected) -class TestTachoMotorSpeedIValue(ptc.ParameterizedTestCase): +class TestTachoMotorSpeedIValue(ptc.ParameterizedTestCase): def test_speed_i_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_i = -1 + self._param["motor"].speed_i = -1 def test_speed_i_zero(self): - self._param['motor'].speed_i = 0 - self.assertEqual(self._param['motor'].speed_i, 0) + self._param["motor"].speed_i = 0 + self.assertEqual(self._param["motor"].speed_i, 0) def test_speed_i_positive(self): - self._param['motor'].speed_i = 1 - self.assertEqual(self._param['motor'].speed_i, 1) + self._param["motor"].speed_i = 1 + self.assertEqual(self._param["motor"].speed_i, 1) def test_speed_i_after_reset(self): - self._param['motor'].speed_i = 1 + self._param["motor"].speed_i = 1 - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'speed_pid' in self._param: - expected = self._param['speed_pid']['kI'] + if "speed_pid" in self._param: + expected = self._param["speed_pid"]["kI"] else: - expected = motor_info[self._param['motor'].driver_name]['speed_i'] - self.assertEqual(self._param['motor'].speed_i, expected) + expected = motor_info[self._param["motor"].driver_name]["speed_i"] + self.assertEqual(self._param["motor"].speed_i, expected) -class TestTachoMotorSpeedDValue(ptc.ParameterizedTestCase): +class TestTachoMotorSpeedDValue(ptc.ParameterizedTestCase): def test_speed_d_negative(self): with self.assertRaises(IOError): - self._param['motor'].speed_d = -1 + self._param["motor"].speed_d = -1 def test_speed_d_zero(self): - self._param['motor'].speed_d = 0 - self.assertEqual(self._param['motor'].speed_d, 0) + self._param["motor"].speed_d = 0 + self.assertEqual(self._param["motor"].speed_d, 0) def test_speed_d_positive(self): - self._param['motor'].speed_d = 1 - self.assertEqual(self._param['motor'].speed_d, 1) + self._param["motor"].speed_d = 1 + self.assertEqual(self._param["motor"].speed_d, 1) def test_speed_d_after_reset(self): - self._param['motor'].speed_d = 1 + self._param["motor"].speed_d = 1 - self._param['motor'].command = 'reset' + self._param["motor"].command = "reset" - if 'speed_pid' in self._param: - expected = self._param['speed_pid']['kD'] + if "speed_pid" in self._param: + expected = self._param["speed_pid"]["kD"] else: - expected = motor_info[self._param['motor'].driver_name]['speed_d'] - self.assertEqual(self._param['motor'].speed_d, expected) + expected = motor_info[self._param["motor"].driver_name]["speed_d"] + self.assertEqual(self._param["motor"].speed_d, expected) -class TestTachoMotorStateValue(ptc.ParameterizedTestCase): +class TestTachoMotorStateValue(ptc.ParameterizedTestCase): def test_state_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].state = 'ThisShouldNotWork' + self._param["motor"].state = "ThisShouldNotWork" def test_state_value_after_reset(self): - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].state, []) + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].state, []) + # def test_stop_action_value(self): # self.assertEqual(self._param['motor'].stop_action, 'coast') class TestTachoMotorStopCommandValue(ptc.ParameterizedTestCase): - def test_stop_action_illegal(self): with self.assertRaises(IOError): - self._param['motor'].stop_action = 'ThisShouldNotWork' + self._param["motor"].stop_action = "ThisShouldNotWork" def test_stop_action_coast(self): - if 'coast' in self._param['stop_actions']: - self._param['motor'].stop_action = 'coast' - self.assertEqual(self._param['motor'].stop_action, 'coast') + if "coast" in self._param["stop_actions"]: + self._param["motor"].stop_action = "coast" + self.assertEqual(self._param["motor"].stop_action, "coast") else: with self.assertRaises(IOError): - self._param['motor'].stop_action = 'coast' + self._param["motor"].stop_action = "coast" def test_stop_action_brake(self): - if 'brake' in self._param['stop_actions']: - self._param['motor'].stop_action = 'brake' - self.assertEqual(self._param['motor'].stop_action, 'brake') + if "brake" in self._param["stop_actions"]: + self._param["motor"].stop_action = "brake" + self.assertEqual(self._param["motor"].stop_action, "brake") else: with self.assertRaises(IOError): - self._param['motor'].stop_action = 'brake' + self._param["motor"].stop_action = "brake" def test_stop_action_hold(self): - if 'hold' in self._param['stop_actions']: - self._param['motor'].stop_action = 'hold' - self.assertEqual(self._param['motor'].stop_action, 'hold') + if "hold" in self._param["stop_actions"]: + self._param["motor"].stop_action = "hold" + self.assertEqual(self._param["motor"].stop_action, "hold") else: with self.assertRaises(IOError): - self._param['motor'].stop_action = 'hold' + self._param["motor"].stop_action = "hold" def test_stop_action_after_reset(self): action = 1 # controller may only support one stop action - if len(self._param['stop_actions']) < 2: + if len(self._param["stop_actions"]) < 2: action = 0 - self._param['motor'].stop_action = self._param['stop_actions'][action] - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].stop_action, self._param['stop_actions'][0]) + self._param["motor"].stop_action = self._param["stop_actions"][action] + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].stop_action, self._param["stop_actions"][0]) -class TestTachoMotorStopCommandsValue(ptc.ParameterizedTestCase): +class TestTachoMotorStopCommandsValue(ptc.ParameterizedTestCase): def test_stop_actions_value(self): - self.assertTrue(self._param['motor'].stop_actions == self._param['stop_actions']) + self.assertTrue(self._param["motor"].stop_actions == self._param["stop_actions"]) def test_stop_actions_value_is_read_only(self): # Use the class variable with self.assertRaises(AttributeError): - self._param['motor'].stop_actions = "ThisShouldNotWork" + self._param["motor"].stop_actions = "ThisShouldNotWork" -class TestTachoMotorTimeSpValue(ptc.ParameterizedTestCase): +class TestTachoMotorTimeSpValue(ptc.ParameterizedTestCase): def test_time_sp_negative(self): with self.assertRaises(IOError): - self._param['motor'].time_sp = -1 + self._param["motor"].time_sp = -1 def test_time_sp_zero(self): - self._param['motor'].time_sp = 0 - self.assertEqual(self._param['motor'].time_sp, 0) + self._param["motor"].time_sp = 0 + self.assertEqual(self._param["motor"].time_sp, 0) def test_time_sp_min_positive(self): - self._param['motor'].time_sp = 1 - self.assertEqual(self._param['motor'].time_sp, 1) + self._param["motor"].time_sp = 1 + self.assertEqual(self._param["motor"].time_sp, 1) def test_time_sp_large_positive(self): - self._param['motor'].time_sp = 1000000 - self.assertEqual(self._param['motor'].time_sp, 1000000) + self._param["motor"].time_sp = 1000000 + self.assertEqual(self._param["motor"].time_sp, 1000000) def test_time_sp_after_reset(self): - self._param['motor'].time_sp = 1 - self._param['motor'].command = 'reset' - self.assertEqual(self._param['motor'].time_sp, 0) + self._param["motor"].time_sp = 1 + self._param["motor"].command = "reset" + self.assertEqual(self._param["motor"].time_sp, 0) + ev3_params = { - 'motor': ev3.Motor('outA'), - 'port': 'outA', - 'driver_name': 'lego-ev3-l-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset'], - 'stop_actions': ['coast', 'brake', 'hold'], + "motor": ev3.Motor("outA"), + "port": "outA", + "driver_name": "lego-ev3-l-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"], + "stop_actions": ["coast", "brake", "hold"], } evb_params = { - 'motor': ev3.Motor('evb-ports:outA'), - 'port': 'evb-ports:outA', - 'driver_name': 'lego-ev3-l-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset'], - 'stop_actions': ['coast', 'brake', 'hold'], + "motor": ev3.Motor("evb-ports:outA"), + "port": "evb-ports:outA", + "driver_name": "lego-ev3-l-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"], + "stop_actions": ["coast", "brake", "hold"], } brickpi_params = { - 'motor': ev3.Motor('ttyAMA0:MA'), - 'port': 'ttyAMA0:MA', - 'driver_name': 'lego-nxt-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'run-direct', 'stop', 'reset'], - 'stop_actions': ['coast', 'hold'], - 'speed_pid': { 'kP': 1000, 'kI': 60, 'kD': 0 }, - 'hold_pid': { 'kP': 20000, 'kI': 0, 'kD': 0 }, + "motor": ev3.Motor("ttyAMA0:MA"), + "port": "ttyAMA0:MA", + "driver_name": "lego-nxt-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "run-direct", "stop", "reset"], + "stop_actions": ["coast", "hold"], + "speed_pid": {"kP": 1000, "kI": 60, "kD": 0}, + "hold_pid": {"kP": 20000, "kI": 0, "kD": 0}, } pistorms_params = { - 'motor': ev3.Motor('pistorms:BAM1'), - 'port': 'pistorms:BAM1', - 'driver_name': 'lego-nxt-motor', - 'commands': ['run-forever', 'run-to-abs-pos', 'run-to-rel-pos', 'run-timed', 'stop', 'reset'], - 'stop_actions': ['coast', 'brake', 'hold'], - 'speed_pid': { 'kP': 1000, 'kI': 60, 'kD': 0 }, - 'hold_pid': { 'kP': 20000, 'kI': 0, 'kD': 0 }, + "motor": ev3.Motor("pistorms:BAM1"), + "port": "pistorms:BAM1", + "driver_name": "lego-nxt-motor", + "commands": ["run-forever", "run-to-abs-pos", "run-to-rel-pos", "run-timed", "stop", "reset"], + "stop_actions": ["coast", "brake", "hold"], + "speed_pid": {"kP": 1000, "kI": 60, "kD": 0}, + "hold_pid": {"kP": 20000, "kI": 0, "kD": 0}, } paramsA = ev3_params -paramsA['motor'].command = 'reset' +paramsA["motor"].command = "reset" suite = unittest.TestSuite() @@ -596,18 +595,18 @@ def test_time_sp_after_reset(self): suite.addTest(ptc.ParameterizedTestCase.parameterize(TestTachoMotorTimeSpValue, param=paramsA)) -if __name__ == '__main__': - unittest.TextTestRunner(verbosity=1,buffer=True ).run(suite) +if __name__ == "__main__": + unittest.TextTestRunner(verbosity=1, buffer=True).run(suite) exit() # Move these up later -class TestMotorRelativePosition(unittest.TestCase): +class TestMotorRelativePosition(unittest.TestCase): @classmethod def setUpClass(cls): - cls._motor = ev3.Motor('outA') + cls._motor = ev3.Motor("outA") cls._motor.speed_sp = 400 cls._motor.ramp_up_sp = 300 cls._motor.ramp_down_sp = 300 @@ -621,31 +620,31 @@ def tearDownClass(cls): @unittest.skip("Skipping coast mode - always fails") def test_stop_coast(self): - self._motor.stop_action = 'coast' - self._motor.command = 'run-to-rel-pos' + self._motor.stop_action = "coast" + self._motor.command = "run-to-rel-pos" time.sleep(1) self.assertGreaterEqual(1, abs(self._motor.position - self._motor.position_sp)) def test_stop_brake(self): - self._motor.stop_action = 'brake' + self._motor.stop_action = "brake" self._motor.position = 0 - for i in range(1,5): - self._motor.command = 'run-to-rel-pos' + for i in range(1, 5): + self._motor.command = "run-to-rel-pos" time.sleep(1) print(self._motor.position) self.assertGreaterEqual(8, abs(self._motor.position - (i * self._motor.position_sp))) def test_stop_hold(self): - self._motor.stop_action = 'hold' + self._motor.stop_action = "hold" self._motor.position = 0 - for i in range(1,5): - self._motor.command = 'run-to-rel-pos' + for i in range(1, 5): + self._motor.command = "run-to-rel-pos" time.sleep(1) print(self._motor.position) self.assertGreaterEqual(1, abs(self._motor.position - (i * self._motor.position_sp))) -if __name__ == '__main__': - unittest.main(verbosity=2,buffer=True ).run(suite) +if __name__ == "__main__": + unittest.main(verbosity=2, buffer=True).run(suite) diff --git a/tests/motor/parameterizedtestcase.py b/tests/motor/parameterizedtestcase.py index 3c59cd5..e16aaad 100644 --- a/tests/motor/parameterizedtestcase.py +++ b/tests/motor/parameterizedtestcase.py @@ -1,10 +1,12 @@ import unittest + class ParameterizedTestCase(unittest.TestCase): """ TestCase classes that want to be parametrized should inherit from this class. """ - def __init__(self, methodName='runTest', param=None): + + def __init__(self, methodName="runTest", param=None): super(ParameterizedTestCase, self).__init__(methodName) self._param = param @@ -17,5 +19,5 @@ def parameterize(testcase_class, param=None): testnames = testloader.getTestCaseNames(testcase_class) suite = unittest.TestSuite() for name in testnames: - suite.addTest(testcase_class(name,param=param)) + suite.addTest(testcase_class(name, param=param)) return suite diff --git a/tests/motor/plot_matplotlib.py b/tests/motor/plot_matplotlib.py index b6f7d62..3e29512 100644 --- a/tests/motor/plot_matplotlib.py +++ b/tests/motor/plot_matplotlib.py @@ -2,9 +2,8 @@ import json import argparse -parser = argparse.ArgumentParser(description='Plot ev3dev datalogs.') -parser.add_argument('infile', - help='the input file to be logged') +parser = argparse.ArgumentParser(description="Plot ev3dev datalogs.") +parser.add_argument("infile", help="the input file to be logged") args = parser.parse_args() @@ -14,59 +13,85 @@ # http://matplotlib.org/examples/pylab_examples/subplots_demo.html # These are the "Tableau 20" colors as RGB. -tableau20 = [(31, 119, 180), (174, 199, 232), (255, 127, 14), (255, 187, 120), - (44, 160, 44), (152, 223, 138), (214, 39, 40), (255, 152, 150), - (148, 103, 189), (197, 176, 213), (140, 86, 75), (196, 156, 148), - (227, 119, 194), (247, 182, 210), (127, 127, 127), (199, 199, 199), - (188, 189, 34), (219, 219, 141), (23, 190, 207), (158, 218, 229)] +tableau20 = [ + (31, 119, 180), + (174, 199, 232), + (255, 127, 14), + (255, 187, 120), + (44, 160, 44), + (152, 223, 138), + (214, 39, 40), + (255, 152, 150), + (148, 103, 189), + (197, 176, 213), + (140, 86, 75), + (196, 156, 148), + (227, 119, 194), + (247, 182, 210), + (127, 127, 127), + (199, 199, 199), + (188, 189, 34), + (219, 219, 141), + (23, 190, 207), + (158, 218, 229), +] # Scale the RGB values to the [0, 1] range, which is the format matplotlib accepts. for i in range(len(tableau20)): r, g, b = tableau20[i] - tableau20[i] = (r / 255., g / 255., b / 255.) + tableau20[i] = (r / 255.0, g / 255.0, b / 255.0) -plt.style.use(['dark_background']) +plt.style.use(["dark_background"]) -test = json.loads( open( args.infile ).read() ) +test = json.loads(open(args.infile).read()) values = {} # Extract the data from the log in a format that's useful for plotting -for k,d in test['data'].items(): - values['k'] = {} - values['k']['x'] = [row[0] for row in d] - values['k']['y'] = [] +for k, d in test["data"].items(): + values["k"] = {} + values["k"]["x"] = [row[0] for row in d] + values["k"]["y"] = [] - for i,a in enumerate(test['meta']['ports'][k]['log_attributes']): - values['k']['y'].append( {'name': a, 'values': [row[1][i] for row in d]}) + for i, a in enumerate(test["meta"]["ports"][k]["log_attributes"]): + values["k"]["y"].append({"name": a, "values": [row[1][i] for row in d]}) f, axarr = plt.subplots(3, sharex=True) - axarr[2].set_xlabel('Time (seconds)') - - f.text(.95,0, args.infile, - fontsize=10, - horizontalalignment='left', - verticalalignment='center' ) - - f.text(.5,1, "{0} - {1}".format( test['meta']['title'], k), - fontsize=14, - horizontalalignment='center', - verticalalignment='center' ) - - f.text(.5,.96, "{0}".format( test['meta']['subtitle']), - fontsize=10, - horizontalalignment='center', - verticalalignment='center' ) - - f.text(.92,.5, "{0}".format( test['meta']['notes']), - fontsize=10, - horizontalalignment='left', - verticalalignment='center' ) + axarr[2].set_xlabel("Time (seconds)") + + f.text(0.95, 0, args.infile, fontsize=10, horizontalalignment="left", verticalalignment="center") + + f.text( + 0.5, + 1, + "{0} - {1}".format(test["meta"]["title"], k), + fontsize=14, + horizontalalignment="center", + verticalalignment="center", + ) + + f.text( + 0.5, + 0.96, + "{0}".format(test["meta"]["subtitle"]), + fontsize=10, + horizontalalignment="center", + verticalalignment="center", + ) + + f.text( + 0.92, + 0.5, + "{0}".format(test["meta"]["notes"]), + fontsize=10, + horizontalalignment="left", + verticalalignment="center", + ) # Clean up the chartjunk - for i,ax in enumerate(axarr): + for i, ax in enumerate(axarr): print(i, ax) # Remove the plot frame lines. They are unnecessary chartjunk. ax.spines["top"].set_visible(False) @@ -76,12 +101,16 @@ ax.get_xaxis().tick_bottom() ax.get_yaxis().tick_left() - axarr[i].plot(values['k']['x'],values['k']['y'][i]['values'], lw=1.5, color=tableau20[i] ) - axarr[i].text(.95,1, "{0}".format( values['k']['y'][i]['name'] ), - fontsize=14, - color=tableau20[i], - horizontalalignment='right', - verticalalignment='center', - transform = axarr[i].transAxes) - - plt.savefig("{0}-{1}.png".format(args.infile,k), bbox_inches="tight") + axarr[i].plot(values["k"]["x"], values["k"]["y"][i]["values"], lw=1.5, color=tableau20[i]) + axarr[i].text( + 0.95, + 1, + "{0}".format(values["k"]["y"][i]["name"]), + fontsize=14, + color=tableau20[i], + horizontalalignment="right", + verticalalignment="center", + transform=axarr[i].transAxes, + ) + + plt.savefig("{0}-{1}.png".format(args.infile, k), bbox_inches="tight") diff --git a/utils/console_fonts.py b/utils/console_fonts.py index 8bbaae6..7eadd62 100644 --- a/utils/console_fonts.py +++ b/utils/console_fonts.py @@ -31,9 +31,10 @@ def show_fonts(): console.set_font(font, True) console.text_at(font, 1, 1, False, True) console.clear_to_eol() - console.text_at("{}, {}".format(console.columns, console.rows), - column=2, row=4, reset_console=False, inverse=False) - print("{}, {}, \"{}\"".format(console.columns, console.rows, font), file=stderr) + console.text_at( + "{}, {}".format(console.columns, console.rows), column=2, row=4, reset_console=False, inverse=False + ) + print('{}, {}, "{}"'.format(console.columns, console.rows, font), file=stderr) fonts.append((console.columns, console.rows, font)) fonts.sort(key=lambda f: (f[0], f[1], f[2])) @@ -42,12 +43,13 @@ def show_fonts(): for cols, rows, font in fonts: print(cols, rows, font, file=stderr) console.set_font(font, True) - for row in range(1, rows+1): - for col in range(1, cols+1): + for row in range(1, rows + 1): + for col in range(1, cols + 1): console.text_at("{}".format(col % 10), col, row, False, (row % 2 == 0)) console.text_at(font.split(".")[0], 1, 1, False, True) console.clear_to_eol() + # Show the fonts; you may want to adjust the ``startswith`` filter to show other codesets. show_fonts() diff --git a/utils/line-follower-find-kp-ki-kd.py b/utils/line-follower-find-kp-ki-kd.py index 9e705b5..a8dca70 100755 --- a/utils/line-follower-find-kp-ki-kd.py +++ b/utils/line-follower-find-kp-ki-kd.py @@ -1,4 +1,3 @@ - """ This program is used to find the kp, ki, kd PID values for ``MoveTank.follow_line()``. These values vary from robot to robot, the best way @@ -52,28 +51,13 @@ def find_kp_ki_kd(tank, start, end, increment, speed, kx_to_tweak, kp, ki, kd): try: if kx_to_tweak == "kp": - tank.follow_line( - kp=kx, ki=ki, kd=kd, - speed=speed, - follow_for=follow_for_ms, - ms=10000, - ) + tank.follow_line(kp=kx, ki=ki, kd=kd, speed=speed, follow_for=follow_for_ms, ms=10000) elif kx_to_tweak == "ki": - tank.follow_line( - kp=kp, ki=kx, kd=kd, - speed=speed, - follow_for=follow_for_ms, - ms=10000, - ) + tank.follow_line(kp=kp, ki=kx, kd=kd, speed=speed, follow_for=follow_for_ms, ms=10000) elif kx_to_tweak == "kd": - tank.follow_line( - kp=kp, ki=ki, kd=kx, - speed=speed, - follow_for=follow_for_ms, - ms=10000, - ) + tank.follow_line(kp=kp, ki=ki, kd=kx, speed=speed, follow_for=follow_for_ms, ms=10000) else: raise Exception("Invalid kx_to_tweak %s" % kx_to_tweak) @@ -102,8 +86,7 @@ def find_kp_ki_kd(tank, start, end, increment, speed, kx_to_tweak, kp, ki, kd): if __name__ == "__main__": # logging - logging.basicConfig(level=logging.DEBUG, - format="%(asctime)s %(levelname)5s: %(message)s") + logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)5s: %(message)s") log = logging.getLogger(__name__) tank = MoveTank(OUTPUT_A, OUTPUT_B) @@ -113,18 +96,18 @@ def find_kp_ki_kd(tank, start, end, increment, speed, kx_to_tweak, kp, ki, kd): # Find the best integer for kp (in increments of 1) then fine tune by # finding the best float (in increments of 0.1) - kp = find_kp_ki_kd(tank, 1, 20, 1, speed, 'kp', 0, 0, 0) - kp = find_kp_ki_kd(tank, kp - 1, kp + 1, 0.1, speed, 'kp', kp, 0, 0) + kp = find_kp_ki_kd(tank, 1, 20, 1, speed, "kp", 0, 0, 0) + kp = find_kp_ki_kd(tank, kp - 1, kp + 1, 0.1, speed, "kp", kp, 0, 0) print("\n\n\n%s\nkp %s\n%s\n\n\n" % ("" * 10, kp, "*" * 10)) # Find the best float ki (in increments of 0.1) - ki = find_kp_ki_kd(tank, 0, 1, 0.1, speed, 'ki', kp, 0, 0) + ki = find_kp_ki_kd(tank, 0, 1, 0.1, speed, "ki", kp, 0, 0) print("\n\n\n%s\nki %s\n%s\n\n\n" % ("" * 10, ki, "*" * 10)) # Find the best integer for kd (in increments of 1) then fine tune by # finding the best float (in increments of 0.1) - kd = find_kp_ki_kd(tank, 0, 10, 1, speed, 'kd', kp, ki, 0) - kd = find_kp_ki_kd(tank, kd - 1, kd + 1, 0.1, speed, 'kd', kp, ki, 0) + kd = find_kp_ki_kd(tank, 0, 10, 1, speed, "kd", kp, ki, 0) + kd = find_kp_ki_kd(tank, kd - 1, kd + 1, 0.1, speed, "kd", kp, ki, 0) print("\n\n\n%s\nkd %s\n%s\n\n\n" % ("" * 10, kd, "*" * 10)) print("Final results: kp %s, ki %s, kd %s" % (kp, ki, kd)) diff --git a/utils/move_differential.py b/utils/move_differential.py index 0466a30..8272e9e 100755 --- a/utils/move_differential.py +++ b/utils/move_differential.py @@ -12,8 +12,7 @@ import sys # logging -logging.basicConfig(level=logging.DEBUG, - format="%(asctime)s %(levelname)5s: %(message)s") +logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(levelname)5s: %(message)s") log = logging.getLogger(__name__) STUD_MM = 8 @@ -34,53 +33,53 @@ mdiff = MoveDifferential(OUTPUT_A, OUTPUT_B, EV3Tire, 16.3 * STUD_MM) # This goes crazy on brickpi3, does it do the same on ev3? -#mdiff.on_for_distance(SpeedRPM(-40), 720, brake=False) -#mdiff.on_for_distance(SpeedRPM(40), 720, brake=False) +# mdiff.on_for_distance(SpeedRPM(-40), 720, brake=False) +# mdiff.on_for_distance(SpeedRPM(40), 720, brake=False) # Test arc left/right turns -#mdiff.on_arc_right(SpeedRPM(80), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM / 4) +# mdiff.on_arc_right(SpeedRPM(80), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM / 4) mdiff.on_arc_left(SpeedRPM(80), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM) # Test turning in place -#mdiff.turn_right(SpeedRPM(40), 180) -#mdiff.turn_left(SpeedRPM(40), 180) +# mdiff.turn_right(SpeedRPM(40), 180) +# mdiff.turn_left(SpeedRPM(40), 180) # Test odometry -#mdiff.odometry_start() -#mdiff.odometry_coordinates_log() +# mdiff.odometry_start() +# mdiff.odometry_coordinates_log() -#mdiff.turn_to_angle(SpeedRPM(40), 0) -#mdiff.on_for_distance(SpeedRPM(40), DistanceFeet(2).mm) -#mdiff.turn_right(SpeedRPM(40), 180) -#mdiff.turn_left(SpeedRPM(30), 90) -#mdiff.on_arc_left(SpeedRPM(80), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM) +# mdiff.turn_to_angle(SpeedRPM(40), 0) +# mdiff.on_for_distance(SpeedRPM(40), DistanceFeet(2).mm) +# mdiff.turn_right(SpeedRPM(40), 180) +# mdiff.turn_left(SpeedRPM(30), 90) +# mdiff.on_arc_left(SpeedRPM(80), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM) # Drive in a quarter arc to the right then go back to where you started -#log.info("turn on arc to the right") -#mdiff.on_arc_right(SpeedRPM(40), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM / 4) -#mdiff.odometry_coordinates_log() -#log.info("\n\n\n\n") -#log.info("go back to (0, 0)") -#mdiff.odometry_coordinates_log() -#mdiff.on_to_coordinates(SpeedRPM(40), 0, 0) -#mdiff.turn_to_angle(SpeedRPM(40), 90) +# log.info("turn on arc to the right") +# mdiff.on_arc_right(SpeedRPM(40), ONE_FOOT_CICLE_RADIUS_MM, ONE_FOOT_CICLE_CIRCUMFERENCE_MM / 4) +# mdiff.odometry_coordinates_log() +# log.info("\n\n\n\n") +# log.info("go back to (0, 0)") +# mdiff.odometry_coordinates_log() +# mdiff.on_to_coordinates(SpeedRPM(40), 0, 0) +# mdiff.turn_to_angle(SpeedRPM(40), 90) # Drive in a rectangle -#mdiff.turn_to_angle(SpeedRPM(40), 120) -#mdiff.on_to_coordinates(SpeedRPM(40), 0, DistanceFeet(1).mm) -#mdiff.on_to_coordinates(SpeedRPM(40), DistanceFeet(2).mm, DistanceFeet(1).mm) -#mdiff.on_to_coordinates(SpeedRPM(40), DistanceFeet(2).mm, 0) -#mdiff.on_to_coordinates(SpeedRPM(40), 0, 0) -#mdiff.turn_to_angle(SpeedRPM(40), 90) +# mdiff.turn_to_angle(SpeedRPM(40), 120) +# mdiff.on_to_coordinates(SpeedRPM(40), 0, DistanceFeet(1).mm) +# mdiff.on_to_coordinates(SpeedRPM(40), DistanceFeet(2).mm, DistanceFeet(1).mm) +# mdiff.on_to_coordinates(SpeedRPM(40), DistanceFeet(2).mm, 0) +# mdiff.on_to_coordinates(SpeedRPM(40), 0, 0) +# mdiff.turn_to_angle(SpeedRPM(40), 90) # Use odometry to drive to specific coordinates -#mdiff.on_to_coordinates(SpeedRPM(40), 600, 300) +# mdiff.on_to_coordinates(SpeedRPM(40), 600, 300) # Now go back to where we started and rotate in place to 90 degrees -#mdiff.on_to_coordinates(SpeedRPM(40), 0, 0) -#mdiff.turn_to_angle(SpeedRPM(40), 90) +# mdiff.on_to_coordinates(SpeedRPM(40), 0, 0) +# mdiff.turn_to_angle(SpeedRPM(40), 90) -#mdiff.odometry_coordinates_log() -#mdiff.odometry_stop() +# mdiff.odometry_coordinates_log() +# mdiff.odometry_stop() diff --git a/utils/move_motor.py b/utils/move_motor.py index 4c0aee8..219cec9 100755 --- a/utils/move_motor.py +++ b/utils/move_motor.py @@ -18,8 +18,7 @@ args = parser.parse_args() # logging -logging.basicConfig(level=logging.INFO, - format="%(asctime)s %(levelname)5s: %(message)s") +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)5s: %(message)s") log = logging.getLogger(__name__) if args.motor == "A": @@ -34,8 +33,8 @@ raise Exception("%s is invalid, options are A, B, C, D") if args.degrees: - log.info("Motor %s, current position %d, move to position %d, max speed %d" % - (args.motor, motor.position, args.degrees, motor.max_speed)) - motor.run_to_rel_pos(speed_sp=args.speed, - position_sp=args.degrees, - stop_action='hold') + log.info( + "Motor %s, current position %d, move to position %d, max speed %d" + % (args.motor, motor.position, args.degrees, motor.max_speed) + ) + motor.run_to_rel_pos(speed_sp=args.speed, position_sp=args.degrees, stop_action="hold") diff --git a/utils/stop_all_motors.py b/utils/stop_all_motors.py index 1f39a27..2af257d 100755 --- a/utils/stop_all_motors.py +++ b/utils/stop_all_motors.py @@ -6,4 +6,4 @@ from ev3dev2.motor import list_motors for motor in list_motors(): - motor.stop(stop_action='brake') + motor.stop(stop_action="brake")