Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/wedo_plugin/LICENSE339
-rw-r--r--plugins/wedo_plugin/README.rst67
-rw-r--r--plugins/wedo_plugin/WeDoMore.py183
-rw-r--r--plugins/wedo_plugin/icons/wedooff.svg71
-rw-r--r--plugins/wedo_plugin/icons/wedoon.svg71
-rw-r--r--plugins/wedo_plugin/usb/ACKNOWLEDGEMENTS51
-rw-r--r--plugins/wedo_plugin/usb/LICENSE27
-rw-r--r--plugins/wedo_plugin/usb/README.rst83
-rw-r--r--plugins/wedo_plugin/usb/ReleaseNotes.rst102
-rw-r--r--plugins/wedo_plugin/usb/TODO36
-rw-r--r--plugins/wedo_plugin/usb/__init__.py17
-rw-r--r--plugins/wedo_plugin/usb/_debug.py2
-rw-r--r--plugins/wedo_plugin/usb/_interop.py10
-rw-r--r--plugins/wedo_plugin/usb/backend/__init__.py2
-rw-r--r--plugins/wedo_plugin/usb/backend/libusb0.py590
-rw-r--r--plugins/wedo_plugin/usb/backend/libusb1.py901
-rw-r--r--plugins/wedo_plugin/usb/backend/openusb.py66
-rw-r--r--plugins/wedo_plugin/usb/control.py2
-rw-r--r--plugins/wedo_plugin/usb/core.py90
-rw-r--r--plugins/wedo_plugin/usb/legacy.py30
-rw-r--r--plugins/wedo_plugin/usb/util.py8
-rw-r--r--plugins/wedo_plugin/wedo/__init__.py189
-rw-r--r--plugins/wedo_plugin/wedo/distance.py54
-rw-r--r--plugins/wedo_plugin/wedo/motor.py7
-rw-r--r--plugins/wedo_plugin/wedo/tilt.py14
-rw-r--r--plugins/wedo_plugin/wedo_plugin.py453
26 files changed, 2948 insertions, 517 deletions
diff --git a/plugins/wedo_plugin/LICENSE b/plugins/wedo_plugin/LICENSE
new file mode 100644
index 0000000..ecbc059
--- /dev/null
+++ b/plugins/wedo_plugin/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. \ No newline at end of file
diff --git a/plugins/wedo_plugin/README.rst b/plugins/wedo_plugin/README.rst
new file mode 100644
index 0000000..8d09bf1
--- /dev/null
+++ b/plugins/wedo_plugin/README.rst
@@ -0,0 +1,67 @@
+About the WeDo
+--------------
+
+The `Lego WeDo`_ is a tethered-over-USB sensing and robotics toolkit produced by Lego for the educational market.
+
+It's gotten lots of press, including a favorable review on the One Laptop Per Child blog, on `deployments and training in Peru`_.
+
+It's supported by Scratch(on Windows and OSX) and by Lego's proprietary software(on Windows.)
+
+It prominently features the LB1836_ motor driver and the LM358_ op-amp, as well as an epoxy blob with USB support.
+
+The digital communication protocol used by the Power Functions system is documented on `Philo's Awesome Page`_.
+
+Requirements
+------------
+
+- Python 2.7+
+- pyusb (setup.py should take care of installing the dependency)
+
+Installation
+------------
+
+From pypi:
+
+ pip install wedo
+
+From the source tree:
+
+ ./setup.py install
+
+
+How to Use it
+-------------
+
+ >>> from wedo import WeDo
+ >>> wd = WeDo()
+ # Activating the first motor full forward:
+ >>> wd.motor_a = 100
+ # Activating the second motor half speed/force backward:
+ >>> wd.motor_b = -50
+ # Current value of the tilt sensor:
+ >>> wd.tilt
+ # Current distance value in meters of the distance sensor:
+ >>> wd.distance
+
+Contributors
+------------
+
+`Ian Daniher`_
+
+Tony Forster
+
+`Walter Bender`_
+
+`Guillaume Binet`_
+
+`Alan Aguiar`_
+
+.. _`Lego WeDo`: http://education.lego.com/en-us/lego-education-product-database/wedo/9580-lego-education-wedo-construction-set/
+.. _LB1836: http://semicon.sanyo.com/en/ds_e/EN3947F.pdf
+.. _LM358: http://www.national.com/ds/LM/LM158.pdf
+.. _`deployments and training in Peru`: http://blog.laptop.org/2011/02/12/lego-wedo-oloc-peru/
+.. _`Philo's Awesome Page`: http://www.philohome.com/pf/LEGO_Power_Functions_RC.pdf
+.. _`Guillaume Binet`: https://github.com/gbin
+.. _`Ian Daniher`: https://github.com/itdaniher
+.. _`Walter Bender`: https://github.com/walterbender
+.. _`Alan Aguiar`: https://github.com/alanjas
diff --git a/plugins/wedo_plugin/WeDoMore.py b/plugins/wedo_plugin/WeDoMore.py
deleted file mode 100644
index 62eed58..0000000
--- a/plugins/wedo_plugin/WeDoMore.py
+++ /dev/null
@@ -1,183 +0,0 @@
-#Copyright (c) 2011, 2012, Ian Daniher
-#Copyright (c) 2012, Tony Forster, Walter Bender
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-import sys
-import os
-sys.path.append(os.path.dirname(__file__))
-import usb.core
-
-import logging
-logger = logging.getLogger('WeDoMore')
-
-
-UNAVAILABLE = None
-TILTSENSOR = [38, 39]
-DISTANCESENSOR = [176, 177, 178, 179]
-MOTOR = [0, 1, 2, 3, 238, 239]
-TILT_FORWARD = 0
-TILT_LEFT = 1
-TILT_RIGHT = 2
-TILT_BACK = 3
-NO_TILT = -1
-
-
-def scan_for_devices():
- ''' Find all available devices '''
- return usb.core.find(find_all=True, idVendor=0x0694, idProduct=0x0003)
-
-
-class WeDo:
- def __init__(self, device=None):
- """Find a USB device with the VID and PID of the Lego
- WeDo. If the HID kernel driver is active, detatch
- it."""
- self.dev = None
- self.number = 0
- self.dev = device
- if self.dev is None:
- logging.debug("No Lego WeDo found")
- else:
- self.init_device()
- self.valMotorA = 0
- self.valMotorB = 0
-
- def init_device(self):
- ''' Reinit device associated with the WeDo instance '''
- if self.dev is not None:
- try:
- if self.dev.is_kernel_driver_active(0):
- try:
- self.dev.detach_kernel_driver(0)
- except usb.core.USBError as e:
- logger.error(
- "Could not detatch kernel driver: %s" % (str(e)))
- except usb.core.USBError as e:
- logger.error("Could not talk to WeDo device: %s" % (str(e)))
- self.endpoint = self.dev[0][(0,0)][0]
-
- def getRawData(self):
- """Read 64 bytes from the WeDo's endpoint, but only
- return the last eight."""
- try:
- data = list(self.endpoint.read(64)[-8:])
- except usb.core.USBError as e:
- logger.error("Could not read from WeDo device: %s" % (str(e)))
- return None
- return data
-
- def processMotorValues(self, value):
- """Check to make sure motor values are sane."""
- retValue = int(value)
- if 0 < value < 101:
- retValue += 27
- elif -101 < value < 0:
- retValue -= 27
- elif value == 0:
- retValue = 0
- return retValue
-
- def setMotors(self, valMotorA, valMotorB):
- """Arguments should be in form of a number between 0
- and 100, positive or negative. Magic numbers used for
- the ctrl_transfer derived from sniffing USB coms."""
- if self.dev is None:
- return
- self.valMotorA = self.processMotorValues(valMotorA)
- self.valMotorB = self.processMotorValues(valMotorB)
- data = [64, self.valMotorA&0xFF, self.valMotorB&0xFF,
- 0x00, 0x00, 0x00, 0x00, 0x00]
- try:
- self.dev.ctrl_transfer(bmRequestType = 0x21, bRequest = 0x09,
- wValue = 0x0200, wIndex = 0,
- data_or_wLength = data)
- except usb.core.USBError as e:
- logger.error("Could not write to driver: %s" % (str(e)))
-
- def getData(self):
- """Sensor data is contained in the 2nd and 4th byte, with
- sensor IDs being contained in the 3rd and 5th byte
- respectively."""
- rawData = self.getRawData()
- if rawData is not None:
- sensorData = {rawData[3]: rawData[2], rawData[5]: rawData[4]}
- else:
- sensorData = {}
- return sensorData
-
- def processTilt(self, v):
- """Use a series of elif/value-checks to process the tilt
- sensor data."""
- if v in range(10, 40):
- return TILT_BACK
- elif v in range(60, 90):
- return TILT_RIGHT
- elif v in range(170, 190):
- return TILT_FORWARD
- elif v in range(220, 240):
- return TILT_LEFT
- elif v in range(120, 140):
- return NO_TILT
- else:
- return NO_TILT
-
- def getTilt(self):
- if self.dev is None:
- return UNAVAILABLE
- data = self.getData()
- for num in data.keys():
- if num in TILTSENSOR:
- return self.processTilt(data[num])
- return UNAVAILABLE
-
- def getDistance(self):
- if self.dev is None:
- return UNAVAILABLE
- data = self.getData()
- for num in data.keys():
- if num in DISTANCESENSOR:
- return data[num] - 69
- return UNAVAILABLE
-
- # TODO: check motor availability
-
- def setMotorA(self, valMotorA):
- self.setMotors(valMotorA, self.valMotorB)
- return self.valMotorA
-
- def setMotorB(self, valMotorB):
- self.setMotors(self.valMotorA, valMotorB)
- return self.valMotorB
-
- def getMotorA(self):
- if self.dev is None:
- return UNAVAILABLE
- if self.valMotorA == 0:
- return 0
- elif self.valMotorA < 0:
- return self.valMotorA + 27
- else:
- return self.valMotorA - 27
-
- def getMotorB(self):
- if self.dev is None:
- return UNAVAILABLE
- if self.valMotorB == 0:
- return 0
- elif self.valMotorB < 0:
- return self.valMotorB + 27
- else:
- return self.valMotorB - 27
diff --git a/plugins/wedo_plugin/icons/wedooff.svg b/plugins/wedo_plugin/icons/wedooff.svg
new file mode 100644
index 0000000..4a12b37
--- /dev/null
+++ b/plugins/wedo_plugin/icons/wedooff.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ width="55"
+ height="55"
+ viewBox="0 0 55 55"
+ id="svg2"
+ xml:space="preserve"
+ inkscape:version="0.48.3.1 r9886"
+ sodipodi:docname="WeDooff.svg"><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1366"
+ inkscape:window-height="744"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="4.2909091"
+ inkscape:cx="27.5"
+ inkscape:cy="27.5"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" /><metadata
+ id="metadata15"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+ id="defs13"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ id="perspective12" />
+
+
+
+
+ </defs><rect
+ width="45"
+ height="45"
+ x="5"
+ y="5"
+ id="rect3269"
+ style="fill:#282828;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
+ d="m 11.421095,15.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z"
+ id="path7"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ style="fill:#ffffff;fill-opacity:1"
+ id="path2823"
+ d="m 35.421095,15.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z" /><path
+ d="m 35.421095,39.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z"
+ id="path2825"
+ style="fill:#ffffff;fill-opacity:1" /><path
+ style="fill:#ffffff;fill-opacity:1"
+ id="path2827"
+ d="m 11.421095,39.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z" /></svg> \ No newline at end of file
diff --git a/plugins/wedo_plugin/icons/wedoon.svg b/plugins/wedo_plugin/icons/wedoon.svg
new file mode 100644
index 0000000..16d54d2
--- /dev/null
+++ b/plugins/wedo_plugin/icons/wedoon.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ width="55"
+ height="55"
+ viewBox="0 0 55 55"
+ id="svg2"
+ xml:space="preserve"
+ inkscape:version="0.47 r22583"
+ sodipodi:docname="WeDoon.svg"><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="504"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="4.2909091"
+ inkscape:cx="27.5"
+ inkscape:cy="27.5"
+ inkscape:window-x="0"
+ inkscape:window-y="24"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" /><metadata
+ id="metadata15"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+ id="defs13"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.5 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.5 : 1"
+ inkscape:persp3d-origin="27.5 : 18.333333 : 1"
+ id="perspective12" />
+
+
+
+
+ </defs><rect
+ width="55"
+ height="55"
+ x="0"
+ y="0"
+ id="rect3269"
+ style="fill:#ffd200;fill-opacity:1;fill-rule:nonzero;stroke:none" /><path
+ d="m 11.421095,15.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z"
+ id="path7"
+ style="fill:#ff0000;fill-opacity:1" /><path
+ style="fill:#ff0000;fill-opacity:1"
+ id="path2823"
+ d="m 35.421095,15.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z" /><path
+ d="m 35.421095,39.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z"
+ id="path2825"
+ style="fill:#ff0000;fill-opacity:1" /><path
+ style="fill:#ff0000;fill-opacity:1"
+ id="path2827"
+ d="m 11.421095,39.380686 c 0,2.0569 1.66964,3.72654 3.72654,3.72654 2.05489,0 3.72453,-1.66964 3.72453,-3.72654 0,-2.05489 -1.66897,-3.72788 -3.72453,-3.72788 -2.0569,0 -3.72654,1.67299 -3.72654,3.72788 z" /></svg> \ No newline at end of file
diff --git a/plugins/wedo_plugin/usb/ACKNOWLEDGEMENTS b/plugins/wedo_plugin/usb/ACKNOWLEDGEMENTS
new file mode 100644
index 0000000..b7fdb22
--- /dev/null
+++ b/plugins/wedo_plugin/usb/ACKNOWLEDGEMENTS
@@ -0,0 +1,51 @@
+This is a list of people who has contributed to PyUSB 1.0 development.
+If I forgot you, please email me.
+
+PyUSB 1.0.0 (beta 1)
+---------------------
+
+- Stefano Di Martino: the port number patch.
+- Simon Norberg: several bug fixes.
+- iThompson: fixed tab errors in Python 3.3.
+- Harry Bock: workaround for Python 3.3 bug.
+- ponty: tox support.
+- Chris Clark: version information patch.
+- themperek: improve read speed.
+- David Halter: for the isochronous writing implementation for the libusb10
+ backend.
+
+PyUSB 1.0.0 (alpha 3)
+---------------------
+
+- Robert von Burg: for the bug reports about Python versions compatibility and
+ kernel driver functions.
+- James Rowe: for patches to the tutorial file.
+- Braiden Kindt: for the patch fixing bug when less than o number of requested
+ bytes are read.
+- Tormod Volden: for his several patches for legacy module.
+
+PyUSB 1.0.0 (alpha 2)
+---------------------
+
+- Chris Clark: for the bug report in the log subsystem.
+- Emmanuel Blot: for the patch which improves performance when debug is disabled.
+- Peter Bigot: for the patch that fixes get_active_configuration seg fault,
+ the patch to add error code to USBError and the patch to fix
+ parameter order in the USBError.
+- Travis Robinson and Xiaofan Chen: for let me use their benchmark firmware.
+- Poul-Henning Kamp: for the suggestion of ``bus`` and ``address`` attributes.
+
+PyUSB 1.0.0 (alpha 1)
+---------------------
+
+- Xiaofan Chen: for support in mailing list.
+- Poul-Henning Kam: for the documentation patches.
+
+PyUSB 1.0.0 (alpha 0)
+---------------------
+
+- Thomas Reitmayr: thanks for your patches to get PyUSB running with libusb 1.0.
+- Carl Ritson: thanks for your patch to get minimal working of legacy layer.
+- Romain Aviolat: thanks for pointing out a mistake in the tutorial and to report a bug in ctrl_transfer.
+- Xiaofan Chen: thanks for your effort testing PyUSB with libusb 1.0 Windows backend and on FreeBSD.
+
diff --git a/plugins/wedo_plugin/usb/LICENSE b/plugins/wedo_plugin/usb/LICENSE
new file mode 100644
index 0000000..1c88b5a
--- /dev/null
+++ b/plugins/wedo_plugin/usb/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2009-2011 Wander Lairson Costa. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. The name of the author may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+OF SUCH DAMAGE.
+
diff --git a/plugins/wedo_plugin/usb/README.rst b/plugins/wedo_plugin/usb/README.rst
new file mode 100644
index 0000000..741c4df
--- /dev/null
+++ b/plugins/wedo_plugin/usb/README.rst
@@ -0,0 +1,83 @@
+=======================================
+PyUSB 1.0 - Easy USB access from Python
+=======================================
+
+Introduction
+============
+
+The PyUSB module provides for Python easy access to the host
+machine's Universal Serial Bus (USB) system.
+
+Until 0.4 version, PyUSB used to be a thin wrapper over libusb.
+With 1.0 version, things changed considerably. Now PyUSB is an
+API rich, backend neutral Python USB module easy to use.
+
+As with most Python modules, PyUSB's documentation is based on Python
+doc strings and can therefore be manipulated by tools such as pydoc.
+
+You can also find a tutorial at: http://pyusb.sourceforge.net/docs/1.0/tutorial.html.
+
+PyUSB is being developed and tested in Linux and Windows, but it should work
+fine in any platform running Python >= 2.4, ctypes and at least one of the
+builtin backends.
+
+PyUSB supports libusb 0.1, libusb 1.0 and OpenUSB, but the user does not need
+to worry about that, unless in some corner cases.
+
+If you have any question about PyUSB, you can use the PyUSB mailing list
+hosted in the SourceForge. In the PyUSB website (http://pyusb.sourceforge.net)
+you can find instructions on how to subscribe to the mailing list.
+
+Installing PyUSB on GNU/Linux Systems
+=====================================
+
+These instructions are for Debian-based systems. Instructions for
+other flavors of GNU/Linux should be similar.
+
+You will first need to install the following packages:
+
+1) python (PyUSB is useless without it), version >= 2.4
+2) At least one of the supported libraries (libusb 1.0, libusb 0.1 or OpenUSB)
+3) If your Python version is < 2.5, you have to install ctypes as a separate package,
+ because these versions of Python does not ship it.
+
+For example, the command::
+
+ $ sudo apt-get install python libusb
+
+should install all these packages on most Debian-based systems with
+access to the proper package repositories.
+
+Once the above packages are installed, you can install PyUSB
+with the command::
+
+ $ sudo python setup.py install
+
+Run it as root from within the same directory as this README file.
+
+Installing PyUSB on Windows
+===========================
+
+Now that PyUSB is 100% written in Python, you install it on Windows
+in the same way you do on Linux::
+
+ python setup.py install
+
+If you get some kind of "command not found" error, make sure to add
+the Python install directory to your PATH environment variable or
+give the complete path to the Python interpreter.
+
+Remember that you need libusb (1.0 or 0.1) or OpenUSB running on your
+system. For Windows users, libusb 1.0 is still experimental, so it is
+recommended libusb-win32 package. Check the libusb website for updates
+(http://www.libusb.org).
+
+Reporting bugs/Submitting patches
+=================================
+
+Some people have been sending patches and reporting bugs directly
+at my email. Please, do it through
+`github <https://github.com/walac/pyusb>`_, I had a hardtime tracking
+their names to put them in the acknowledgments file. ;-)
+
+PS: this README file was based on the great Josh Lifton's one... ^_^
diff --git a/plugins/wedo_plugin/usb/ReleaseNotes.rst b/plugins/wedo_plugin/usb/ReleaseNotes.rst
new file mode 100644
index 0000000..53c7d96
--- /dev/null
+++ b/plugins/wedo_plugin/usb/ReleaseNotes.rst
@@ -0,0 +1,102 @@
+==========
+PyUSB News
+==========
+
+What's new in PyUSB 1.0.0 (beta 1)?
+===================================
+
+- Isochronous transfer for libusb 1.0 (by David Halter).
+- Experimental OpenUSB support.
+- Documentation update.
+- ``PYUSB_DEBUG_LEVEL`` environment variable is now called ``PYUSB_DEBUG``.
+- Legacy module nwo groups according to their *bus*.
+- Version information available for apps (by Chris Clark).
+- Faster read operation (by themperek).
+- Tox support (by ponty).
+- Support for port number info (by Stefano Di Martino).
+- Several bug fixes (please, check the Changelog file).
+
+Known issues
+============
+
+- OpenUSB backend hangs on some control transfers.
+
+TODO
+====
+
+- More tests with legacy module.
+- Isochronous transfers for libusb-win32.
+
+What's new in PyUSB 1.0.0 (alpha 3)?
+====================================
+
+**WARNING**: this release renames the libusb 1.0 and libusb 0.1 backends. If
+your code makes direct access to this backends, you will have to change it.
+
+- Fixed several legacy module bugs (by Tormod Volden).
+- Fixed libusb0 backend for BSDs and Mac OSX.
+- Fixed data loss when less the requested number of bytes were read (by
+ Braiden Kindt).
+- Documentation fixes.
+
+What's new in PyUSB 1.0.0 (alpha 2)?
+====================================
+
+- Test firmware now lives in its own respository (https://github.com/walac/bmfw).
+- ``USBError`` now has the property ``backend_error_code`` that tells the
+ backend specific error.
+- ``errno`` value in ``USBError`` is translated according to the backend error.
+- Now ``Device`` class has the ``bus`` and ``address`` attributes to
+ differentiate identical devices.
+- Optimization when log is disabled (by Emmanuel Blot).
+- Several other minor fixes and improvaments (check ChangeLog file).
+
+Features not implemented
+------------------------
+
+- OpenUSB support.
+- Isochronous transfer.
+
+What's new in PyUSB 1.0.0 (alpha 1)?
+====================================
+
+This release implements more PyUSB 1.0 features towards beta stage. The new
+features implemented include:
+
+- Standard control requests through usb.control module.
+- Request current configuration from device when you do not call
+ set_configuration.
+- get_string function in the usb.util module to get string descriptors.
+- Full 0.4 API emulation.
+- Device is not reset anymore in test cases to avoid problems in systems
+ where it does not work.
+
+Features not implemented
+------------------------
+
+- OpenUSB support.
+- Isochronous transfer.
+
+What's new in PyUSB 1.0.0 (alpha 0)?
+====================================
+
+This is the first PyUSB 1.0 series public release. This is an alpha release, which
+means that most of the features described in the README file and on the website are
+not yet stable or even implemented.
+
+Features not implemented
+------------------------
+
+- Full support for legacy 0.4 legacy code (although partial support is provided).
+- OpenUSB backend.
+- libusb 1.0 windows backend stability (although it is reasonable usable).
+- Support for several standard control requests (including GET_STRING).
+- Python < 2.6 and Python 3 not yet fully tested.
+
+Known issues
+------------
+
+- ``reset`` method fails under FreeUSB (libusb 1.0 backend).
+- ``reset`` method hangs under Windows (libusb 1.0 backend).
+- Sometimes occurs `read` timeout on Windows (libusb 1.0 backend).
+- Test cases fail to run under cygwin.
diff --git a/plugins/wedo_plugin/usb/TODO b/plugins/wedo_plugin/usb/TODO
new file mode 100644
index 0000000..d7eeb70
--- /dev/null
+++ b/plugins/wedo_plugin/usb/TODO
@@ -0,0 +1,36 @@
+1.0.0-a3:
+ * Finish implementation and test OpenUSB backend.
+ * Samples.
+ * Improve documentation.
+ * Implement isochronous transfers.
+ * Test, test and test.
+
+1.0.0-a2:
+ * Validate it on platforms other than Windows and Linux.
+ * Finish implementation and test OpenUSB backend.
+ * Samples.
+ * Improve documentation.
+ * Implement isochronous transfers.
+ * Test, test and test.
+
+1.0.0-a1:
+ * Validate it on platforms other than Windows and Linux.
+ * Finish implementation and test OpenUSB backend.
+ * Samples.
+ * Improve documentation.
+ * Implement isochronous transfers.
+ * Test, test and test.
+
+1.0.0-a0:
+ * Implement standard control requests API.
+ * Finish implementation of legacy compatibility layer.
+ * Determine automatically current configuration when user hasn't set it.
+ * Validate it on platforms other than Windows and Linux.
+ * Finish implementation and test of OpenUSB backend.
+ * Validate it on Python 2.3 and Python 3.x.
+ * Samples.
+ * Improve documentation.
+ * Implement isochronous transfers.
+ * Upgrade PIC test firmware to use 2.6a version Microchip USB stack.
+ * Test, test and test.
+
diff --git a/plugins/wedo_plugin/usb/__init__.py b/plugins/wedo_plugin/usb/__init__.py
index 8909cf2..178ed14 100644
--- a/plugins/wedo_plugin/usb/__init__.py
+++ b/plugins/wedo_plugin/usb/__init__.py
@@ -1,8 +1,8 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
-#
+# Copyright (C) 2009-2013 Wander Lairson Costa
+#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
-#
+#
# The authors hereby grant permission to use, copy, modify, distribute,
# and license this software and its documentation for any purpose, provided
# that existing copyright notices are retained in all copies and that this
@@ -12,13 +12,13 @@
# and need not follow the licensing terms described here, provided that
# the new terms are clearly indicated on the first page of each file where
# they apply.
-#
+#
# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
+#
# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
@@ -43,13 +43,18 @@ import os
__author__ = 'Wander Lairson Costa'
+# Use Semantic Versioning, http://semver.org/
+version_info = (1, 0, 0, 'b1')
+__version__ = '%d.%d.%d%s' % version_info
+
+
__all__ = ['legacy', 'core', 'backend', 'util']
def _setup_log():
from usb import _debug
logger = logging.getLogger('usb')
- debug_level = os.getenv('PYUSB_DEBUG_LEVEL')
+ debug_level = os.getenv('PYUSB_DEBUG')
if debug_level is not None:
_debug.enable_tracing(True)
diff --git a/plugins/wedo_plugin/usb/_debug.py b/plugins/wedo_plugin/usb/_debug.py
index 13b0ced..8b1f910 100644
--- a/plugins/wedo_plugin/usb/_debug.py
+++ b/plugins/wedo_plugin/usb/_debug.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
+# Copyright (C) 2009-2013 Wander Lairson Costa
#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
diff --git a/plugins/wedo_plugin/usb/_interop.py b/plugins/wedo_plugin/usb/_interop.py
index 6069d5e..93b8f04 100644
--- a/plugins/wedo_plugin/usb/_interop.py
+++ b/plugins/wedo_plugin/usb/_interop.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
+# Copyright (C) 2009-2013 Wander Lairson Costa
#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
@@ -129,7 +129,9 @@ def as_array(data=None):
try:
return array.array('B', data)
except TypeError:
- # When you pass a unicode string, you got a TypeError
- # if first parameter is not 'u'
- return array.array('u', data)
+ # When you pass a unicode string or a character sequence,
+ # you get a TypeError if first parameter does not match
+ a = array.array('B')
+ a.fromstring(data)
+ return a
diff --git a/plugins/wedo_plugin/usb/backend/__init__.py b/plugins/wedo_plugin/usb/backend/__init__.py
index 67ee00f..e08d7bc 100644
--- a/plugins/wedo_plugin/usb/backend/__init__.py
+++ b/plugins/wedo_plugin/usb/backend/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
+# Copyright (C) 2009-2013 Wander Lairson Costa
#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
diff --git a/plugins/wedo_plugin/usb/backend/libusb0.py b/plugins/wedo_plugin/usb/backend/libusb0.py
new file mode 100644
index 0000000..8738335
--- /dev/null
+++ b/plugins/wedo_plugin/usb/backend/libusb0.py
@@ -0,0 +1,590 @@
+# Copyright (C) 2009-2013 Wander Lairson Costa
+#
+# The following terms apply to all files associated
+# with the software unless explicitly disclaimed in individual files.
+#
+# The authors hereby grant permission to use, copy, modify, distribute,
+# and license this software and its documentation for any purpose, provided
+# that existing copyright notices are retained in all copies and that this
+# notice is included verbatim in any distributions. No written agreement,
+# license, or royalty fee is required for any of the authorized uses.
+# Modifications to this software may be copyrighted by their authors
+# and need not follow the licensing terms described here, provided that
+# the new terms are clearly indicated on the first page of each file where
+# they apply.
+#
+# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+# MODIFICATIONS.
+
+from ctypes import *
+import ctypes.util
+import os
+import usb.backend
+import usb.util
+import sys
+from usb.core import USBError
+from usb._debug import methodtrace
+import usb._interop as _interop
+import logging
+
+__author__ = 'Wander Lairson Costa'
+
+__all__ = ['get_backend']
+
+_logger = logging.getLogger('usb.backend.libusb0')
+
+# usb.h
+
+_PC_PATH_MAX = 4
+
+if sys.platform.find('bsd') != -1 or sys.platform.find('mac') != -1 or \
+ sys.platform.find('darwin') != -1:
+ _PATH_MAX = 1024
+elif sys.platform == 'win32' or sys.platform == 'cygwin':
+ _PATH_MAX = 511
+else:
+ _PATH_MAX = os.pathconf('.', _PC_PATH_MAX)
+
+# libusb-win32 makes all structures packed, while
+# default libusb only does for some structures
+# _PackPolicy defines the structure packing according
+# to the platform.
+class _PackPolicy(object):
+ pass
+
+if sys.platform == 'win32' or sys.platform == 'cygwin':
+ _PackPolicy._pack_ = 1
+
+# Data structures
+
+class _usb_descriptor_header(Structure):
+ _pack_ = 1
+ _fields_ = [('blength', c_uint8),
+ ('bDescriptorType', c_uint8)]
+
+class _usb_string_descriptor(Structure):
+ _pack_ = 1
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('wData', c_uint16)]
+
+class _usb_endpoint_descriptor(Structure, _PackPolicy):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('bEndpointAddress', c_uint8),
+ ('bmAttributes', c_uint8),
+ ('wMaxPacketSize', c_uint16),
+ ('bInterval', c_uint8),
+ ('bRefresh', c_uint8),
+ ('bSynchAddress', c_uint8),
+ ('extra', POINTER(c_uint8)),
+ ('extralen', c_int)]
+
+class _usb_interface_descriptor(Structure, _PackPolicy):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('bInterfaceNumber', c_uint8),
+ ('bAlternateSetting', c_uint8),
+ ('bNumEndpoints', c_uint8),
+ ('bInterfaceClass', c_uint8),
+ ('bInterfaceSubClass', c_uint8),
+ ('bInterfaceProtocol', c_uint8),
+ ('iInterface', c_uint8),
+ ('endpoint', POINTER(_usb_endpoint_descriptor)),
+ ('extra', POINTER(c_uint8)),
+ ('extralen', c_int)]
+
+class _usb_interface(Structure, _PackPolicy):
+ _fields_ = [('altsetting', POINTER(_usb_interface_descriptor)),
+ ('num_altsetting', c_int)]
+
+class _usb_config_descriptor(Structure, _PackPolicy):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('wTotalLength', c_uint16),
+ ('bNumInterfaces', c_uint8),
+ ('bConfigurationValue', c_uint8),
+ ('iConfiguration', c_uint8),
+ ('bmAttributes', c_uint8),
+ ('bMaxPower', c_uint8),
+ ('interface', POINTER(_usb_interface)),
+ ('extra', POINTER(c_uint8)),
+ ('extralen', c_int)]
+
+class _usb_device_descriptor(Structure, _PackPolicy):
+ _pack_ = 1
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('bcdUSB', c_uint16),
+ ('bDeviceClass', c_uint8),
+ ('bDeviceSubClass', c_uint8),
+ ('bDeviceProtocol', c_uint8),
+ ('bMaxPacketSize0', c_uint8),
+ ('idVendor', c_uint16),
+ ('idProduct', c_uint16),
+ ('bcdDevice', c_uint16),
+ ('iManufacturer', c_uint8),
+ ('iProduct', c_uint8),
+ ('iSerialNumber', c_uint8),
+ ('bNumConfigurations', c_uint8)]
+
+class _usb_device(Structure, _PackPolicy):
+ pass
+
+class _usb_bus(Structure, _PackPolicy):
+ pass
+
+_usb_device._fields_ = [('next', POINTER(_usb_device)),
+ ('prev', POINTER(_usb_device)),
+ ('filename', c_int8 * (_PATH_MAX + 1)),
+ ('bus', POINTER(_usb_bus)),
+ ('descriptor', _usb_device_descriptor),
+ ('config', POINTER(_usb_config_descriptor)),
+ ('dev', c_void_p),
+ ('devnum', c_uint8),
+ ('num_children', c_ubyte),
+ ('children', POINTER(POINTER(_usb_device)))]
+
+_usb_bus._fields_ = [('next', POINTER(_usb_bus)),
+ ('prev', POINTER(_usb_bus)),
+ ('dirname', c_char * (_PATH_MAX + 1)),
+ ('devices', POINTER(_usb_device)),
+ ('location', c_uint32),
+ ('root_dev', POINTER(_usb_device))]
+
+_usb_dev_handle = c_void_p
+
+class _DeviceDescriptor:
+ def __init__(self, dev):
+ desc = dev.descriptor
+ self.bLength = desc.bLength
+ self.bDescriptorType = desc.bDescriptorType
+ self.bcdUSB = desc.bcdUSB
+ self.bDeviceClass = desc.bDeviceClass
+ self.bDeviceSubClass = desc.bDeviceSubClass
+ self.bDeviceProtocol = desc.bDeviceProtocol
+ self.bMaxPacketSize0 = desc.bMaxPacketSize0
+ self.idVendor = desc.idVendor
+ self.idProduct = desc.idProduct
+ self.bcdDevice = desc.bcdDevice
+ self.iManufacturer = desc.iManufacturer
+ self.iProduct = desc.iProduct
+ self.iSerialNumber = desc.iSerialNumber
+ self.bNumConfigurations = desc.bNumConfigurations
+ self.address = dev.devnum
+ self.bus = dev.bus[0].location
+
+ self.port_number = None
+_lib = None
+
+def _load_library():
+ if sys.platform != 'cygwin':
+ candidates = ('usb-0.1', 'usb', 'libusb0')
+ for candidate in candidates:
+ # Workaround for CPython 3.3 issue#16283 / pyusb #14
+ if sys.platform == 'win32':
+ candidate = candidate + '.dll'
+
+ libname = ctypes.util.find_library(candidate)
+ if libname is not None: break
+ else:
+ # corner cases
+ # cygwin predefines library names with 'cyg' instead of 'lib'
+ try:
+ return CDLL('cygusb0.dll')
+ except:
+ _logger.error('Libusb 0 could not be loaded in cygwin', exc_info=True)
+
+ raise OSError('USB library could not be found')
+ return CDLL(libname)
+
+def _setup_prototypes(lib):
+ # usb_dev_handle *usb_open(struct usb_device *dev);
+ lib.usb_open.argtypes = [POINTER(_usb_device)]
+ lib.usb_open.restype = _usb_dev_handle
+
+ # int usb_close(usb_dev_handle *dev);
+ lib.usb_close.argtypes = [_usb_dev_handle]
+
+ # int usb_get_string(usb_dev_handle *dev,
+ # int index,
+ # int langid,
+ # char *buf,
+ # size_t buflen);
+ lib.usb_get_string.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_int,
+ c_char_p,
+ c_size_t
+ ]
+
+ # int usb_get_string_simple(usb_dev_handle *dev,
+ # int index,
+ # char *buf,
+ # size_t buflen);
+ lib.usb_get_string_simple.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_char_p,
+ c_size_t
+ ]
+
+ # int usb_get_descriptor_by_endpoint(usb_dev_handle *udev,
+ # int ep,
+ # unsigned char type,
+ # unsigned char index,
+ # void *buf,
+ # int size);
+ lib.usb_get_descriptor_by_endpoint.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_ubyte,
+ c_ubyte,
+ c_void_p,
+ c_int
+ ]
+
+ # int usb_get_descriptor(usb_dev_handle *udev,
+ # unsigned char type,
+ # unsigned char index,
+ # void *buf,
+ # int size);
+ lib.usb_get_descriptor.argtypes = [
+ _usb_dev_handle,
+ c_ubyte,
+ c_ubyte,
+ c_void_p,
+ c_int
+ ]
+
+ # int usb_bulk_write(usb_dev_handle *dev,
+ # int ep,
+ # const char *bytes,
+ # int size,
+ # int timeout);
+ lib.usb_bulk_write.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_char_p,
+ c_int,
+ c_int
+ ]
+
+ # int usb_bulk_read(usb_dev_handle *dev,
+ # int ep,
+ # char *bytes,
+ # int size,
+ # int timeout);
+ lib.usb_bulk_read.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_char_p,
+ c_int,
+ c_int
+ ]
+
+ # int usb_interrupt_write(usb_dev_handle *dev,
+ # int ep,
+ # const char *bytes,
+ # int size,
+ # int timeout);
+ lib.usb_interrupt_write.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_char_p,
+ c_int,
+ c_int
+ ]
+
+ # int usb_interrupt_read(usb_dev_handle *dev,
+ # int ep,
+ # char *bytes,
+ # int size,
+ # int timeout);
+ lib.usb_interrupt_read.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_char_p,
+ c_int,
+ c_int
+ ]
+
+ # int usb_control_msg(usb_dev_handle *dev,
+ # int requesttype,
+ # int request,
+ # int value,
+ # int index,
+ # char *bytes,
+ # int size,
+ # int timeout);
+ lib.usb_control_msg.argtypes = [
+ _usb_dev_handle,
+ c_int,
+ c_int,
+ c_int,
+ c_int,
+ c_char_p,
+ c_int,
+ c_int
+ ]
+
+ # int usb_set_configuration(usb_dev_handle *dev, int configuration);
+ lib.usb_set_configuration.argtypes = [_usb_dev_handle, c_int]
+
+ # int usb_claim_interface(usb_dev_handle *dev, int interface);
+ lib.usb_claim_interface.argtypes = [_usb_dev_handle, c_int]
+
+ # int usb_release_interface(usb_dev_handle *dev, int interface);
+ lib.usb_release_interface.argtypes = [_usb_dev_handle, c_int]
+
+ # int usb_set_altinterface(usb_dev_handle *dev, int alternate);
+ lib.usb_set_altinterface.argtypes = [_usb_dev_handle, c_int]
+
+ # int usb_resetep(usb_dev_handle *dev, unsigned int ep);
+ lib.usb_resetep.argtypes = [_usb_dev_handle, c_int]
+
+ # int usb_clear_halt(usb_dev_handle *dev, unsigned int ep);
+ lib.usb_clear_halt.argtypes = [_usb_dev_handle, c_int]
+
+ # int usb_reset(usb_dev_handle *dev);
+ lib.usb_reset.argtypes = [_usb_dev_handle]
+
+ # char *usb_strerror(void);
+ lib.usb_strerror.argtypes = []
+ lib.usb_strerror.restype = c_char_p
+
+ # void usb_set_debug(int level);
+ lib.usb_set_debug.argtypes = [c_int]
+
+ # struct usb_device *usb_device(usb_dev_handle *dev);
+ lib.usb_device.argtypes = [_usb_dev_handle]
+ lib.usb_device.restype = POINTER(_usb_device)
+
+ # struct usb_bus *usb_get_busses(void);
+ lib.usb_get_busses.restype = POINTER(_usb_bus)
+
+def _check(retval):
+ if retval is None:
+ errmsg = _lib.usb_strerror()
+ else:
+ ret = int(retval)
+ if ret < 0:
+ errmsg = _lib.usb_strerror()
+ # No error means that we need to get the error
+ # message from the return code
+ # Thanks to Nicholas Wheeler to point out the problem...
+ # Also see issue #2860940
+ if errmsg.lower() == 'no error':
+ errmsg = os.strerror(-ret)
+ else:
+ return ret
+ raise USBError(errmsg, ret)
+
+# implementation of libusb 0.1.x backend
+class _LibUSB(usb.backend.IBackend):
+ @methodtrace(_logger)
+ def enumerate_devices(self):
+ _check(_lib.usb_find_busses())
+ _check(_lib.usb_find_devices())
+ bus = _lib.usb_get_busses()
+ while bool(bus):
+ dev = bus[0].devices
+ while bool(dev):
+ yield dev[0]
+ dev = dev[0].next
+ bus = bus[0].next
+
+ @methodtrace(_logger)
+ def get_device_descriptor(self, dev):
+ return _DeviceDescriptor(dev)
+
+ @methodtrace(_logger)
+ def get_configuration_descriptor(self, dev, config):
+ if config >= dev.descriptor.bNumConfigurations:
+ raise IndexError('Invalid configuration index ' + str(config))
+ return dev.config[config]
+
+ @methodtrace(_logger)
+ def get_interface_descriptor(self, dev, intf, alt, config):
+ cfgdesc = self.get_configuration_descriptor(dev, config)
+ if intf >= cfgdesc.bNumInterfaces:
+ raise IndexError('Invalid interface index ' + str(interface))
+ interface = cfgdesc.interface[intf]
+ if alt >= interface.num_altsetting:
+ raise IndexError('Invalid alternate setting index ' + str(alt))
+ return interface.altsetting[alt]
+
+ @methodtrace(_logger)
+ def get_endpoint_descriptor(self, dev, ep, intf, alt, config):
+ interface = self.get_interface_descriptor(dev, intf, alt, config)
+ if ep >= interface.bNumEndpoints:
+ raise IndexError('Invalid endpoint index ' + str(ep))
+ return interface.endpoint[ep]
+
+ @methodtrace(_logger)
+ def open_device(self, dev):
+ return _check(_lib.usb_open(dev))
+
+ @methodtrace(_logger)
+ def close_device(self, dev_handle):
+ _check(_lib.usb_close(dev_handle))
+
+ @methodtrace(_logger)
+ def set_configuration(self, dev_handle, config_value):
+ _check(_lib.usb_set_configuration(dev_handle, config_value))
+
+ @methodtrace(_logger)
+ def set_interface_altsetting(self, dev_handle, intf, altsetting):
+ _check(_lib.usb_set_altinterface(dev_handle, altsetting))
+
+ @methodtrace(_logger)
+ def get_configuration(self, dev_handle):
+ bmRequestType = usb.util.build_request_type(
+ usb.util.CTRL_IN,
+ usb.util.CTRL_TYPE_STANDARD,
+ usb.util.CTRL_RECIPIENT_DEVICE
+ )
+ return self.ctrl_transfer(dev_handle,
+ bmRequestType,
+ 0x08,
+ 0,
+ 0,
+ 1,
+ 100
+ )[0]
+
+
+ @methodtrace(_logger)
+ def claim_interface(self, dev_handle, intf):
+ _check(_lib.usb_claim_interface(dev_handle, intf))
+
+ @methodtrace(_logger)
+ def release_interface(self, dev_handle, intf):
+ _check(_lib.usb_release_interface(dev_handle, intf))
+
+ @methodtrace(_logger)
+ def bulk_write(self, dev_handle, ep, intf, data, timeout):
+ return self.__write(_lib.usb_bulk_write,
+ dev_handle,
+ ep,
+ intf,
+ data, timeout)
+
+ @methodtrace(_logger)
+ def bulk_read(self, dev_handle, ep, intf, size, timeout):
+ return self.__read(_lib.usb_bulk_read,
+ dev_handle,
+ ep,
+ intf,
+ size,
+ timeout)
+
+ @methodtrace(_logger)
+ def intr_write(self, dev_handle, ep, intf, data, timeout):
+ return self.__write(_lib.usb_interrupt_write,
+ dev_handle,
+ ep,
+ intf,
+ data,
+ timeout)
+
+ @methodtrace(_logger)
+ def intr_read(self, dev_handle, ep, intf, size, timeout):
+ return self.__read(_lib.usb_interrupt_read,
+ dev_handle,
+ ep,
+ intf,
+ size,
+ timeout)
+
+ @methodtrace(_logger)
+ def ctrl_transfer(self,
+ dev_handle,
+ bmRequestType,
+ bRequest,
+ wValue,
+ wIndex,
+ data_or_wLength,
+ timeout):
+ if usb.util.ctrl_direction(bmRequestType) == usb.util.CTRL_OUT:
+ address, length = data_or_wLength.buffer_info()
+ length *= data_or_wLength.itemsize
+ return _check(_lib.usb_control_msg(
+ dev_handle,
+ bmRequestType,
+ bRequest,
+ wValue,
+ wIndex,
+ cast(address, c_char_p),
+ length,
+ timeout
+ ))
+ else:
+ data = _interop.as_array((0,) * data_or_wLength)
+ read = int(_check(_lib.usb_control_msg(
+ dev_handle,
+ bmRequestType,
+ bRequest,
+ wValue,
+ wIndex,
+ cast(data.buffer_info()[0],
+ c_char_p),
+ data_or_wLength,
+ timeout
+ )))
+ return data[:read]
+
+ @methodtrace(_logger)
+ def reset_device(self, dev_handle):
+ _check(_lib.usb_reset(dev_handle))
+
+ @methodtrace(_logger)
+ def detach_kernel_driver(self, dev_handle, intf):
+ _check(_lib.usb_detach_kernel_driver_np(dev_handle, intf))
+
+ def __write(self, fn, dev_handle, ep, intf, data, timeout):
+ address, length = data.buffer_info()
+ length *= data.itemsize
+ return int(_check(fn(
+ dev_handle,
+ ep,
+ cast(address, c_char_p),
+ length,
+ timeout
+ )))
+
+ def __read(self, fn, dev_handle, ep, intf, size, timeout):
+ data = _interop.as_array('\x00' * size)
+ address, length = data.buffer_info()
+ length *= data.itemsize
+ ret = int(_check(fn(
+ dev_handle,
+ ep,
+ cast(address, c_char_p),
+ length,
+ timeout
+ )))
+ return data[:ret]
+
+def get_backend():
+ global _lib
+ try:
+ if _lib is None:
+ _lib = _load_library()
+ _setup_prototypes(_lib)
+ _lib.usb_init()
+ return _LibUSB()
+ except Exception:
+ _logger.error('Error loading libusb 0.1 backend', exc_info=True)
+ return None
diff --git a/plugins/wedo_plugin/usb/backend/libusb1.py b/plugins/wedo_plugin/usb/backend/libusb1.py
new file mode 100644
index 0000000..5396092
--- /dev/null
+++ b/plugins/wedo_plugin/usb/backend/libusb1.py
@@ -0,0 +1,901 @@
+# Copyright (C) 2009-2013 Wander Lairson Costa
+#
+# The following terms apply to all files associated
+# with the software unless explicitly disclaimed in individual files.
+#
+# The authors hereby grant permission to use, copy, modify, distribute,
+# and license this software and its documentation for any purpose, provided
+# that existing copyright notices are retained in all copies and that this
+# notice is included verbatim in any distributions. No written agreement,
+# license, or royalty fee is required for any of the authorized uses.
+# Modifications to this software may be copyrighted by their authors
+# and need not follow the licensing terms described here, provided that
+# the new terms are clearly indicated on the first page of each file where
+# they apply.
+#
+# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
+# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
+# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
+# IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
+# NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+# MODIFICATIONS.
+
+from ctypes import *
+import ctypes.util
+import usb.util
+import sys
+import logging
+from usb._debug import methodtrace
+import usb._interop as _interop
+import errno
+import math
+from usb.core import USBError
+
+__author__ = 'Wander Lairson Costa'
+
+__all__ = [
+ 'get_backend',
+ 'LIBUSB_SUCESS',
+ 'LIBUSB_ERROR_IO',
+ 'LIBUSB_ERROR_INVALID_PARAM',
+ 'LIBUSB_ERROR_ACCESS',
+ 'LIBUSB_ERROR_NO_DEVICE',
+ 'LIBUSB_ERROR_NOT_FOUND',
+ 'LIBUSB_ERROR_BUSY',
+ 'LIBUSB_ERROR_TIMEOUT',
+ 'LIBUSB_ERROR_OVERFLOW',
+ 'LIBUSB_ERROR_PIPE',
+ 'LIBUSB_ERROR_INTERRUPTED',
+ 'LIBUSB_ERROR_NO_MEM',
+ 'LIBUSB_ERROR_NOT_SUPPORTED',
+ 'LIBUSB_ERROR_OTHER'
+ 'LIBUSB_TRANSFER_COMPLETED',
+ 'LIBUSB_TRANSFER_ERROR',
+ 'LIBUSB_TRANSFER_TIMED_OUT',
+ 'LIBUSB_TRANSFER_CANCELLED',
+ 'LIBUSB_TRANSFER_STALL',
+ 'LIBUSB_TRANSFER_NO_DEVICE',
+ 'LIBUSB_TRANSFER_OVERFLOW'
+ ]
+
+_logger = logging.getLogger('usb.backend.libusb1')
+
+# libusb.h
+
+# transfer_type codes
+# Control endpoint
+_LIBUSB_TRANSFER_TYPE_CONTROL = 0,
+# Isochronous endpoint
+_LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1
+# Bulk endpoint
+_LIBUSB_TRANSFER_TYPE_BULK = 2
+# Interrupt endpoint
+_LIBUSB_TRANSFER_TYPE_INTERRUPT = 3
+
+# return codes
+
+LIBUSB_SUCCESS = 0
+LIBUSB_ERROR_IO = -1
+LIBUSB_ERROR_INVALID_PARAM = -2
+LIBUSB_ERROR_ACCESS = -3
+LIBUSB_ERROR_NO_DEVICE = -4
+LIBUSB_ERROR_NOT_FOUND = -5
+LIBUSB_ERROR_BUSY = -6
+LIBUSB_ERROR_TIMEOUT = -7
+LIBUSB_ERROR_OVERFLOW = -8
+LIBUSB_ERROR_PIPE = -9
+LIBUSB_ERROR_INTERRUPTED = -10
+LIBUSB_ERROR_NO_MEM = -11
+LIBUSB_ERROR_NOT_SUPPORTED = -12
+LIBUSB_ERROR_OTHER = -99
+
+# map return codes to strings
+_str_error = {
+ LIBUSB_SUCCESS:'Success (no error)',
+ LIBUSB_ERROR_IO:'Input/output error',
+ LIBUSB_ERROR_INVALID_PARAM:'Invalid parameter',
+ LIBUSB_ERROR_ACCESS:'Access denied (insufficient permissions)',
+ LIBUSB_ERROR_NO_DEVICE:'No such device (it may have been disconnected)',
+ LIBUSB_ERROR_NOT_FOUND:'Entity not found',
+ LIBUSB_ERROR_BUSY:'Resource busy',
+ LIBUSB_ERROR_TIMEOUT:'Operation timed out',
+ LIBUSB_ERROR_OVERFLOW:'Overflow',
+ LIBUSB_ERROR_PIPE:'Pipe error',
+ LIBUSB_ERROR_INTERRUPTED:'System call interrupted (perhaps due to signal)',
+ LIBUSB_ERROR_NO_MEM:'Insufficient memory',
+ LIBUSB_ERROR_NOT_SUPPORTED:'Operation not supported or unimplemented on this platform',
+ LIBUSB_ERROR_OTHER:'Unknown error'
+}
+
+# map return code to errno values
+_libusb_errno = {
+ 0:None,
+ LIBUSB_ERROR_IO:errno.__dict__.get('EIO', None),
+ LIBUSB_ERROR_INVALID_PARAM:errno.__dict__.get('EINVAL', None),
+ LIBUSB_ERROR_ACCESS:errno.__dict__.get('EACCES', None),
+ LIBUSB_ERROR_NO_DEVICE:errno.__dict__.get('ENODEV', None),
+ LIBUSB_ERROR_NOT_FOUND:errno.__dict__.get('ENOENT', None),
+ LIBUSB_ERROR_BUSY:errno.__dict__.get('EBUSY', None),
+ LIBUSB_ERROR_TIMEOUT:errno.__dict__.get('ETIMEDOUT', None),
+ LIBUSB_ERROR_OVERFLOW:errno.__dict__.get('EOVERFLOW', None),
+ LIBUSB_ERROR_PIPE:errno.__dict__.get('EPIPE', None),
+ LIBUSB_ERROR_INTERRUPTED:errno.__dict__.get('EINTR', None),
+ LIBUSB_ERROR_NO_MEM:errno.__dict__.get('ENOMEM', None),
+ LIBUSB_ERROR_NOT_SUPPORTED:errno.__dict__.get('ENOSYS', None),
+ LIBUSB_ERROR_OTHER:None
+}
+
+# Transfer status codes:
+# Note that this does not indicate
+# that the entire amount of requested data was transferred.
+LIBUSB_TRANSFER_COMPLETED = 0
+LIBUSB_TRANSFER_ERROR = 1
+LIBUSB_TRANSFER_TIMED_OUT = 2
+LIBUSB_TRANSFER_CANCELLED = 3
+LIBUSB_TRANSFER_STALL = 4
+LIBUSB_TRANSFER_NO_DEVICE = 5
+LIBUSB_TRANSFER_OVERFLOW = 6
+
+# map return codes to strings
+_str_transfer_error = {
+ LIBUSB_TRANSFER_COMPLETED:'Success (no error)',
+ LIBUSB_TRANSFER_ERROR:'Transfer failed',
+ LIBUSB_TRANSFER_TIMED_OUT:'Transfer timed out',
+ LIBUSB_TRANSFER_CANCELLED:'Transfer was cancelled',
+ LIBUSB_TRANSFER_STALL:'For bulk/interrupt endpoints: halt condition '\
+ 'detected (endpoint stalled). For control '\
+ 'endpoints: control request not supported.',
+ LIBUSB_TRANSFER_NO_DEVICE:'Device was disconnected',
+ LIBUSB_TRANSFER_OVERFLOW:'Device sent more data than requested'
+}
+
+# map transfer codes to errno codes
+_transfer_errno = {
+ LIBUSB_TRANSFER_COMPLETED:0,
+ LIBUSB_TRANSFER_ERROR:errno.__dict__.get('EIO', None),
+ LIBUSB_TRANSFER_TIMED_OUT:errno.__dict__.get('ETIMEDOUT', None),
+ LIBUSB_TRANSFER_CANCELLED:errno.__dict__.get('EAGAIN', None),
+ LIBUSB_TRANSFER_STALL:errno.__dict__.get('EIO', None),
+ LIBUSB_TRANSFER_NO_DEVICE:errno.__dict__.get('ENODEV', None),
+ LIBUSB_TRANSFER_OVERFLOW:errno.__dict__.get('EOVERFLOW', None)
+}
+
+# Data structures
+
+class _libusb_endpoint_descriptor(Structure):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('bEndpointAddress', c_uint8),
+ ('bmAttributes', c_uint8),
+ ('wMaxPacketSize', c_uint16),
+ ('bInterval', c_uint8),
+ ('bRefresh', c_uint8),
+ ('bSynchAddress', c_uint8),
+ ('extra', POINTER(c_ubyte)),
+ ('extra_length', c_int)]
+
+class _libusb_interface_descriptor(Structure):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('bInterfaceNumber', c_uint8),
+ ('bAlternateSetting', c_uint8),
+ ('bNumEndpoints', c_uint8),
+ ('bInterfaceClass', c_uint8),
+ ('bInterfaceSubClass', c_uint8),
+ ('bInterfaceProtocol', c_uint8),
+ ('iInterface', c_uint8),
+ ('endpoint', POINTER(_libusb_endpoint_descriptor)),
+ ('extra', POINTER(c_ubyte)),
+ ('extra_length', c_int)]
+
+class _libusb_interface(Structure):
+ _fields_ = [('altsetting', POINTER(_libusb_interface_descriptor)),
+ ('num_altsetting', c_int)]
+
+class _libusb_config_descriptor(Structure):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('wTotalLength', c_uint16),
+ ('bNumInterfaces', c_uint8),
+ ('bConfigurationValue', c_uint8),
+ ('iConfiguration', c_uint8),
+ ('bmAttributes', c_uint8),
+ ('bMaxPower', c_uint8),
+ ('interface', POINTER(_libusb_interface)),
+ ('extra', POINTER(c_ubyte)),
+ ('extra_length', c_int)]
+
+class _libusb_device_descriptor(Structure):
+ _fields_ = [('bLength', c_uint8),
+ ('bDescriptorType', c_uint8),
+ ('bcdUSB', c_uint16),
+ ('bDeviceClass', c_uint8),
+ ('bDeviceSubClass', c_uint8),
+ ('bDeviceProtocol', c_uint8),
+ ('bMaxPacketSize0', c_uint8),
+ ('idVendor', c_uint16),
+ ('idProduct', c_uint16),
+ ('bcdDevice', c_uint16),
+ ('iManufacturer', c_uint8),
+ ('iProduct', c_uint8),
+ ('iSerialNumber', c_uint8),
+ ('bNumConfigurations', c_uint8)]
+
+
+# Isochronous packet descriptor.
+class _libusb_iso_packet_descriptor(Structure):
+ _fields_ = [('length', c_uint),
+ ('actual_length', c_uint),
+ ('status', c_int)] # enum libusb_transfer_status
+
+_libusb_device_handle = c_void_p
+
+class _libusb_transfer(Structure):
+ pass
+_libusb_transfer_p = POINTER(_libusb_transfer)
+
+_libusb_transfer_cb_fn_p = CFUNCTYPE(None, _libusb_transfer_p)
+
+_libusb_transfer._fields_ = [('dev_handle', _libusb_device_handle),
+ ('flags', c_uint8),
+ ('endpoint', c_uint8),
+ ('type', c_uint8),
+ ('timeout', c_uint),
+ ('status', c_int), # enum libusb_transfer_status
+ ('length', c_int),
+ ('actual_length', c_int),
+ ('callback', _libusb_transfer_cb_fn_p),
+ ('user_data', py_object),
+ ('buffer', c_void_p),
+ ('num_iso_packets', c_int),
+ ('iso_packet_desc', _libusb_iso_packet_descriptor)
+]
+
+def _get_iso_packet_list(transfer):
+ list_type = _libusb_iso_packet_descriptor * transfer.num_iso_packets
+ return list_type.from_address(addressof(transfer.iso_packet_desc))
+
+_lib = None
+
+def _load_library():
+ if sys.platform != 'cygwin':
+ candidates = ('usb-1.0', 'libusb-1.0', 'usb')
+ for candidate in candidates:
+ if sys.platform == 'win32':
+ candidate = candidate + '.dll'
+
+ libname = ctypes.util.find_library(candidate)
+ if libname is not None: break
+ else:
+ # corner cases
+ # cygwin predefines library names with 'cyg' instead of 'lib'
+ try:
+ return CDLL('cygusb-1.0.dll')
+ except Exception:
+ _logger.error('Libusb 1.0 could not be loaded in cygwin', exc_info=True)
+
+ raise OSError('USB library could not be found')
+ # Windows backend uses stdcall calling convention
+ if sys.platform == 'win32':
+ l = WinDLL(libname)
+ else:
+ l = CDLL(libname)
+ # On FreeBSD 8/9, libusb 1.0 and libusb 0.1 are in the same shared
+ # object libusb.so, so if we found libusb library name, we must assure
+ # it is 1.0 version. We just try to get some symbol from 1.0 version
+ if not hasattr(l, 'libusb_init'):
+ raise OSError('USB library could not be found')
+ return l
+
+def _setup_prototypes(lib):
+ # void libusb_set_debug (libusb_context *ctx, int level)
+ lib.libusb_set_debug.argtypes = [c_void_p, c_int]
+
+ # int libusb_init (libusb_context **context)
+ lib.libusb_init.argtypes = [POINTER(c_void_p)]
+
+ # void libusb_exit (struct libusb_context *ctx)
+ lib.libusb_exit.argtypes = [c_void_p]
+
+ # ssize_t libusb_get_device_list (libusb_context *ctx,
+ # libusb_device ***list)
+ lib.libusb_get_device_list.argtypes = [
+ c_void_p,
+ POINTER(POINTER(c_void_p))
+ ]
+
+ # void libusb_free_device_list (libusb_device **list,
+ # int unref_devices)
+ lib.libusb_free_device_list.argtypes = [
+ POINTER(c_void_p),
+ c_int
+ ]
+
+ # libusb_device *libusb_ref_device (libusb_device *dev)
+ lib.libusb_ref_device.argtypes = [c_void_p]
+ lib.libusb_ref_device.restype = c_void_p
+
+ # void libusb_unref_device(libusb_device *dev)
+ lib.libusb_unref_device.argtypes = [c_void_p]
+
+ # int libusb_open(libusb_device *dev, libusb_device_handle **handle)
+ lib.libusb_open.argtypes = [c_void_p, POINTER(_libusb_device_handle)]
+
+ # void libusb_close(libusb_device_handle *dev_handle)
+ lib.libusb_close.argtypes = [_libusb_device_handle]
+
+ # int libusb_set_configuration(libusb_device_handle *dev,
+ # int configuration)
+ lib.libusb_set_configuration.argtypes = [_libusb_device_handle, c_int]
+
+ # int libusb_get_configuration(libusb_device_handle *dev,
+ # int *config)
+ lib.libusb_get_configuration.argtypes = [_libusb_device_handle, POINTER(c_int)]
+
+ # int libusb_claim_interface(libusb_device_handle *dev,
+ # int interface_number)
+ lib.libusb_claim_interface.argtypes = [_libusb_device_handle, c_int]
+
+ # int libusb_release_interface(libusb_device_handle *dev,
+ # int interface_number)
+ lib.libusb_release_interface.argtypes = [_libusb_device_handle, c_int]
+
+ # int libusb_set_interface_alt_setting(libusb_device_handle *dev,
+ # int interface_number,
+ # int alternate_setting)
+ lib.libusb_set_interface_alt_setting.argtypes = [
+ _libusb_device_handle,
+ c_int,
+ c_int
+ ]
+
+ # int libusb_reset_device (libusb_device_handle *dev)
+ lib.libusb_reset_device.argtypes = [_libusb_device_handle]
+
+ # int libusb_kernel_driver_active(libusb_device_handle *dev,
+ # int interface)
+ lib.libusb_kernel_driver_active.argtypes = [
+ _libusb_device_handle,
+ c_int
+ ]
+
+ # int libusb_detach_kernel_driver(libusb_device_handle *dev,
+ # int interface)
+ lib.libusb_detach_kernel_driver.argtypes = [
+ _libusb_device_handle,
+ c_int
+ ]
+
+ # int libusb_attach_kernel_driver(libusb_device_handle *dev,
+ # int interface)
+ lib.libusb_attach_kernel_driver.argtypes = [
+ _libusb_device_handle,
+ c_int
+ ]
+
+ # int libusb_get_device_descriptor(
+ # libusb_device *dev,
+ # struct libusb_device_descriptor *desc
+ # )
+ lib.libusb_get_device_descriptor.argtypes = [
+ c_void_p,
+ POINTER(_libusb_device_descriptor)
+ ]
+
+ # int libusb_get_config_descriptor(
+ # libusb_device *dev,
+ # uint8_t config_index,
+ # struct libusb_config_descriptor **config
+ # )
+ lib.libusb_get_config_descriptor.argtypes = [
+ c_void_p,
+ c_uint8,
+ POINTER(POINTER(_libusb_config_descriptor))
+ ]
+
+ # void libusb_free_config_descriptor(
+ # struct libusb_config_descriptor *config
+ # )
+ lib.libusb_free_config_descriptor.argtypes = [
+ POINTER(_libusb_config_descriptor)
+ ]
+
+ # int libusb_get_string_descriptor_ascii(libusb_device_handle *dev,
+ # uint8_t desc_index,
+ # unsigned char *data,
+ # int length)
+ lib.libusb_get_string_descriptor_ascii.argtypes = [
+ _libusb_device_handle,
+ c_uint8,
+ POINTER(c_ubyte),
+ c_int
+ ]
+
+ # int libusb_control_transfer(libusb_device_handle *dev_handle,
+ # uint8_t bmRequestType,
+ # uint8_t bRequest,
+ # uint16_t wValue,
+ # uint16_t wIndex,
+ # unsigned char *data,
+ # uint16_t wLength,
+ # unsigned int timeout)
+ lib.libusb_control_transfer.argtypes = [
+ _libusb_device_handle,
+ c_uint8,
+ c_uint8,
+ c_uint16,
+ c_uint16,
+ POINTER(c_ubyte),
+ c_uint16,
+ c_uint
+ ]
+
+ #int libusb_bulk_transfer(
+ # struct libusb_device_handle *dev_handle,
+ # unsigned char endpoint,
+ # unsigned char *data,
+ # int length,
+ # int *transferred,
+ # unsigned int timeout
+ # )
+ lib.libusb_bulk_transfer.argtypes = [
+ _libusb_device_handle,
+ c_ubyte,
+ POINTER(c_ubyte),
+ c_int,
+ POINTER(c_int),
+ c_uint
+ ]
+
+ # int libusb_interrupt_transfer(
+ # libusb_device_handle *dev_handle,
+ # unsigned char endpoint,
+ # unsigned char *data,
+ # int length,
+ # int *actual_length,
+ # unsigned int timeout
+ # );
+ lib.libusb_interrupt_transfer.argtypes = [
+ _libusb_device_handle,
+ c_ubyte,
+ POINTER(c_ubyte),
+ c_int,
+ POINTER(c_int),
+ c_uint
+ ]
+
+ # libusb_transfer* libusb_alloc_transfer(int iso_packets);
+ lib.libusb_alloc_transfer.argtypes = [c_int]
+ lib.libusb_alloc_transfer.restype = POINTER(_libusb_transfer)
+
+ # void libusb_free_transfer(struct libusb_transfer *transfer)
+ lib.libusb_free_transfer.argtypes = [POINTER(_libusb_transfer)]
+
+ # int libusb_submit_transfer(struct libusb_transfer *transfer);
+ lib.libusb_submit_transfer.argtypes = [POINTER(_libusb_transfer)]
+
+ # void libusb_set_iso_packet_lengths(
+ # libusb_transfer* transfer,
+ # unsigned int length
+ # );
+ def libusb_set_iso_packet_lengths(transfer_p, length):
+ r"""This function is inline in the libusb.h file, so we must implement
+ it.
+
+ lib.libusb_set_iso_packet_lengths.argtypes = [
+ POINTER(_libusb_transfer),
+ c_int
+ ]
+ """
+ transfer = transfer_p.contents
+ for iso_packet_desc in _get_iso_packet_list(transfer):
+ iso_packet_desc.length = length
+ lib.libusb_set_iso_packet_lengths = libusb_set_iso_packet_lengths
+
+ #int libusb_get_max_iso_packet_size(libusb_device* dev,
+ # unsigned char endpoint);
+ lib.libusb_get_max_iso_packet_size.argtypes = [c_void_p,
+ c_ubyte]
+
+ # void libusb_fill_iso_transfer(
+ # struct libusb_transfer* transfer,
+ # libusb_device_handle* dev_handle,
+ # unsigned char endpoint,
+ # unsigned char* buffer,
+ # int length,
+ # int num_iso_packets,
+ # libusb_transfer_cb_fn callback,
+ # void * user_data,
+ # unsigned int timeout
+ # );
+ def libusb_fill_iso_transfer(_libusb_transfer_p, dev_handle, endpoint, buffer, length,
+ num_iso_packets, callback, user_data, timeout):
+ r"""This function is inline in the libusb.h file, so we must implement
+ it.
+
+ lib.libusb_fill_iso_transfer.argtypes = [
+ _libusb_transfer,
+ _libusb_device_handle,
+ c_ubyte,
+ POINTER(c_ubyte),
+ c_int,
+ c_int,
+ _libusb_transfer_cb_fn_p,
+ c_void_p,
+ c_uint
+ ]
+ """
+ transfer = _libusb_transfer_p.contents
+ transfer.dev_handle = dev_handle
+ transfer.endpoint = endpoint
+ transfer.type = _LIBUSB_TRANSFER_TYPE_ISOCHRONOUS
+ transfer.timeout = timeout
+ transfer.buffer = cast(buffer, c_void_p)
+ transfer.length = length
+ transfer.num_iso_packets = num_iso_packets
+ transfer.user_data = user_data
+ transfer.callback = callback
+ lib.libusb_fill_iso_transfer = libusb_fill_iso_transfer
+
+ # uint8_t libusb_get_bus_number(libusb_device *dev)
+ lib.libusb_get_bus_number.argtypes = [c_void_p]
+ lib.libusb_get_bus_number.restype = c_uint8
+
+ # uint8_t libusb_get_device_address(libusb_device *dev)
+ lib.libusb_get_device_address.argtypes = [c_void_p]
+ lib.libusb_get_device_address.restype = c_uint8
+
+ try:
+ # uint8_t libusb_get_port_number(libusb_device *dev)
+ lib.libusb_get_port_number.argtypes = [c_void_p]
+ lib.libusb_get_port_number.restype = c_uint8
+ except AttributeError:
+ pass
+
+ #int libusb_handle_events(libusb_context *ctx);
+ lib.libusb_handle_events.argtypes = [c_void_p]
+
+# check a libusb function call
+def _check(retval):
+ if isinstance(retval, int):
+ retval = c_int(retval)
+ if isinstance(retval, c_int):
+ if retval.value < 0:
+ ret = retval.value
+ raise USBError(_str_error[ret], ret, _libusb_errno[ret])
+ return retval
+
+# wrap a device
+class _Device(object):
+ def __init__(self, devid):
+ self.devid = _lib.libusb_ref_device(devid)
+ def __del__(self):
+ _lib.libusb_unref_device(self.devid)
+
+# wrap a descriptor and keep a reference to another object
+# Thanks to Thomas Reitmayr.
+class _WrapDescriptor(object):
+ def __init__(self, desc, obj = None):
+ self.obj = obj
+ self.desc = desc
+ def __getattr__(self, name):
+ return getattr(self.desc, name)
+
+# wrap a configuration descriptor
+class _ConfigDescriptor(object):
+ def __init__(self, desc):
+ self.desc = desc
+ def __del__(self):
+ _lib.libusb_free_config_descriptor(self.desc)
+ def __getattr__(self, name):
+ return getattr(self.desc.contents, name)
+
+
+# iterator for libusb devices
+class _DevIterator(object):
+ def __init__(self, ctx):
+ self.dev_list = POINTER(c_void_p)()
+ self.num_devs = _check(_lib.libusb_get_device_list(
+ ctx,
+ byref(self.dev_list))
+ ).value
+ def __iter__(self):
+ for i in range(self.num_devs):
+ yield _Device(self.dev_list[i])
+ def __del__(self):
+ _lib.libusb_free_device_list(self.dev_list, 1)
+
+class _DeviceHandle(object):
+ def __init__(self, dev):
+ self.handle = _libusb_device_handle()
+ self.devid = dev.devid
+ _check(_lib.libusb_open(self.devid, byref(self.handle)))
+
+class _IsoTransferHandler(object):
+ def __init__(self, dev_handle, ep, buff, timeout):
+ address, length = buff.buffer_info()
+
+ packet_length = _lib.libusb_get_max_iso_packet_size(dev_handle.devid, ep)
+ packet_count = int(math.ceil(float(length) / packet_length))
+
+ self.transfer = _lib.libusb_alloc_transfer(packet_count)
+
+ _lib.libusb_fill_iso_transfer(self.transfer,
+ dev_handle.handle,
+ ep,
+ cast(address, POINTER(c_ubyte)),
+ length,
+ packet_count,
+ _libusb_transfer_cb_fn_p(self.__callback),
+ None,
+ timeout)
+
+ self.__set_packets_length(length, packet_length)
+
+ def __del__(self):
+ _lib.libusb_free_transfer(self.transfer)
+
+ def submit(self, ctx = None):
+ self.__callback_done = 0
+ _check(_lib.libusb_submit_transfer(self.transfer))
+
+ while not self.__callback_done:
+ _check(_lib.libusb_handle_events(ctx))
+
+ return self.__compute_size_transf_data()
+
+ def __compute_size_transf_data(self):
+ return sum([t.actual_length for t in
+ _get_iso_packet_list(self.transfer.contents)])
+
+ def __set_packets_length(self, n, packet_length):
+ _lib.libusb_set_iso_packet_lengths(self.transfer, packet_length)
+ r = n % packet_length
+ if r:
+ iso_packets = _get_iso_packet_list(self.transfer.contents)
+ iso_packets[-1].length = r
+
+ def __callback(self, transfer):
+ if transfer.contents.status == LIBUSB_TRANSFER_COMPLETED:
+ self.__callback_done = 1
+ else:
+ status = int(transfer.contents.status)
+ raise usb.USBError(_str_transfer_error[status],
+ status,
+ _transfer_errno[status])
+
+# implementation of libusb 1.0 backend
+class _LibUSB(usb.backend.IBackend):
+ @methodtrace(_logger)
+ def __init__(self, lib):
+ usb.backend.IBackend.__init__(self)
+ self.lib = lib
+ self.ctx = c_void_p()
+ _check(self.lib.libusb_init(byref(self.ctx)))
+
+ @methodtrace(_logger)
+ def __del__(self):
+ self.lib.libusb_exit(self.ctx)
+
+
+ @methodtrace(_logger)
+ def enumerate_devices(self):
+ return _DevIterator(self.ctx)
+
+ @methodtrace(_logger)
+ def get_device_descriptor(self, dev):
+ dev_desc = _libusb_device_descriptor()
+ _check(self.lib.libusb_get_device_descriptor(dev.devid, byref(dev_desc)))
+ dev_desc.bus = self.lib.libusb_get_bus_number(dev.devid)
+ dev_desc.address = self.lib.libusb_get_device_address(dev.devid)
+
+ #Only available i newer versions of libusb
+ try:
+ dev_desc.port_number = self.lib.libusb_get_port_number(dev.devid)
+ except AttributeError:
+ dev_desc.port_number = None
+
+ return dev_desc
+
+ @methodtrace(_logger)
+ def get_configuration_descriptor(self, dev, config):
+ cfg = POINTER(_libusb_config_descriptor)()
+ _check(self.lib.libusb_get_config_descriptor(
+ dev.devid,
+ config, byref(cfg)))
+ return _ConfigDescriptor(cfg)
+
+ @methodtrace(_logger)
+ def get_interface_descriptor(self, dev, intf, alt, config):
+ cfg = self.get_configuration_descriptor(dev, config)
+ if intf >= cfg.bNumInterfaces:
+ raise IndexError('Invalid interface index ' + str(intf))
+ i = cfg.interface[intf]
+ if alt >= i.num_altsetting:
+ raise IndexError('Invalid alternate setting index ' + str(alt))
+ return _WrapDescriptor(i.altsetting[alt], cfg)
+
+ @methodtrace(_logger)
+ def get_endpoint_descriptor(self, dev, ep, intf, alt, config):
+ i = self.get_interface_descriptor(dev, intf, alt, config)
+ if ep > i.bNumEndpoints:
+ raise IndexError('Invalid endpoint index ' + str(ep))
+ return _WrapDescriptor(i.endpoint[ep], i)
+
+ @methodtrace(_logger)
+ def open_device(self, dev):
+ return _DeviceHandle(dev)
+
+ @methodtrace(_logger)
+ def close_device(self, dev_handle):
+ self.lib.libusb_close(dev_handle.handle)
+
+ @methodtrace(_logger)
+ def set_configuration(self, dev_handle, config_value):
+ _check(self.lib.libusb_set_configuration(dev_handle.handle, config_value))
+
+ @methodtrace(_logger)
+ def get_configuration(self, dev_handle):
+ config = c_int()
+ _check(self.lib.libusb_get_configuration(dev_handle.handle, byref(config)))
+ return config.value
+
+ @methodtrace(_logger)
+ def set_interface_altsetting(self, dev_handle, intf, altsetting):
+ _check(self.lib.libusb_set_interface_alt_setting(
+ dev_handle.handle,
+ intf,
+ altsetting))
+
+ @methodtrace(_logger)
+ def claim_interface(self, dev_handle, intf):
+ _check(self.lib.libusb_claim_interface(dev_handle.handle, intf))
+
+ @methodtrace(_logger)
+ def release_interface(self, dev_handle, intf):
+ _check(self.lib.libusb_release_interface(dev_handle.handle, intf))
+
+ @methodtrace(_logger)
+ def bulk_write(self, dev_handle, ep, intf, data, timeout):
+ return self.__write(self.lib.libusb_bulk_transfer,
+ dev_handle,
+ ep,
+ intf,
+ data,
+ timeout)
+
+ @methodtrace(_logger)
+ def bulk_read(self, dev_handle, ep, intf, size, timeout):
+ return self.__read(self.lib.libusb_bulk_transfer,
+ dev_handle,
+ ep,
+ intf,
+ size,
+ timeout)
+
+ @methodtrace(_logger)
+ def intr_write(self, dev_handle, ep, intf, data, timeout):
+ return self.__write(self.lib.libusb_interrupt_transfer,
+ dev_handle,
+ ep,
+ intf,
+ data,
+ timeout)
+
+ @methodtrace(_logger)
+ def intr_read(self, dev_handle, ep, intf, size, timeout):
+ return self.__read(self.lib.libusb_interrupt_transfer,
+ dev_handle,
+ ep,
+ intf,
+ size,
+ timeout)
+
+ @methodtrace(_logger)
+ def iso_write(self, dev_handle, ep, intf, data, timeout):
+ handler = _IsoTransferHandler(dev_handle, ep, data, timeout)
+ return handler.submit(self.ctx)
+
+ @methodtrace(_logger)
+ def iso_read(self, dev_handle, ep, intf, size, timeout):
+ data = _interop.as_array('\x00' * size)
+ handler = _IsoTransferHandler(dev_handle, ep, data, timeout)
+ return data[:handler.submit(self.ctx)]
+
+ @methodtrace(_logger)
+ def ctrl_transfer(self,
+ dev_handle,
+ bmRequestType,
+ bRequest,
+ wValue,
+ wIndex,
+ data_or_wLength,
+ timeout):
+ if usb.util.ctrl_direction(bmRequestType) == usb.util.CTRL_OUT:
+ buff = data_or_wLength
+ else:
+ buff = _interop.as_array((0,) * data_or_wLength)
+
+ addr, length = buff.buffer_info()
+ length *= buff.itemsize
+
+ ret = _check(self.lib.libusb_control_transfer(
+ dev_handle.handle,
+ bmRequestType,
+ bRequest,
+ wValue,
+ wIndex,
+ cast(addr, POINTER(c_ubyte)),
+ length,
+ timeout))
+
+ if usb.util.ctrl_direction(bmRequestType) == usb.util.CTRL_OUT:
+ return ret.value
+ else:
+ return buff[:ret.value]
+
+ @methodtrace(_logger)
+ def reset_device(self, dev_handle):
+ _check(self.lib.libusb_reset_device(dev_handle.handle))
+
+ @methodtrace(_logger)
+ def is_kernel_driver_active(self, dev_handle, intf):
+ return bool(_check(self.lib.libusb_kernel_driver_active(dev_handle.handle,
+ intf)))
+
+ @methodtrace(_logger)
+ def detach_kernel_driver(self, dev_handle, intf):
+ _check(self.lib.libusb_detach_kernel_driver(dev_handle.handle, intf))
+
+ @methodtrace(_logger)
+ def attach_kernel_driver(self, dev_handle, intf):
+ _check(self.lib.libusb_attach_kernel_driver(dev_handle.handle, intf))
+
+ def __write(self, fn, dev_handle, ep, intf, data, timeout):
+ address, length = data.buffer_info()
+ length *= data.itemsize
+ transferred = c_int()
+ retval = fn(dev_handle.handle,
+ ep,
+ cast(address, POINTER(c_ubyte)),
+ length,
+ byref(transferred),
+ timeout)
+ # do not assume LIBUSB_ERROR_TIMEOUT means no I/O.
+ if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT):
+ _check(retval)
+
+ return transferred.value
+
+ def __read(self, fn, dev_handle, ep, intf, size, timeout):
+ data = _interop.as_array('\x00' * size)
+ address, length = data.buffer_info()
+ length *= data.itemsize
+ transferred = c_int()
+ retval = fn(dev_handle.handle,
+ ep,
+ cast(address, POINTER(c_ubyte)),
+ length,
+ byref(transferred),
+ timeout)
+ # do not assume LIBUSB_ERROR_TIMEOUT means no I/O.
+ if not (transferred.value and retval == LIBUSB_ERROR_TIMEOUT):
+ _check(retval)
+ return data[:transferred.value]
+
+def get_backend():
+ global _lib
+ try:
+ if _lib is None:
+ _lib = _load_library()
+ _setup_prototypes(_lib)
+ return _LibUSB(_lib)
+ except Exception:
+ _logger.error('Error loading libusb 1.0 backend', exc_info=True)
+ return None
diff --git a/plugins/wedo_plugin/usb/backend/openusb.py b/plugins/wedo_plugin/usb/backend/openusb.py
index 23e4d45..3398c4a 100644
--- a/plugins/wedo_plugin/usb/backend/openusb.py
+++ b/plugins/wedo_plugin/usb/backend/openusb.py
@@ -1,8 +1,8 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
-#
+# Copyright (C) 2009-2013 Wander Lairson Costa
+#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
-#
+#
# The authors hereby grant permission to use, copy, modify, distribute,
# and license this software and its documentation for any purpose, provided
# that existing copyright notices are retained in all copies and that this
@@ -12,13 +12,13 @@
# and need not follow the licensing terms described here, provided that
# the new terms are clearly indicated on the first page of each file where
# they apply.
-#
+#
# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
+#
# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
@@ -32,6 +32,8 @@ import usb.util
from usb._debug import methodtrace
import logging
import errno
+import sys
+import usb._interop as _interop
__author__ = 'Wander Lairson Costa'
@@ -187,7 +189,7 @@ class _usb_device_desc(Structure):
class _openusb_request_result(Structure):
_fields_ = [('status', c_int32),
- ('transfered_bytes', c_uint32)]
+ ('transferred_bytes', c_uint32)]
class _openusb_ctrl_request(Structure):
class _openusb_ctrl_setup(Structure):
@@ -195,7 +197,8 @@ class _openusb_ctrl_request(Structure):
('bRequest', c_uint8),
('wValue', c_uint16),
('wIndex', c_uint16)]
- _fields_ = [('payload', POINTER(c_uint8)),
+ _fields_ = [('setup', _openusb_ctrl_setup),
+ ('payload', POINTER(c_uint8)),
('length', c_uint32),
('timeout', c_uint32),
('flags', c_uint32),
@@ -243,7 +246,12 @@ _lib = None
_ctx = None
def _load_library():
- libname = ctypes.util.find_library('openusb')
+ candidate = 'openusb'
+ # Workaround for CPython 3.3 issue#16283 / pyusb #14
+ if sys.platform == 'win32':
+ candidate = candidate + '.dll'
+
+ libname = ctypes.util.find_library(candidate)
if libname is None:
raise OSError('USB library could not be found')
return CDLL(libname)
@@ -396,7 +404,7 @@ def _setup_prototypes(lib):
]
lib.openusb_parse_interface_desc.restype = c_int32
-
+
# int32_t openusb_parse_endpoint_desc(openusb_handle_t handle,
# openusb_devid_t devid,
# uint8_t *buffer,
@@ -477,7 +485,10 @@ def _setup_prototypes(lib):
lib.openusb_isoc_xfer.restype = c_int32
def _check(retval):
- ret = retval.value
+ if isinstance(retval, c_int):
+ ret = retval.value
+ else:
+ ret = retval
if ret != 0:
from usb.core import USBError
raise USBError(_lib.openusb_strerror(ret), ret, _openusb_errno[ret])
@@ -492,7 +503,7 @@ class _Context(object):
class _BusIterator(object):
def __init__(self):
- self.buslist = POINTER(openusb_busid)()
+ self.buslist = POINTER(_openusb_busid)()
num_busids = c_uint32()
_check(_lib.openusb_get_busid_list(_ctx.handle,
byref(self.buslist),
@@ -536,6 +547,7 @@ class _OpenUSB(usb.backend.IBackend):
byref(desc)))
desc.bus = None
desc.address = None
+ desc.port_number = None
return desc
@methodtrace(_logger)
@@ -598,7 +610,7 @@ class _OpenUSB(usb.backend.IBackend):
@methodtrace(_logger)
def set_interface_altsetting(self, dev_handle, intf, altsetting):
- _check(_lib.set_altsetting(dev_handle, intf, altsetting))
+ _check(_lib.openusb_set_altsetting(dev_handle, intf, altsetting))
@methodtrace(_logger)
def claim_interface(self, dev_handle, intf):
@@ -612,20 +624,22 @@ class _OpenUSB(usb.backend.IBackend):
def bulk_write(self, dev_handle, ep, intf, data, timeout):
request = _openusb_bulk_request()
memset(byref(request), 0, sizeof(request))
- request.payload, request.length = data.buffer_info()
+ payload, request.length = data.buffer_info()
+ request.payload = cast(payload, POINTER(c_uint8))
request.timeout = timeout
_check(_lib.openusb_bulk_xfer(dev_handle, intf, ep, byref(request)))
- return request.transfered_bytes.value
+ return request.result.transferred_bytes
@methodtrace(_logger)
def bulk_read(self, dev_handle, ep, intf, size, timeout):
request = _openusb_bulk_request()
- buffer = array.array('B', '\x00' * size)
+ buffer = _interop.as_array('\x00' * size)
memset(byref(request), 0, sizeof(request))
- request.payload, request.length = buffer.buffer_info()
+ payload, request.length = buffer.buffer_info()
+ request.payload = cast(payload, POINTER(c_uint8))
request.timeout = timeout
_check(_lib.openusb_bulk_xfer(dev_handle, intf, ep, byref(request)))
- return buffer[:request.transfered_bytes.value]
+ return buffer[:request.result.transferred_bytes]
@methodtrace(_logger)
def intr_write(self, dev_handle, ep, intf, data, timeout):
@@ -635,18 +649,18 @@ class _OpenUSB(usb.backend.IBackend):
request.payload = cast(payload, POINTER(c_uint8))
request.timeout = timeout
_check(_lib.openusb_intr_xfer(dev_handle, intf, ep, byref(request)))
- return request.transfered_bytes.value
+ return request.result.transferred_bytes
@methodtrace(_logger)
def intr_read(self, dev_handle, ep, intf, size, timeout):
request = _openusb_intr_request()
- buffer = array.array('B', '\x00' * size)
+ buffer = _interop.as_array('B', '\x00' * size)
memset(byref(request), 0, sizeof(request))
payload, request.length = buffer.buffer_info()
request.payload = cast(payload, POINTER(c_uint8))
request.timeout = timeout
_check(_lib.openusb_intr_xfer(dev_handle, intf, ep, byref(request)))
- return buffer[:request.transfered_bytes.value]
+ return buffer[:request.result.transferred_bytes]
# TODO: implement isochronous
# @methodtrace(_logger)
@@ -675,20 +689,20 @@ class _OpenUSB(usb.backend.IBackend):
direction = usb.util.ctrl_direction(bmRequestType)
- if direction == ENDPOINT_OUT:
+ if direction == usb.util.CTRL_OUT:
buffer = data_or_wLength
else:
- buffer = array.array('B', '\x00' * data_or_wLength)
+ buffer = _interop.as_array('\x00' * data_or_wLength)
payload, request.length = buffer.buffer_info()
request.payload = cast(payload, POINTER(c_uint8))
- ret = _check(_lib.openusb_ctrl_xfer(dev_handle, 0, 0, byref(request)))
+ _check(_lib.openusb_ctrl_xfer(dev_handle, 0, 0, byref(request)))
- if direction == ENDPOINT_OUT:
- ret
+ if direction == usb.util.CTRL_OUT:
+ return request.result.transferred_bytes
else:
- buffer[:ret]
+ return buffer[:request.result.transferred_bytes]
@methodtrace(_logger)
def reset_device(self, dev_handle):
diff --git a/plugins/wedo_plugin/usb/control.py b/plugins/wedo_plugin/usb/control.py
index 8647c14..c7726da 100644
--- a/plugins/wedo_plugin/usb/control.py
+++ b/plugins/wedo_plugin/usb/control.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
+# Copyright (C) 2009-2013 Wander Lairson Costa
#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
diff --git a/plugins/wedo_plugin/usb/core.py b/plugins/wedo_plugin/usb/core.py
index c90d011..e2509b8 100644
--- a/plugins/wedo_plugin/usb/core.py
+++ b/plugins/wedo_plugin/usb/core.py
@@ -1,8 +1,8 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
-#
+# Copyright (C) 2009-2013 Wander Lairson Costa
+#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
-#
+#
# The authors hereby grant permission to use, copy, modify, distribute,
# and license this software and its documentation for any purpose, provided
# that existing copyright notices are retained in all copies and that this
@@ -12,13 +12,13 @@
# and need not follow the licensing terms described here, provided that
# the new terms are clearly indicated on the first page of each file where
# they apply.
-#
+#
# IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
# FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
# ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
# DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
-#
+#
# THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
@@ -53,7 +53,7 @@ _DEFAULT_TIMEOUT = 1000
def _set_attr(input, output, fields):
for f in fields:
- setattr(output, f, int(getattr(input, f)))
+ setattr(output, f, getattr(input, f))
class _ResourceManager(object):
def __init__(self, dev, backend):
@@ -125,12 +125,12 @@ class _ResourceManager(object):
self._claimed_intf.remove(i)
def managed_set_interface(self, device, intf, alt):
- if intf is None:
- i = self.get_interface(device, intf)
- elif isinstance(intf, Interface):
+ if isinstance(intf, Interface):
i = intf
else:
cfg = self.get_active_configuration(device)
+ if intf is None:
+ intf = cfg[(0,0)].bInterfaceNumber
if alt is not None:
i = util.find_descriptor(cfg, bInterfaceNumber=intf, bAlternateSetting=alt)
else:
@@ -144,13 +144,12 @@ class _ResourceManager(object):
def get_interface(self, device, intf):
# TODO: check the viability of issuing a GET_INTERFACE
# request when we don't have a alternate setting cached
- if intf is None:
- cfg = self.get_active_configuration(device)
- return cfg[(0,0)]
- elif isinstance(intf, Interface):
+ if isinstance(intf, Interface):
return intf
else:
cfg = self.get_active_configuration(device)
+ if intf is None:
+ intf = cfg[(0,0)].bInterfaceNumber
if intf in self._alt_set:
return util.find_descriptor(cfg,
bInterfaceNumber=intf,
@@ -197,7 +196,7 @@ class _ResourceManager(object):
class USBError(IOError):
r"""Exception class for USB errors.
-
+
Backends must raise this exception when USB related errors occur.
The backend specific error code is available through the
'backend_error_code' member variable.
@@ -276,7 +275,7 @@ class Endpoint(object):
def write(self, data, timeout = None):
r"""Write data to the endpoint.
-
+
The parameter data contains the data to be sent to the endpoint and
timeout is the time limit of the operation. The transfer type and
endpoint address are automatically inferred.
@@ -289,7 +288,7 @@ class Endpoint(object):
def read(self, size, timeout = None):
r"""Read data from the endpoint.
-
+
The parameter size is the number of bytes to read and timeout is the
time limit of the operation.The transfer type and endpoint address
are automatically inferred.
@@ -388,7 +387,7 @@ class Interface(object):
class Configuration(object):
r"""Represent a configuration object.
-
+
This class contains all fields of the Configuration Descriptor
according to the USB Specification. You may access them as class
properties. For example, to access the field bConfigurationValue
@@ -461,7 +460,7 @@ class Configuration(object):
class Device(object):
r"""Device object.
-
+
This class contains all fields of the Device Descriptor according
to the USB Specification. You may access them as class properties.
For example, to access the field bDescriptorType of the device
@@ -528,16 +527,29 @@ class Device(object):
'iSerialNumber',
'bNumConfigurations',
'address',
- 'bus'
+ 'bus',
+ 'port_number'
)
)
- self.bus = int(desc.bus) if desc.bus is not None else None
- self.address = int(desc.address) if desc.address is not None else None
+ if desc.bus is not None:
+ self.bus = int(desc.bus)
+ else:
+ self.bus = None
+
+ if desc.address is not None:
+ self.address = int(desc.address)
+ else:
+ self.address = None
+
+ if desc.port_number is not None:
+ self.port_number = int(desc.port_number)
+ else:
+ self.port_number = None
def set_configuration(self, configuration = None):
r"""Set the active configuration.
-
+
The configuration parameter is the bConfigurationValue field of the
configuration you want to set as active. If you call this method
without parameter, it will use the first configuration found.
@@ -552,7 +564,7 @@ class Device(object):
def set_interface_altsetting(self, interface = None, alternate_setting = None):
r"""Set the alternate setting for an interface.
-
+
When you want to use an interface and it has more than one alternate setting,
you should call this method to select the alternate setting you would like
to use. If you call the method without one or the two parameters, it will
@@ -578,6 +590,7 @@ class Device(object):
def reset(self):
r"""Reset the device."""
+ self._ctx.managed_open()
self._ctx.dispose(self, False)
self._ctx.backend.reset_device(self._ctx.handle)
self._ctx.dispose(self, True)
@@ -590,7 +603,8 @@ class Device(object):
communicate with. The interface parameter is the bInterfaceNumber field
of the interface descriptor which contains the endpoint. If you do not
provide one, the first one found will be used, as explained in the
- set_interface_altsetting() method.
+ set_interface_altsetting() method. The bInterfaceNumber parameter is
+ not used for most backends, and often can be ignored.
The data parameter should be a sequence like type convertible to
array type (see array module).
@@ -627,8 +641,10 @@ class Device(object):
communicate with. The interface parameter is the bInterfaceNumber field
of the interface descriptor which contains the endpoint. If you do not
provide one, the first one found will be used, as explained in the
- set_interface_altsetting() method. The size parameters tells how many
- bytes you want to read.
+ set_interface_altsetting() method. The bInterfaceNumber parameter is
+ not used for most backends, and often can be ignored.
+
+ The size parameter tells how many bytes you want to read.
The timeout is specified in miliseconds.
@@ -701,7 +717,8 @@ class Device(object):
If a kernel driver is active, and the object will be unable to perform I/O.
"""
self._ctx.managed_open()
- return self._ctx.backend.is_kernel_driver_active(self._ctx.handle, interface)
+ return self._ctx.backend.is_kernel_driver_active(self._ctx.handle,
+ self._ctx.get_interface(self, interface).bInterfaceNumber)
def detach_kernel_driver(self, interface):
r"""Detach a kernel driver.
@@ -709,13 +726,15 @@ class Device(object):
If successful, you will then be able to perform I/O.
"""
self._ctx.managed_open()
- self._ctx.backend.detach_kernel_driver(self._ctx.handle, interface)
+ self._ctx.backend.detach_kernel_driver(self._ctx.handle,
+ self._ctx.get_interface(self, interface).bInterfaceNumber)
def attach_kernel_driver(self, interface):
r"""Re-attach an interface's kernel driver, which was previously
detached using detach_kernel_driver()."""
self._ctx.managed_open()
- self._ctx.backend.attach_kernel_driver(self._ctx.handle, interface)
+ self._ctx.backend.attach_kernel_driver(self._ctx.handle,
+ self._ctx.get_interface(self, interface).bInterfaceNumber)
def __iter__(self):
r"""Iterate over all configurations of the device."""
@@ -820,8 +839,7 @@ def find(find_all=False, backend = None, custom_match = None, **args):
def device_iter(k, v):
for dev in backend.enumerate_devices():
d = Device(dev, backend)
- if (custom_match is None or custom_match(d)) and \
- _interop._reduce(
+ if _interop._reduce(
lambda a, b: a and b,
map(
operator.eq,
@@ -829,15 +847,15 @@ def find(find_all=False, backend = None, custom_match = None, **args):
map(lambda i: getattr(d, i), k)
),
True
- ):
+ ) and (custom_match is None or custom_match(d)):
yield d
if backend is None:
- import usb.backend.libusb10 as libusb10
- import usb.backend.libusb01 as libusb01
+ import usb.backend.libusb1 as libusb1
+ import usb.backend.libusb0 as libusb0
import usb.backend.openusb as openusb
- for m in (libusb10, openusb, libusb01):
+ for m in (libusb1, openusb, libusb0):
backend = m.get_backend()
if backend is not None:
_logger.info('find(): using backend "%s"', m.__name__)
@@ -846,7 +864,7 @@ def find(find_all=False, backend = None, custom_match = None, **args):
raise ValueError('No backend available')
k, v = args.keys(), args.values()
-
+
if find_all:
return [d for d in device_iter(k, v)]
else:
diff --git a/plugins/wedo_plugin/usb/legacy.py b/plugins/wedo_plugin/usb/legacy.py
index 0c9c52c..ebe9f4d 100644
--- a/plugins/wedo_plugin/usb/legacy.py
+++ b/plugins/wedo_plugin/usb/legacy.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
+# Copyright (C) 2009-2013 Wander Lairson Costa
#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
@@ -222,8 +222,10 @@ class DeviceHandle(object):
Arguments:
interface: interface number or an Interface object.
"""
- if_num = interface.interfaceNumber \
- if isinstance(interface, Interface) else interface
+ if isinstance(interface, Interface):
+ if_num = interface.interfaceNumber
+ else:
+ if_num = interface
util.claim_interface(self.dev, if_num)
self.__claimed_interface = if_num
@@ -301,7 +303,11 @@ class Device(object):
self.deviceClass = dev.bDeviceClass
self.deviceSubClass = dev.bDeviceSubClass
self.deviceProtocol = dev.bDeviceProtocol
- self.deviceVersion = dev.bcdDevice
+ self.deviceVersion = str((dev.bcdDevice >> 12) & 0xf) + \
+ str((dev.bcdDevice >> 8) & 0xf) + \
+ '.' + \
+ str((dev.bcdDevice >> 4) & 0xf) + \
+ str(dev.bcdDevice & 0xf)
self.devnum = None
self.filename = ''
self.iManufacturer = dev.iManufacturer
@@ -310,7 +316,11 @@ class Device(object):
self.idProduct = dev.idProduct
self.idVendor = dev.idVendor
self.maxPacketSize = dev.bMaxPacketSize0
- self.usbVersion = dev.bcdUSB
+ self.usbVersion = str((dev.bcdUSB >> 12) & 0xf) + \
+ str((dev.bcdUSB >> 8) & 0xf) + \
+ '.' + \
+ str((dev.bcdUSB >> 4) & 0xf) + \
+ str(dev.bcdUSB & 0xf)
self.configurations = [Configuration(c) for c in dev]
self.dev = dev
@@ -323,12 +333,14 @@ class Device(object):
class Bus(object):
r"""Bus object."""
- def __init__(self):
+ def __init__(self, devices):
self.dirname = ''
- self.localtion = 0
- self.devices = [Device(d) for d in core.find(find_all=True)]
+ self.location = 0
+ self.devices = [Device(d) for d in devices]
def busses():
r"""Return a tuple with the usb busses."""
- return (Bus(),)
+ return (Bus(g) for k, g in _interop._groupby(
+ _interop._sorted(core.find(find_all=True), key=lambda d: d.bus),
+ lambda d: d.bus))
diff --git a/plugins/wedo_plugin/usb/util.py b/plugins/wedo_plugin/usb/util.py
index 1f8cee3..53bcae3 100644
--- a/plugins/wedo_plugin/usb/util.py
+++ b/plugins/wedo_plugin/usb/util.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2011 Wander Lairson Costa
+# Copyright (C) 2009-2013 Wander Lairson Costa
#
# The following terms apply to all files associated
# with the software unless explicitly disclaimed in individual files.
@@ -224,7 +224,7 @@ def get_string(dev, length, index, langid = None):
dev is the Device object to which the request will be
sent to.
- length is the length of string in number of characters.
+ length is the maximum length of the string in number of characters.
index is the string descriptor index and langid is the Language
ID of the descriptor. If langid is omitted, the string descriptor
@@ -243,7 +243,7 @@ def get_string(dev, length, index, langid = None):
# Note from libusb 1.0 sources (descriptor.c)
buf = get_descriptor(
dev,
- 1024,
+ 254,
DESC_TYPE_STRING,
0
)
@@ -257,4 +257,4 @@ def get_string(dev, length, index, langid = None):
index,
langid
)
- return buf[2:].tostring().decode('utf-16-le')
+ return buf[2:buf[0]].tostring().decode('utf-16-le')
diff --git a/plugins/wedo_plugin/wedo/__init__.py b/plugins/wedo_plugin/wedo/__init__.py
new file mode 100644
index 0000000..8f77bb5
--- /dev/null
+++ b/plugins/wedo_plugin/wedo/__init__.py
@@ -0,0 +1,189 @@
+from functools import wraps
+
+from wedo.distance import interpolate_distance_data
+from wedo.motor import processMotorValues
+from wedo.tilt import process_tilt
+from wedo.tilt import FLAT, TILT_BACK, TILT_FORWARD, TILT_LEFT, TILT_RIGHT
+
+import usb.core
+import logging
+
+logger = logging.getLogger('wedo')
+
+ID_VENDOR, ID_PRODUCT = 0x0694, 0x0003
+UNAVAILABLE = None
+TILTSENSOR = (38, 39)
+DISTANCESENSOR = (176, 177, 178, 179)
+MOTOR = (0, 1, 2, 3, 238, 239)
+
+# limit the visibility to simplify the usage
+__all__ = ["scan_for_devices", "WeDo", "FLAT", "TILT_BACK", "TILT_FORWARD", "TILT_LEFT", "TILT_RIGHT"]
+
+def scan_for_devices():
+ """ Find all available devices """
+ return usb.core.find(find_all=True, idVendor=ID_VENDOR, idProduct=ID_PRODUCT)
+
+
+class WeDo(object):
+ """
+ Each instance of this class represents a physical WeDo device.
+
+ Usage :
+
+ >>> from wedo import WeDo
+ >>> wd = WeDo()
+
+ Activating the first motor full forward:
+ >>> wd.motor_a = 100
+
+ Activating the second motor half speed/force backward:
+ >>> wd.motor_b = -50
+
+ Current value of the tilt sensor:
+ >>> wd.tilt
+
+ Current distance value in meters of the distance sensor:
+ >>> wd.distance
+ """
+
+ def __init__(self, device=None):
+ """
+ If a device is not given, it will attach this instance to the first one found.
+ Otherwise you can pass a specific one from the list returned by scan_for_devices.
+ """
+ self.number = 0
+ self.dev = device
+ if self.dev is None:
+ devices = scan_for_devices()
+ if not devices:
+ raise OSError("Could not find a connected WeDo device")
+ self.dev = devices[0]
+ self.init_device()
+ self.valMotorA = 0
+ self.valMotorB = 0
+
+ def init_device(self):
+ """
+ Reinit device associated with the WeDo instance
+ """
+ if self.dev is None:
+ raise ValueError("No device attached to this instance")
+ try:
+ if self.dev.is_kernel_driver_active(0):
+ try:
+ self.dev.detach_kernel_driver(0)
+ except usb.core.USBError as e:
+ logger.error(
+ "Could not detatch kernel driver: %s" % (str(e)))
+ self.endpoint = self.dev[0][(0, 0)][0]
+ except usb.core.USBError as e:
+ logger.error("Could not talk to WeDo device: %s" % (str(e)))
+
+ def getRawData(self):
+ """Read 64 bytes from the WeDo's endpoint, but only
+ return the last eight."""
+ try:
+ return self.endpoint.read(64)[-8:]
+ except usb.core.USBError as e:
+ logger.exception("Could not read from WeDo device")
+ return None
+
+ def setMotors(self):
+ """
+ Arguments should be in form of a number between 0
+ and 100, positive or negative. Magic numbers used for
+ the ctrl_transfer derived from sniffing USB coms.
+ """
+ data = [64, processMotorValues(self.valMotorA) & 0xFF, processMotorValues(self.valMotorB) & 0xFF,
+ 0x00, 0x00, 0x00, 0x00, 0x00]
+ try:
+ self.dev.ctrl_transfer(bmRequestType=0x21, bRequest=0x09, wValue=0x0200, wIndex=0, data_or_wLength=data)
+ except usb.core.USBError as e:
+ logger.exception("Could not write to driver")
+
+ def getData(self):
+ """
+ Sensor data is contained in the 2nd and 4th byte, with
+ sensor IDs being contained in the 3rd and 5th byte
+ respectively.
+ """
+ rawData = self.getRawData()
+ if rawData is not None:
+ sensorData = {rawData[3]: rawData[2], rawData[5]: rawData[4]}
+ else:
+ sensorData = {}
+ return sensorData
+
+ @property
+ def raw_tilt(self):
+ """
+ Returns the raw tilt direction (arbitrary units)
+ """
+ data = self.getData()
+ for num in data:
+ if num in TILTSENSOR:
+ return data[num]
+ return UNAVAILABLE
+
+ @property
+ def tilt(self):
+ """
+ Returns the tilt direction (one of the FLAT, TILT_FORWARD, TILT_LEFT, TILT_RIGHT, TILT_BACK constants)
+ """
+ raw_data = self.raw_tilt
+ if raw_data is UNAVAILABLE:
+ return UNAVAILABLE
+ return process_tilt(raw_data)
+
+ @property
+ def raw_distance(self):
+ """
+ Return the raw evaluated distance from the distance meter (arbitrary units)
+ """
+ data = self.getData()
+ for num in data:
+ if num in DISTANCESENSOR:
+ return data[num]
+ return UNAVAILABLE
+
+ @property
+ def distance(self):
+ """
+ Return the evaluated distance in meters from the distance meter.
+ (Note: this is the ideal distance without any objets on the side, you might have to adapt it depending on your construction)
+ """
+
+ raw_data = self.raw_distance
+ if raw_data is UNAVAILABLE:
+ return UNAVAILABLE
+ return interpolate_distance_data(raw_data)
+
+ @property
+ def motor_a(self):
+ """ Get back the last speed/force set for motor A
+ """
+ return self.valMotorA
+
+ @property
+ def motor_b(self):
+ """ Get back the last speed/force set for motor A
+ """
+ return self.valMotorB
+
+ @motor_a.setter
+ def motor_a(self, value):
+ """ Sets the speed/force of the motor A, expects a value between -100 and 100
+ """
+ if value > 100 or value < -100:
+ raise ValueError("A motor can only be between -100 and 100")
+ self.valMotorA = value
+ self.setMotors()
+
+ @motor_b.setter
+ def motor_b(self, value):
+ """ Sets the speed/force of the motor B, expects a value between -100 and 100
+ """
+ if value > 100 or value < -100:
+ raise ValueError("A motor can only be between -100 and 100")
+ self.valMotorB = value
+ self.setMotors() \ No newline at end of file
diff --git a/plugins/wedo_plugin/wedo/distance.py b/plugins/wedo_plugin/wedo/distance.py
new file mode 100644
index 0000000..0507b2c
--- /dev/null
+++ b/plugins/wedo_plugin/wedo/distance.py
@@ -0,0 +1,54 @@
+from bisect import bisect_left, bisect_right
+
+# data from distance sensor -> real measure in meters under ideal conditions (no side object, perpendicular wall)
+RAW_MEASURES = {
+ 210: 0.46,
+ 208: 0.39,
+ 207: 0.34,
+ 206: 0.32,
+ 205: 0.305,
+ 204: 0.29,
+ 203: 0.27,
+ 202: 0.26,
+ 201: 0.25,
+ 200: 0.245,
+ 199: 0.235,
+ 198: 0.225,
+ 197: 0.22,
+ 196: 0.215,
+ 195: 0.2,
+ 194: 0.195,
+ 193: 0.18,
+ 192: 0.175,
+ 191: 0.17,
+ 180: 0.15,
+ 170: 0.13,
+ 160: 0.125,
+ 150: 0.11,
+ 140: 0.105,
+ 130: 0.1,
+ 120: 0.095,
+ 100: 0.075,
+ 71: 0.065,
+ 70: 0.06,
+ 69: 0.053,
+ 68: 0}
+
+RAW_MEASURES_KEYS = sorted(list(RAW_MEASURES))
+
+
+def interpolate_distance_data(raw):
+ left_index = bisect_left(RAW_MEASURES_KEYS, raw) - 1
+ if left_index < 0:
+ left_index = 0
+ right_index = left_index if left_index == len(RAW_MEASURES_KEYS) - 1 else left_index + 1
+
+ left = RAW_MEASURES_KEYS[left_index]
+ if left > raw:
+ return RAW_MEASURES[RAW_MEASURES_KEYS[left_index]]
+
+ right = RAW_MEASURES_KEYS[right_index]
+ mright = RAW_MEASURES[right]
+ mleft = RAW_MEASURES[left]
+ addup = ((raw - left) / (right - left)) * (mright - mleft) if mright != mleft else 0
+ return mleft + addup \ No newline at end of file
diff --git a/plugins/wedo_plugin/wedo/motor.py b/plugins/wedo_plugin/wedo/motor.py
new file mode 100644
index 0000000..e74c00e
--- /dev/null
+++ b/plugins/wedo_plugin/wedo/motor.py
@@ -0,0 +1,7 @@
+def processMotorValues(value):
+ """Check to make sure motor values are sane."""
+ if 0 < value <= 100:
+ return value + 27
+ elif -100 <= value < 0:
+ return value - 27
+ return 0
diff --git a/plugins/wedo_plugin/wedo/tilt.py b/plugins/wedo_plugin/wedo/tilt.py
new file mode 100644
index 0000000..4f35340
--- /dev/null
+++ b/plugins/wedo_plugin/wedo/tilt.py
@@ -0,0 +1,14 @@
+FLAT, TILT_FORWARD, TILT_LEFT, TILT_RIGHT, TILT_BACK = range(5)
+
+def process_tilt(raw_tilt_value):
+ """Use a series of elif/value-checks to process the tilt
+ sensor data."""
+ if 10 <= raw_tilt_value <= 40:
+ return TILT_BACK
+ elif 60 <= raw_tilt_value <= 90:
+ return TILT_RIGHT
+ elif 170 <= raw_tilt_value <= 190:
+ return TILT_FORWARD
+ elif 220 <= raw_tilt_value <= 240:
+ return TILT_LEFT
+ return FLAT
diff --git a/plugins/wedo_plugin/wedo_plugin.py b/plugins/wedo_plugin/wedo_plugin.py
index 765046d..d0958f5 100644
--- a/plugins/wedo_plugin/wedo_plugin.py
+++ b/plugins/wedo_plugin/wedo_plugin.py
@@ -1,4 +1,9 @@
-#Copyright (c) 2012-13 Tony Forster, Ian Daniher, Walter Bender
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2011, 2012 Ian Daniher
+# Copyright (c) 2012 Tony Forster, Walter Bender, Alan Aguiar
+# Copyright (c) 2013 Alan Aguiar
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -14,286 +19,242 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-from gettext import gettext as _
-
-from plugins.plugin import Plugin
-from WeDoMore import WeDo, scan_for_devices, UNAVAILABLE
-
+import os
+import sys
+sys.path.insert(0, os.path.abspath('./plugins/wedo_plugin'))
from TurtleArt.tapalette import make_palette
-from TurtleArt.taprimitive import (ArgSlot, Primitive)
-from TurtleArt.tatype import TYPE_NUMBER, TYPE_INT
-
+from TurtleArt.tapalette import palette_name_to_index
+from TurtleArt.tapalette import special_block_colors
+from TurtleArt.tapalette import palette_blocks
+from TurtleArt.talogo import logoerror
+from TurtleArt.taprimitive import Primitive, ArgSlot
+from TurtleArt.tatype import TYPE_INT, TYPE_NUMBER
+from plugins.plugin import Plugin
+from wedo import WeDo, scan_for_devices, UNAVAILABLE
+from gettext import gettext as _
-import logging
-_logger = logging.getLogger('turtleart-activity WeDo plugin')
+COLOR_NOTPRESENT = ["#A0A0A0","#808080"]
+COLOR_PRESENT = ["#FF6060", "#A06060"]
+ERROR_NO_NUMBER = _("The parameter must be a integer, not '%s'")
+ERROR_SPEED = _('Motor speed must be an integer between -100 and 100')
+WEDO_FOUND = _('WeDo found %s bricks')
+WEDO_NOT_FOUND = _('WeDo not found')
+INDEX_NOT_FOUND = _('WeDo number %s was not found')
+ERROR = -1
class Wedo_plugin(Plugin):
def __init__(self, parent):
- ''' Scan for WeDo devices '''
+ Plugin.__init__(self)
+ self.tw = parent
self.WeDos = []
- device_list = scan_for_devices()
- for i, dev in enumerate(device_list):
- self.WeDos.append(WeDo(device=dev))
- self.WeDos[-1].number = i
- self._parent = parent
- if len(self.WeDos) == 0:
- self._status = False # no WeDo devices found
- self.active_wedo = None
- else:
- self._status = True
- self.active_wedo = 0
+ self.active_wedo = 0
def setup(self):
- ''' Setup palettes '''
- palette = make_palette('WeDo', colors=["#FF6060", "#A06060"],
- help_string=_('Palette of WeDo blocks'))
+
+ palette = make_palette('wedo', COLOR_NOTPRESENT, _('Palette of WeDo blocks'),
+ translation=_('wedo'))
+
+ palette.add_block('wedorefresh',
+ style='basic-style',
+ label=_('refresh WeDo'),
+ prim_name='wedorefresh',
+ help_string=_('Search for a connected WeDo.'))
+ self.tw.lc.def_prim('wedorefresh', 0,
+ Primitive(self.refresh))
+ special_block_colors['wedorefresh'] = COLOR_PRESENT[:]
palette.add_block('wedoselect',
- style='basic-style-1arg',
- default=1,
- label=_('WeDo'),
- help_string=_('set current WeDo device'),
- prim_name='wedoselect')
- self._parent.lc.def_prim(
- 'wedoselect', 1,
- Primitive(self.prim_wedo_select,
- arg_descs=[ArgSlot(TYPE_NUMBER)]))
+ style='basic-style-1arg',
+ default = 1,
+ label=_('WeDo'),
+ help_string=_('set current WeDo device'),
+ prim_name = 'wedoselect')
+ self.tw.lc.def_prim('wedoselect', 1,
+ Primitive(self.select, arg_descs=[ArgSlot(TYPE_NUMBER)]))
palette.add_block('wedogetcount',
- style='box-style',
- label=_('number of WeDos'),
- help_string=_('number of WeDo devices'),
- prim_name='wedocount')
- self._parent.lc.def_prim(
- 'wedocount', 0,
- Primitive(self.prim_wedo_count, return_type=TYPE_INT))
+ style='box-style',
+ label=_('number of WeDos'),
+ help_string=_('number of WeDo devices'),
+ prim_name = 'wedocount')
+ self.tw.lc.def_prim('wedocount', 0,
+ Primitive(self.count, TYPE_INT))
palette.add_block('tilt',
- style='box-style',
- label=_('tilt'),
- help_string=_('tilt sensor output: (-1 == no tilt, \
+ style='box-style',
+ label=_('tilt'),
+ help_string=_('tilt sensor output: (-1 == no tilt,\
0 == tilt forward, 3 == tilt back, 1 == tilt left, 2 == tilt right)'),
- value_block=True,
- prim_name='wedotilt')
- self._parent.lc.def_prim(
- 'wedotilt', 0,
- Primitive(self.prim_wedo_get_tilt, return_type=TYPE_INT))
+ value_block=True,
+ prim_name = 'wedotilt')
+ self.tw.lc.def_prim('wedotilt', 0,
+ Primitive(self.getTilt, TYPE_INT))
palette.add_block('wedodistance',
- style='box-style',
- label=_('distance'),
- help_string=_('distance sensor output'),
- value_block=True,
- prim_name='wedodistance')
- self._parent.lc.def_prim(
- 'wedodistance', 0,
- Primitive(self.prim_wedo_get_distance, return_type=TYPE_INT))
-
+ style='box-style',
+ label=_('distance'),
+ help_string=_('distance sensor output'),
+ value_block=True,
+ prim_name = 'wedodistance')
+ self.tw.lc.def_prim('wedodistance', 0,
+ Primitive(self.getDistance, TYPE_INT))
+
palette.add_block('wedogetMotorA',
- style='box-style',
- label=_('Motor A'),
- help_string=_('returns the current value of \
-Motor A'),
- value_block=True,
- prim_name='wedogetMotorA')
- self._parent.lc.def_prim(
- 'wedogetMotorA', 0,
- Primitive(self.prim_wedo_get_motor_a, return_type=TYPE_INT))
+ style='box-style',
+ label=_('Motor A'),
+ help_string=_('returns the current value of Motor A'),
+ value_block=True,
+ prim_name = 'wedogetMotorA')
+ self.tw.lc.def_prim('wedogetMotorA', 0,
+ Primitive(self.getMotorA, TYPE_INT))
palette.add_block('wedogetMotorB',
- style='box-style',
- label=_('Motor B'),
- help_string=_('returns the current value of \
-Motor B'),
- value_block=True,
- prim_name='wedogetMotorB')
- self._parent.lc.def_prim(
- 'wedogetMotorB', 0,
- Primitive(self.prim_wedo_get_motor_b, return_type=TYPE_INT))
+ style='box-style',
+ label=_('Motor B'),
+ help_string=_('returns the current value of Motor B'),
+ value_block=True,
+ prim_name = 'wedogetMotorB')
+ self.tw.lc.def_prim('wedogetMotorB', 0,
+ Primitive(self.getMotorB, TYPE_INT))
palette.add_block('wedosetMotorA',
- style='basic-style-1arg',
- label=_('Motor A'),
- default=30,
- prim_name='wedosetMotorA',
- help_string=_('set the value for Motor A'))
- self._parent.lc.def_prim(
- 'wedosetMotorA', 1,
- Primitive(self.prim_wedo_set_motor_a,
- arg_descs=[ArgSlot(TYPE_INT)]))
+ style = 'basic-style-1arg',
+ label = _('Motor A'),
+ default = 30,
+ prim_name = 'wedosetMotorA',
+ help_string = _('set the value for Motor A'))
+ self.tw.lc.def_prim('wedosetMotorA', 1,
+ Primitive(self.setMotorA, arg_descs=[ArgSlot(TYPE_NUMBER)]))
palette.add_block('wedosetMotorB',
- style='basic-style-1arg',
- label=_('Motor B'),
- default=30,
- prim_name='wedosetMotorB',
- help_string=_('set the value for Motor B'))
- self._parent.lc.def_prim(
- 'wedosetMotorB', 1,
- Primitive(self.prim_wedo_set_motor_b,
- arg_descs=[ArgSlot(TYPE_INT)]))
-
- def prim_wedo_select(self, i):
+ style = 'basic-style-1arg',
+ label = _('Motor B'),
+ default = 30,
+ prim_name = 'wedosetMotorB',
+ help_string = _('set the value for Motor B'))
+ self.tw.lc.def_prim('wedosetMotorB', 1,
+ Primitive(self.setMotorB, arg_descs=[ArgSlot(TYPE_NUMBER)]))
+
+ ############################### Turtle signals ############################
+
+ def stop(self):
+ self.stop_all()
+
+ def quit(self):
+ self.stop_all()
+
+ ################################# Primitives ##############################
+
+ def refresh(self):
+ self.wedo_find()
+ self.change_color_blocks()
+ if self.WeDos:
+ n = self.count()
+ self.tw.showlabel('print', WEDO_FOUND % int(n))
+ else:
+ self.tw.showlabel('print', WEDO_NOT_FOUND)
+
+ def select(self, i):
''' Select current device '''
- if self.prim_wedo_count() == 0:
- self.active_wedo = None
- self._parent.showlabel(
- 'status', _('WeDo is unavailable'))
- return
- if type(i) == unicode:
- i = str(i.encode('ascii', 'replace'))
- if type(i) == float or type(i) == str:
- i = int(i)
- if type(i) != int:
- i = 1
- if i < 0:
- i = -i
- if i < 0 or i > self.prim_wedo_count() - 1:
- self._parent.showlabel(
- 'status', _('WeDo %d is unavailable; defaulting to 1') % (i))
- i -= 1 # Userspace counts from 1; internally, we count from 0
- if i > self.prim_wedo_count() - 1:
- i = 0
- self.active_wedo = i
-
- def prim_wedo_count(self):
+ if self.count() == 0:
+ raise logoerror(WEDO_NOT_FOUND)
+ try:
+ t = int(i)
+ t = t - 1
+ except:
+ raise logoerror(ERROR_NO_NUMBER % i)
+ if (t < self.count()) and (t >= 0):
+ self.active_wedo = t
+ else:
+ raise logoerror(INDEX_NOT_FOUND % (t + 1))
+
+ def count(self):
''' How many devices are available? '''
- n = len(self.WeDos)
- for wedo in self.WeDos:
- if wedo.dev is None:
- n -= 1
- return n
+ return len(self.WeDos)
- def wedo_find(self):
- ''' Find an available device '''
- for wedo in self.WeDos:
- if wedo.dev is not None:
- return self.WeDos.index(wedo)
- return None
-
- def prim_wedo_get_tilt(self):
- if self.active_wedo is None:
- self.active_wedo = self.wedo_find()
- if self.active_wedo is None:
- self._parent.showlabel('status', _('WeDo is unavailable'))
- return -1
- tilt = self.WeDos[self.active_wedo].getTilt()
- if tilt == UNAVAILABLE:
- # Should we look for tilt on another WeDo?
- self._parent.showlabel(
- 'status', _('%s is unavailable on WeDo %d') % (
- _('tilt'), self.active_wedo + 1))
- return -1
+ def getTilt(self):
+ if self.WeDos:
+ wedo = self.WeDos[self.active_wedo]
+ if wedo.tilt == UNAVAILABLE:
+ return ERROR
+ return wedo.tilt
else:
- return tilt
-
- def prim_wedo_get_distance(self):
- if self.active_wedo is None:
- self.active_wedo = self.wedo_find()
- if self.active_wedo is None:
- self._parent.showlabel('status', _('WeDo is unavailable'))
- return 0
- distance = self.WeDos[self.active_wedo].getDistance()
- if distance == UNAVAILABLE:
- # Should we look for distance on another WeDo?
- self._parent.showlabel(
- 'status', _('%s is unavailable on WeDo %d') % (
- _('distance'), self.active_wedo + 1))
- return 0
+ return ERROR
+
+ def getDistance(self):
+ if self.WeDos:
+ wedo = self.WeDos[self.active_wedo]
+ if wedo.distance == UNAVAILABLE:
+ return ERROR
+ return wedo.distance
else:
- return distance
-
- def prim_wedo_get_motor_a(self):
- if self.active_wedo is None:
- self.active_wedo = self.wedo_find()
- if self.active_wedo is None:
- self._parent.showlabel('status', _('WeDo is unavailable'))
- return 0
- speed = self.WeDos[self.active_wedo].getMotorA()
- if speed == UNAVAILABLE:
- self._parent.showlabel(
- 'status', _('%s is unavailable on WeDo %d') % (
- _('Motor A'), self.active_wedo + 1))
- return 0
+ return ERROR
+
+ def getMotorA(self):
+ if self.WeDos:
+ wedo = self.WeDos[self.active_wedo]
+ return wedo.motor_a
else:
- return speed
-
- def prim_wedo_get_motor_b(self):
- if self.active_wedo is None:
- self.active_wedo = self.wedo_find()
- if self.active_wedo is None:
- self._parent.showlabel('status', _('WeDo is unavailable'))
- return 0
- speed = self.WeDos[self.active_wedo].getMotorB()
- if speed == UNAVAILABLE:
- self._parent.showlabel(
- 'status', _('%s is unavailable on WeDo %d') % (
- _('Motor B'), self.active_wedo + 1))
- return 0
+ return ERROR
+
+ def getMotorB(self):
+ if self.WeDos:
+ wedo = self.WeDos[self.active_wedo]
+ return wedo.motor_b
else:
- return speed
-
- def prim_wedo_set_motor_a(self, speed):
- if self.active_wedo is None:
- self.active_wedo = self.wedo_find()
- if self.active_wedo is None:
- self._parent.showlabel('status', _('WeDo is unavailable'))
- return
- status = self.WeDos[self.active_wedo].setMotorA(speed)
- if status == UNAVAILABLE:
- self._parent.showlabel(
- 'status', _('%s is unavailable on WeDo %d') % (
- _('Motor A'), self.active_wedo + 1))
-
- def prim_wedo_set_motor_b(self, speed):
- if self.active_wedo is None:
- self.active_wedo = self.wedo_find()
- if self.active_wedo is None:
- self._parent.showlabel('status', _('WeDo is unavailable'))
- return
- status = self.WeDos[self.active_wedo].setMotorB(speed)
- if status == UNAVAILABLE:
- self._parent.showlabel(
- 'status', _('%s is unavailable on WeDo %d') % (
- _('Motor B'), self.active_wedo + 1))
-
- def start(self):
- ''' Each time program starts, scan for devices and reset status '''
+ return ERROR
+
+ def setMotorA(self, speed):
+ try:
+ speed = int(speed)
+ except:
+ raise logoerror(ERROR_SPEED)
+ if speed > 100 or speed < -100:
+ raise logoerror(ERROR_SPEED)
+ if self.WeDos:
+ wedo = self.WeDos[self.active_wedo]
+ wedo.motor_a = speed
+
+ def setMotorB(self, speed):
+ try:
+ speed = int(speed)
+ except:
+ raise logoerror(ERROR_SPEED)
+ if speed > 100 or speed < -100:
+ raise logoerror(ERROR_SPEED)
+ if self.WeDos:
+ wedo = self.WeDos[self.active_wedo]
+ wedo.motor_b = speed
+
+ ############################### Useful functions ##########################
+
+ def wedo_find(self):
for wedo in self.WeDos:
wedo.dev = None
- self.active_wedo = None
- device_list = scan_for_devices()
- if len(device_list) > 0:
- self.status = True
- if self.active_wedo is None:
- self.active_wedo = 0
- else:
- self.status = False
- for i, dev in enumerate(device_list):
- if i < len(self.WeDos):
- self.WeDos[i].dev = dev
- self.WeDos[i].number = i
- self.WeDos[i].init_device() # Reinitial device
- else:
- self.WeDos.append(WeDo(device=dev))
- self.WeDos[i].number = i
-
- def stop(self):
- if self._status:
- for wedo in self.WeDos:
- if wedo.dev is not None:
- wedo.setMotors(0, 0)
+ self.active_wedo = 0
+ self.WeDos = []
+ for dev in scan_for_devices():
+ w = WeDo(dev)
+ self.WeDos.append(w)
- def goto_background(self):
- pass
+ def stop_all(self):
+ for wedo in self.WeDos:
+ wedo.motor_a = 0
+ wedo.motor_b = 0
- def return_to_foreground(self):
- pass
+ def change_color_blocks(self):
+ index = palette_name_to_index('wedo')
+ if (index is not None):
+ wedo_blocks = palette_blocks[index]
+ for block in self.tw.block_list.list:
+ if block.type in ['proto', 'block']:
+ if block.name in wedo_blocks:
+ if (self.WeDos) or (block.name == 'wedorefresh'):
+ special_block_colors[block.name] = COLOR_PRESENT[:]
+ else:
+ special_block_colors[block.name] = COLOR_NOTPRESENT[:]
+ block.refresh()
+ self.tw.regenerate_palette(index)
- def quit(self):
- if self._status:
- for wedo in self.WeDos:
- if wedo.dev is not None:
- wedo.setMotors(0, 0)