diff --git a/.gitignore b/.gitignore index cde185cf2cea3df2ed46b7ded928641b730089a4..3f28e68886fbeeaf60b0f7115422ea2dd356d4e3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ config.json dist/* build/* cache +src/cfclient.egg-info/* # PC client settings conf/* @@ -17,4 +18,4 @@ msvcp90.dll # PyCharm .idea/* -.DS_Store \ No newline at end of file +.DS_Store diff --git a/MANIFEST.in b/MANIFEST.in index 7092f9064c808cbedf0f8c96268bcb7dd60fa913..131e1124d51401f6b04e5b84895fcac4ea8732d0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ recursive-include src/cfclient/configs *.json recursive-include src/cfclient/ui *.ui +recursive-include src/cfclient/resources * diff --git a/README.md b/README.md index c053496e458e50287f8eb932750dbbc195ab00cf..21291c50e9391fb93ded867bff23dfd602a024f6 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,16 @@ where you would like to use the Crazyflie. For more info see our [wiki](http://wiki.bitcraze.se/ "Bitcraze Wiki"). -Installation ------------- - -## Linux - -To install the Crazyflie PC client in Linux, you can run the setup script with: - -```sudo setup_linux.sh``` - -This will install the Crazyflie PC client systemwide, create a udev entry for -the Crazyradio and setup the permissions so that the current user can use the -radio without root permissions after restarting the computer. For further -instructions on how to run from source and [install dependencies](https://github.com/SteveClement/crazyflie-clients-python#dependencies) see below. - -## Windows - -Follow these steps to install the binary distribution on Windows 7/8/10. - - Download the latest release [here](https://github.com/bitcraze/crazyflie-clients-python/releases) (named cfclient-win32-install-*.exe) - - Execute the installer. After the install the application will be added to the start menu. - - Install the Crazyradio drivers by following [these instructions](https://wiki.bitcraze.io/doc:crazyradio:install_windows_zadig) +Note. The project is currently being reorganized, which means that This +documentation might become inacurate. You can track the reorganisation work in +the ticket #227. Running from source ------------------- +The Crazyflie client requires [cflib](https://github.com/bitcraze/crazyflie-lib-python). +Follow the cflib readme to install it. + ## Windows (7/8/10) Install dependencies. With Windows installers (tested using only 32-bit installs on 64-bit OS): @@ -43,7 +29,14 @@ Then install PyUSB, PyZMQ, PySDL2 and PyQtGraph using pip C:\Users\bitcraze>\Python34\python.exe -m pip install pyusb==1.0.0b2 pyzmq pysdl2 pyqtgraph ``` -Finally you run the client using the following command +Install cflib from https://github.com/bitcraze/crazyflie-lib-python. + +Install cfclient to run it from source +``` +C:\Users\bitcraze>\Python34\python.exe -m pip install -e . +``` + +Finally you can run the client using the following command ``` \Python34\python bin\cfclient ``` @@ -90,6 +83,13 @@ they might or might not be affected by this. pip3 install pysdl2 pyusb pyqtgraph ``` +1. Install cflib from https://github.com/bitcraze/crazyflie-lib-python + +1. Install cfclient to run it from source. From the source folder run: + ``` + pip3 install -e . + ``` + 1. You now have all the dependencies needed to run the client. From the source folder, run it with the following command: ``` python bin/cfclient @@ -120,6 +120,12 @@ they might or might not be affected by this. ``` sudo port install py34-pyqtgraph ``` + Install cflib from https://github.com/bitcraze/crazyflie-lib-python + + Install cfclient to run it from source. From the source folder run: + ``` + pip3 install -e . + ``` You can now run the client from the source folder with ``` python bin/cfclient @@ -142,6 +148,13 @@ they might or might not be affected by this. ### Launching the GUI application +Install cflib from https://github.com/bitcraze/crazyflie-lib-python + +Install cfclient to run it from source. From the source folder run (to install +for your user only you can add ```--user``` to the command): +``` +pip3 install -e . +``` To launch the GUI application in the source folder type: ```python bin/cfclient``` @@ -185,33 +198,6 @@ Example commands to install these dependencies: ### Setting udev permissions -The following steps make it possible to use the USB Radio without being root. - -Note: If using a fresh Debian install, you may need to install sudo first -(executing exit command to exit from root shell first): - -``` -su - -apt-get install sudo -``` - -Now, with sudo installed, you should be able to do the following commands - -``` -sudo groupadd plugdev -sudo usermod -a -G plugdev <username> -``` - -Create a file named ```/etc/udev/rules.d/99-crazyradio.rules``` and add the -following: -``` -SUBSYSTEM=="usb", ATTRS{idVendor}=="1915", ATTRS{idProduct}=="7777", MODE="0664", GROUP="plugdev" -``` - -To connect Crazyflie 2.0 via usb, create a file name ```/etc/udev/rules.d/99-crazyflie.rules``` and add the following: -``` -SUBSYSTEM=="usb", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0664", GROUP="plugdev" -``` - -Restart the computer and you are now able to access the USB radio dongle -without being root. +Using Crazyradio on Linux requires that you set udev permissions. See the cflib +[readme](https://github.com/bitcraze/crazyflie-lib-python#setting-udev-permissions) +for more information. diff --git a/bin/cfclient b/bin/cfclient index 294e865043a5651b93ad43d3b7e8ab4020e86b49..4e34e85f64766a421843554ec196a6c3b14d02df 100755 --- a/bin/cfclient +++ b/bin/cfclient @@ -1,116 +1,5 @@ #!/usr/bin/env python3 +from cfclient.gui import main -from __future__ import absolute_import - -import logging -import os -import os.path as _path -from os.path import expanduser -import sys - -APP_NAME = "cfclient" - - -def init_config_path(isInSource): - """ - Configure path for config files. - - If the program is started in the source folder, use a folder "conf" in the - root source directory. Otherwise put it in a directory depending on system - architecture and configuration. If the chosen directory does not exist, - create it. - """ - - if isInSource: - configPath = _path.join(sys.path[0], "..", "conf") - else: - prefix = expanduser("~") - - if sys.platform == "linux2": - if _path.exists(_path.join(prefix, ".local")): - configPath = _path.join(prefix, ".local", APP_NAME) - else: - configPath = _path.join(prefix, "." + APP_NAME) - elif sys.platform == "win32": - configPath = _path.join(os.environ['APPDATA'], APP_NAME) - elif sys.platform == "darwin": - from AppKit import NSSearchPathForDirectoriesInDomains - # FIXME: Copy-pasted from StackOverflow, not tested! - # http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains - # NSApplicationSupportDirectory = 14 - # NSUserDomainMask = 1 - # True for expanding the tilde into a fully qualified path - configPath = _path.join(NSSearchPathForDirectoriesInDomains( - 14, 1, True)[0], APP_NAME) - else: - # Unknown OS, I hope this is good enough - configPath = _path.join(prefix, "." + APP_NAME) - - if not _path.exists(configPath): - os.makedirs(configPath) - - return configPath - - -def init_paths(): - """ - Make the app work in the source tree. - - This puts the root module folder in sys.path[0] and the config folder in - sys.path[1] - """ - - inSource = False - - if hasattr(sys, "frozen"): - if sys.frozen in ('dll', 'console_exe', 'windows_exe'): - sys.path[0] = _path.normpath( - _path.dirname(_path.realpath(sys.executable))) - elif sys.frozen in ('macosx_app',): - # FIXME: Copy-pasted from StackOverflow, not tested! - # py2app: - # Notes on how to find stuff on MAC, by an expert (Bob Ippolito): - # http://mail.python.org/pipermail/pythonmac-sig/2004-November/012121.html - approot = os.environ['RESOURCEPATH'] - else: - prefix = _path.normpath( - _path.join( - _path.dirname(_path.realpath(__file__)), '..')) - - src_lib = _path.join(prefix, 'src') - share_lib = prefix - inSource = False - for location in [src_lib, share_lib] + sys.path: - main_ui = _path.join(location, 'cfclient', 'ui', 'main.ui') - if _path.exists(main_ui): - sys.path.insert(0, location) - if location == src_lib: - inSource = True - break - - if sys.path[0] == "": - raise Exception("Cannot find cfclient install folder!") - - if inSource: - os.environ["PYSDL2_DLL_PATH"] = os.path.abspath(sys.path[0] + "\..") - else: - os.environ["PYSDL2_DLL_PATH"] = sys.path[0] - - sys.path.insert(1, init_config_path(inSource)) - - # Add paths to modules. - sys.path.append(_path.join(src_lib, 'cflib')) - - -if __name__ == '__main__': - if sys.version_info < (3,): - print("The Crazyflie Python client only works with Python 3+, and " - "you are running it using version {}.{}.{}".format( - sys.version_info.major, sys.version_info.minor, - sys.version_info.micro)) - print("Exiting!") - sys.exit(1) - init_paths() - import cfclient - - cfclient.main() +if __name__ == "__main__": + main() diff --git a/bin/cfheadless b/bin/cfheadless index 24bed2b1334381ec14ddcbce190a7e92792e0dac..5d88e4e130a92e5e12c338cb263607d9457f50af 100755 --- a/bin/cfheadless +++ b/bin/cfheadless @@ -1,93 +1,5 @@ #!/usr/bin/env python3 +from cfclient.headless import main -from __future__ import absolute_import - -APP_NAME = "cfclient" - - -def init_config_path(isInSource): - import sys - import os - import os.path as _path - from os.path import expanduser - - if isInSource: - configPath = _path.join(sys.path[0], "..", "conf") - else: - prefix = expanduser("~") - - if sys.platform == "linux2": - if _path.exists(_path.join(prefix, ".local")): - configPath = _path.join(prefix, ".local", APP_NAME) - else: - configPath = _path.join(prefix, "." + APP_NAME) - elif sys.platform == "win32": - configPath = _path.join(os.environ['APPDATA'], APP_NAME) - elif sys.platform == "darwin": - from AppKit import NSSearchPathForDirectoriesInDomains - # FIXME: Copy-pasted from StackOverflow, not tested! - # http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains - # NSApplicationSupportDirectory = 14 - # NSUserDomainMask = 1 - # True for expanding the tilde into a fully qualified path - configPath = path.join( - NSSearchPathForDirectoriesInDomains(14, 1, True)[0], APPNAME) - else: - # Unknown OS, I hope this is good enough - configPath = _path.join(prefix, "." + APP_NAME) - - if not _path.exists(configPath): - os.makedirs(configPath) - - return configPath - - -def init_paths(): - """Make the app work in the source tree. - This puts the root module folder in path[0] and the config folder in - path[1]""" - import sys - import os.path as _path - - sys.path = ["", ""] + sys.path - inSource = False - - if hasattr(sys, "frozen"): - if sys.frozen in ('dll', 'console_exe', 'windows_exe'): - sys.path[0] = _path.normpath( - _path.dirname(_path.realpath(sys.executable))) - elif frozen in ('macosx_app',): - # FIXME: Copy-pasted from StackOverflow, not tested! - # py2app: - # Notes on how to find stuff on MAC, by an expert (Bob Ippolito): - # http://mail.python.org/pipermail/pythonmac-sig/2004-November/012121.html - approot = os.environ['RESOURCEPATH'] - else: - prefix = _path.normpath( - _path.join(_path.dirname(_path.realpath(__file__)), '..')) - - src_lib = _path.join(prefix, 'src') - share_lib = prefix - inSource = False - for location in [src_lib, share_lib] + sys.path: - main_ui = _path.join(location, 'cfclient', 'ui', 'main.ui') - if _path.exists(main_ui): - sys.path[0] = location - if location == src_lib: - inSource = True - break - - if sys.path[0] == "": - raise Exception("Cannot find cfclient install folder!") - - sys.path[1] = init_config_path(inSource) - - # Add paths to modules. - sys.path.append(_path.join(src_lib, 'cflib')) - - -if __name__ == '__main__': - init_paths() - import cfheadless - - cfheadless.main() +if __name__ == "__main__": + main() diff --git a/bin/cfloader b/bin/cfloader index 2b68dd209d71477ab2286f772d475bab32ad8276..0def9b718012121a6d6edbe0ca722414efb0644e 100755 --- a/bin/cfloader +++ b/bin/cfloader @@ -1,204 +1,5 @@ #!/usr/bin/env python3 -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. +from cfloader import main -# 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. - -# Crazy Loader bootloader utility -# Can reset bootload and reset back the bootloader - -import sys -import os -# Fix the path so imports works regardless from where it's run -sys.path[0] = os.path.join(sys.path[0][:-4], "src/cflib") - -import cflib.crtp # noqa -from cflib.bootloader import Bootloader # noqa -from cflib.bootloader.boottypes import BootVersion, TargetTypes, Target # noqa - -# Initialise the CRTP link driver -link = None -cload = None -try: - cflib.crtp.init_drivers() - link = cflib.crtp.get_link_driver("radio://") -except Exception as e: - print("Error: {}".format(str(e))) - if link: - link.close() - sys.exit(-1) - -# Set the default parameters -# Default to Arnaud's copter -cpu_id = "32:00:6e:06:58:37:35:32:60:58:01:43" -clink = None -action = "info" -boot = "cold" - -if len(sys.argv) < 2: - print() - print("==============================") - print(" CrazyLoader Flash Utility") - print("==============================") - print() - print(" Usage:", sys.argv[0], "[CRTP options] <action> [parameters]") - print() - print("The CRTP options are described above") - print() - print("Crazyload option:") - print(" info : Print the info of the bootloader " - "and quit.") - print(" Will let the target in bootloader " - "mode") - print(" reset : Reset the device in firmware mode") - print(" flash <file> [targets] : flash the <img> binary file from " - "the first") - print(" possible page in flash and reset " - "to firmware") - print(" mode.") - if link: - link.close() - sys.exit(0) - -# Analyse the command line parameters -sys.argv = sys.argv[1:] -argv = [] - -warm_uri = None - -i = 0 -while i < len(sys.argv): - if sys.argv[i] == "-i": - i += 1 - cpu_id = sys.argv[i] - elif sys.argv[i] == "--cold-boot" or sys.argv[i] == "-c": - boot = "cold" - elif sys.argv[i] == "--warm-boot" or sys.argv[i] == "-w": - boot = "reset" - i += 1 - clink = sys.argv[i] - else: - argv += [sys.argv[i]] - i += 1 -sys.argv = argv - -# Analyse the command -if len(sys.argv) < 1: - action = "info" -elif sys.argv[0] == "info": - action = "info" -elif sys.argv[0] == "reset": - action = "reset" -elif sys.argv[0] == "flash": - # print len(sys.argv) - if len(sys.argv) < 2: - print("The flash action require a file name.") - link.close() - sys.exit(-1) - action = "flash" - filename = sys.argv[1] - targetnames = {} - for t in sys.argv[2:]: - [target, type] = t.split("-") - if target in targetnames: - targetnames[target] += (type,) - else: - targetnames[target] = (type,) -else: - print("Action", sys.argv[0], "unknown!") - link.close() - sys.exit(-1) - -# Currently there's two different targets available -targets = () - -try: - # Initialise the bootloader lib - bl = Bootloader(clink) - - ######################################### - # Get the connection with the bootloader - ######################################### - # The connection is done by reseting to the bootloader (default) - if boot == "reset": - print("Reset to bootloader mode ..."), - sys.stdout.flush() - if bl.start_bootloader(warm_boot=True): - print(" done!") - else: - print("Failed to warmboot") - bl.close() - sys.exit(-1) - else: # The connection is done by a cold boot ... - print("Restart the Crazyflie you want to bootload in the next"), - print(" 10 seconds ..."), - - sys.stdout.flush() - if bl.start_bootloader(warm_boot=False): - print(" done!") - else: - print("Cannot connect the bootloader!") - bl.close() - sys.exit(-1) - - print("Connected to bootloader on {} (version=0x{:X})".format( - BootVersion.to_ver_string(bl.protocol_version), - bl.protocol_version)) - - if bl.protocol_version == BootVersion.CF2_PROTO_VER: - targets += (bl.get_target(TargetTypes.NRF51),) - targets += (bl.get_target(TargetTypes.STM32),) - - ###################################### - # Doing something (hopefully) useful - ###################################### - - # Print information about the targets - for target in targets: - print(target) - if action == "info": - None # Already done ... - elif action == "reset": - print - print("Reset in firmware mode ...") - bl.reset_to_firmware() - elif action == "flash": - bl.flash(filename, targetnames) - print("Reset in firmware mode ...") - bl.reset_to_firmware() - else: - None -except Exception as e: - import traceback - - traceback.print_exc(file=sys.stdout) - print(e) - -finally: - ######################### - # Closing the connection - ######################### - if bl: - bl.close() +if __name__ == "__main__": + main() diff --git a/bin/cfzmq b/bin/cfzmq index 3881a98a459f8d67e6201327ba94a93135496432..8cffe898d0d982f2232f06bb3f58fca75b795fec 100755 --- a/bin/cfzmq +++ b/bin/cfzmq @@ -1,93 +1,5 @@ #!/usr/bin/env python3 +from cfzmq import main -from __future__ import absolute_import - -APP_NAME = "cfzmq" - - -def init_config_path(isInSource): - import sys - import os - import os.path as _path - from os.path import expanduser - - if isInSource: - configPath = _path.join(sys.path[0], "..", "conf") - else: - prefix = expanduser("~") - - if sys.platform == "linux2": - if _path.exists(_path.join(prefix, ".local")): - configPath = _path.join(prefix, ".local", APP_NAME) - else: - configPath = _path.join(prefix, "." + APP_NAME) - elif sys.platform == "win32": - configPath = _path.join(os.environ['APPDATA'], APP_NAME) - elif sys.platform == "darwin": - from AppKit import NSSearchPathForDirectoriesInDomains - # FIXME: Copy-pasted from StackOverflow, not tested! - # http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains - # NSApplicationSupportDirectory = 14 - # NSUserDomainMask = 1 - # True for expanding the tilde into a fully qualified path - configPath = path.join( - NSSearchPathForDirectoriesInDomains(14, 1, True)[0], APPNAME) - else: - # Unknown OS, I hope this is good enough - configPath = _path.join(prefix, "." + APP_NAME) - - if not _path.exists(configPath): - os.makedirs(configPath) - - return configPath - - -def init_paths(): - """Make the app work in the source tree. - This puts the root module folder in path[0] and the config folder in - path[1]""" - import sys - import os.path as _path - - sys.path = ["", ""] + sys.path - inSource = False - - if hasattr(sys, "frozen"): - if sys.frozen in ('dll', 'console_exe', 'windows_exe'): - sys.path[0] = _path.normpath( - _path.dirname(_path.realpath(sys.executable))) - elif frozen in ('macosx_app',): - # FIXME: Copy-pasted from StackOverflow, not tested! - # py2app: - # Notes on how to find stuff on MAC, by an expert (Bob Ippolito): - # http://mail.python.org/pipermail/pythonmac-sig/2004-November/012121.html - approot = os.environ['RESOURCEPATH'] - else: - prefix = _path.normpath( - _path.join(_path.dirname(_path.realpath(__file__)), '..')) - - src_lib = _path.join(prefix, 'src') - share_lib = prefix - inSource = False - for location in [src_lib, share_lib] + sys.path: - main_ui = _path.join(location, 'cfclient', 'ui', 'main.ui') - if _path.exists(main_ui): - sys.path[0] = location - if location == src_lib: - inSource = True - break - - if sys.path[0] == "": - raise Exception("Cannot find cfclient install folder!") - - sys.path[1] = init_config_path(inSource) - - # Add paths to modules. - sys.path.append(_path.join(src_lib, 'cflib')) - - -if __name__ == '__main__': - init_paths() - import cfzmq - - cfzmq.main() +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py index ccc6b48a70ffcfd2d714d7396f5a8e7a2e5d1105..e7f311853a42173f77c0f094e7765f6aa80b8161 100644 --- a/setup.py +++ b/setup.py @@ -1,123 +1,57 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- -import glob -import os -import sys -from distutils.core import setup +import subprocess from subprocess import PIPE from subprocess import Popen +from setuptools import setup, find_packages # Recover version from Git -try: - process = Popen(["git", "describe", "--tags"], stdout=PIPE) - (output, err) = process.communicate() - exit_code = process.wait() -except OSError: - raise Exception("Cannot run git: Git is required to generate packages!") - -VERSION = output.strip().decode("UTF-8") - -toplevel_data_files = ['README.md', 'LICENSE.txt'] - -# Platform specific settings -if sys.platform.startswith('win32'): +def get_version(): try: - import py2exe - except ImportError: - print("Warning: py2exe not usable") + process = Popen(["git", "describe", "--tags"], stdout=PIPE) + (output, err) = process.communicate() + process.wait() + except OSError: + raise Exception("Cannot run git: " + + "Git is required to generate packages!") - setup_args = dict( - console=[{ - "script": 'bin/cfclient', - "icon_resources": [(1, "bitcraze.ico")] - }], - options={"py2exe": { - "includes": [ - "sip", "PyQt4", - "cfclient.ui.widgets", - "cflib.bootloader.cloader", - "cfclient.ui.toolboxes.*", - "cfclient.ui.*", - "cfclient.ui.tabs.*", - "cfclient.ui.widgets.*", - "cfclient.ui.dialogs.*", - "cfclient.utils.input.inputreaders.*", - "cfclient.utils.input.inputinterfaces.*", - 'zmq.backend.cython'], - "excludes": [ - "AppKit", - 'zmq.libzmq'], - 'dll_excludes': [ - 'libzmq.pyd'], - "skip_archive": True}}) + version = output.strip().decode("UTF-8") - toplevel_data_files.append('SDL2.dll') -else: - setup_args = dict( - scripts=['bin/cfclient', 'bin/cfheadless']) - -# Initial parameters -setup_args = dict(name='cfclient', - description='Bitcraze Cazyflie nano quadcopter client', - version=VERSION, - author='Bitcraze team', - author_email='contact@bitcraze.se', - url='http://www.bitcraze.se', - package_dir={'': 'lib'}, - packages=['cfclient', 'cfclient.ui', 'cfclient.ui.tabs', - 'cfclient.ui.toolboxes', 'cfclient.ui.widgets', - 'cfclient.utils', 'cfclient.ui.dialogs', 'cflib', - 'cflib.bootloader', 'cflib.crazyflie', - 'cflib.drivers', - 'cflib.utils', 'cflib.crtp', - 'cfclient.utils.input', - 'cfclient.utils.input.inputinterfaces', - 'cfclient.utils.input.mux', - 'cfclient.utils.input.inputreaders'], - data_files=[('', toplevel_data_files), - ('cfclient/ui', - glob.glob('src/cfclient/ui/*.ui')), - ('cfclient/ui/tabs', - glob.glob('src/cfclient/ui/tabs/*.ui')), - ('cfclient/ui/widgets', - glob.glob('src/cfclient/ui/widgets/*.ui')), - ('cfclient/ui/toolboxes', - glob.glob('src/cfclient/ui/toolboxes/*.ui')), - ('cfclient/ui/dialogs', - glob.glob('src/cfclient/ui/dialogs/*.ui')), - ('cfclient/configs', - glob.glob('src/cfclient/configs/*.json')), - ('cflib/cache', - glob.glob('src/cflib/cache/*.json')), - ('cfclient/configs/input', - glob.glob('src/cfclient/configs/input/*.json')), - ('cfclient/configs/log', - glob.glob('src/cfclient/configs/log/*.json')), - ('cfclient', - glob.glob('src/cfclient/*.png'))], - **setup_args) + if subprocess.call(["git", "diff-index", "--quiet", "HEAD"]) != 0: + version += "_modified" + return version -# Fetch values from package.xml when using catkin -if os.getenv('CATKIN_TEST_RESULTS_DIR'): - from catkin_pkg.python_setup import generate_distutils_setup - # Delete keys which should not match catkin packaged variant - for k in ('version', 'url'): - setup_args.pop(k, None) - setup_args = generate_distutils_setup(**setup_args) +VERSION = get_version() - -# Write a temp file to pass verision into script -version_file = os.path.join(os.path.dirname(__file__), - "lib", "cfclient", "version.py") -try: - with open(version_file, "w") as versionpy: - versionpy.write("VERSION='{}'".format(VERSION)) -except: - print("Warning: Version file cannot be written.") - -setup(**setup_args) - -if (os.path.isfile(version_file)): - os.remove(version_file) +# Initial parameters +setup( + name='cfclient', + description='Bitcraze Cazyflie quadcopter client', + version=VERSION, + author='Bitcraze team', + author_email='contact@bitcraze.se', + url='http://www.bitcraze.io', + + classifiers=[ + 'License :: OSI Approved :: GPLv2 License', + + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + ], + + keywords='quadcopter crazyflie', + + package_dir={'': 'src'}, + packages=['cfclient', 'cfzmq', 'cfloader'], + + entry_points={ + 'console_scripts': [ + 'cfclient=cfclient.gui:main', + 'cfheadless=cfclient.headless:main', + 'cfloader=cfloader:main', + 'cfzmq=cfzmq:main' + ], + } +) diff --git a/setup_linux.sh b/setup_linux.sh deleted file mode 100755 index 648265d533075a717f332a153f8dbf214a9a1bfa..0000000000000000000000000000000000000000 --- a/setup_linux.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# Install the Crazyflie PC client and set up permissions so that it can be used -# by the current user immediately -# Caution! This installs the Crazyflie PC client as root to your Python -# site-packages directory. If you wish to install it as a normal user, use the -# instructions on the Wiki at -# http://wiki.bitcraze.se/projects:crazyflie:pc_utils:install -# After installation, the Crazyflie PC client can be started with `cfclient`. -# @author Daniel Lee 2013 - -# Allow user to use USB radio without root permission -groupadd plugdev -usermod -a -G plugdev $USER -echo SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"1915\", ATTRS{idProduct}==\"7777\", \ -MODE=\"0664\", GROUP=\"plugdev\" > /etc/udev/rules.d/99-crazyradio.rules -echo SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"0483\", ATTRS{idProduct}==\"5740\", \ -MODE=\"0664\", GROUP=\"plugdev\" > /etc/udev/rules.d/99-crazyflie.rules - -# Install Crazyflie PC client -python3 setup.py install - diff --git a/src/cfclient/__init__.py b/src/cfclient/__init__.py index a9b1b5711ab4853df14e42efd489721c75e37dba..d0782e593c116ad6f0bc224750e3a9ac1eeadef4 100644 --- a/src/cfclient/__init__.py +++ b/src/cfclient/__init__.py @@ -24,24 +24,50 @@ # this program; if not, write to the Free Software Foundation, Inc., 51 # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from .cfclient import main +import os +import pkg_resources + + +def _init_config_path(): + import sys + import os + import os.path as _path + from os.path import expanduser + + prefix = expanduser("~") + + if sys.platform == "linux2": + if _path.exists(_path.join(prefix, ".local")): + configPath = _path.join(prefix, ".local", __name__) + else: + configPath = _path.join(prefix, "." + __name__) + elif sys.platform == "win32": + configPath = _path.join(os.environ['APPDATA'], __name__) + elif sys.platform == "darwin": + from AppKit import NSSearchPathForDirectoriesInDomains + # FIXME: Copy-pasted from StackOverflow, not tested! + # http://developer.apple.com/DOCUMENTATION/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/Reference/reference.html#//apple_ref/c/func/NSSearchPathForDirectoriesInDomains # noqa + # NSApplicationSupportDirectory = 14 + # NSUserDomainMask = 1 + # True for expanding the tilde into a fully qualified path + configPath = _path.join( + NSSearchPathForDirectoriesInDomains(14, 1, True)[0], __name__) + else: + # Unknown OS, I hope this is good enough + configPath = _path.join(prefix, "." + __name__) + + if not _path.exists(configPath): + os.makedirs(configPath) + + return configPath + +# Path used all over the application +# FIXME: module_path is missused: it should not be used to load ressources if +# we want to be able to follow PEP302 and run from zip! +module_path = os.path.dirname(__file__) +config_path = _init_config_path() try: - from .version import VERSION -except: - try: - import subprocess - - VERSION = subprocess.check_output(["git", "describe"]).encode('utf-8') - except: - VERSION = "dev" - - try: - import subprocess - - ret = subprocess.call(["git", "diff", "--quiet", "HEAD"] - ).encode('utf-8') - if ret > 0: - VERSION += "+" - except: - VERSION += "+" + VERSION = pkg_resources.require("cfclient")[0].version +except pkg_resources.DistributionNotFound: + VERSION = "dev" diff --git a/src/cfclient/cfclient.py b/src/cfclient/gui.py similarity index 96% rename from src/cfclient/cfclient.py rename to src/cfclient/gui.py index 901234ab5f03b8df027261756292b68a5bbdad6c..b53a4971e41eb64ff1b3a01779c44272b157a029 100644 --- a/src/cfclient/cfclient.py +++ b/src/cfclient/gui.py @@ -33,6 +33,8 @@ import datetime import logging +import cfclient + __author__ = 'Bitcraze AB' __all__ = [''] @@ -80,7 +82,7 @@ def main(): logger = logging.getLogger(__name__) - logger.debug("Using config path {}".format(sys.path[1])) + logger.debug("Using config path {}".format(cfclient.config_path)) logger.debug("sys.path={}".format(sys.path)) # Try all the imports used in the project here to control what happens.... @@ -135,7 +137,7 @@ def main(): app = QApplication(sys.argv) - app.setWindowIcon(QIcon(sys.path[0] + "/cfclient/icon-256.png")) + app.setWindowIcon(QIcon(cfclient.module_path + "/icon-256.png")) # Make sure the right icon is set in Windows 7+ taskbar if os.name == 'nt': import ctypes @@ -150,3 +152,6 @@ def main(): main_window = MainUI() main_window.show() sys.exit(app.exec_()) + +if __name__ == "__main__": + main() diff --git a/src/cfheadless.py b/src/cfclient/headless.py similarity index 96% rename from src/cfheadless.py rename to src/cfclient/headless.py index 5c14ee8315e13d0fdf2d621b18dd50386f6d648f..683975e4065f9609c4dfdd6aa2f37f258ad9c886 100644 --- a/src/cfheadless.py +++ b/src/cfclient/headless.py @@ -57,8 +57,8 @@ class HeadlessClient(): self._jr = JoystickReader(do_device_discovery=False) - self._cf = Crazyflie(ro_cache=sys.path[0] + "/cflib/cache", - rw_cache=sys.path[1] + "/cache") + self._cf = Crazyflie(ro_cache=None, + rw_cache=cfclient.config_path + "/cache") signal.signal(signal.SIGINT, signal.SIG_DFL) @@ -90,7 +90,7 @@ class HeadlessClient(): for i, dev in enumerate(self._devs): print(" - Controller #{}: {}".format(i, dev)) print("\nAvailable input mapping:") - for map in os.listdir(sys.path[1] + '/input'): + for map in os.listdir(cfclient.config_path + '/input'): print(" - " + map.split(".json")[0]) def connect_crazyflie(self, link_uri): @@ -168,3 +168,6 @@ def main(): headless.connect_crazyflie(link_uri=args.uri) else: print("No input-device connected, exiting!") + +if __name__ == "__main__": + main() diff --git a/src/cfclient/ui/dialogs/about.py b/src/cfclient/ui/dialogs/about.py index 730b880b9326d479b07342db7e86beef1b82817f..6647b6b508d7e3075767306516c810e5c434c746 100644 --- a/src/cfclient/ui/dialogs/about.py +++ b/src/cfclient/ui/dialogs/about.py @@ -26,6 +26,7 @@ """ The about dialog. """ + import sys import cfclient @@ -42,8 +43,8 @@ __author__ = 'Bitcraze AB' __all__ = ['AboutDialog'] (about_widget_class, - about_widget_base_class) = (uic.loadUiType(sys.path[0] + - '/cfclient/ui/dialogs/about.ui')) + about_widget_base_class) = (uic.loadUiType(cfclient.module_path + + '/ui/dialogs/about.ui')) DEBUG_INFO_FORMAT = """ <b>Cfclient</b><br> diff --git a/src/cfclient/ui/dialogs/bootloader.py b/src/cfclient/ui/dialogs/bootloader.py index 41f17fbbea5cff9c583ec7a3e7a4cfa6a4713867..99a2113351616e3b34f706f6488c40b250c1eae3 100644 --- a/src/cfclient/ui/dialogs/bootloader.py +++ b/src/cfclient/ui/dialogs/bootloader.py @@ -32,7 +32,6 @@ read/write the configuration block in the Crazyflie flash. from cflib.bootloader import Bootloader import struct -import sys import time import logging @@ -40,6 +39,7 @@ import logging from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL +import cfclient from cfclient.ui.tab import Tab import cflib.crtp @@ -51,8 +51,8 @@ __all__ = ['BootloaderDialog'] logger = logging.getLogger(__name__) -service_dialog_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/dialogs/bootloader.ui")[0] +service_dialog_class = uic.loadUiType(cfclient.module_path + + "/ui/dialogs/bootloader.ui")[0] class UIState: diff --git a/src/cfclient/ui/dialogs/cf1config.py b/src/cfclient/ui/dialogs/cf1config.py index 752d47845b3b07cc7b29ce4c7934ccb87bd8d9cc..98ad3a660437d67fec0607935f64386f5a9ca324 100644 --- a/src/cfclient/ui/dialogs/cf1config.py +++ b/src/cfclient/ui/dialogs/cf1config.py @@ -31,7 +31,6 @@ read/write the configuration block in the Crazyflie flash. """ import struct -import sys from cflib.bootloader import Bootloader import logging @@ -39,6 +38,7 @@ import logging from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL +import cfclient from cfclient.utils.config import Config from functools import reduce @@ -47,8 +47,8 @@ __all__ = ['Cf1ConfigDialog'] logger = logging.getLogger(__name__) -service_dialog_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/dialogs/cf1config.ui")[0] +service_dialog_class = uic.loadUiType(cfclient.module_path + + "/ui/dialogs/cf1config.ui")[0] class UIState: diff --git a/src/cfclient/ui/dialogs/cf2config.py b/src/cfclient/ui/dialogs/cf2config.py index de278d213d9a32635c928c234b9f9e0e489c6be2..03e1527ea7722246d4f62235ced961c6b20da3bd 100644 --- a/src/cfclient/ui/dialogs/cf2config.py +++ b/src/cfclient/ui/dialogs/cf2config.py @@ -28,25 +28,21 @@ The bootloader dialog is used to update the Crazyflie firmware and to read/write the configuration block in the Crazyflie flash. """ import logging -import sys +import cfclient from cflib.crazyflie.mem import MemoryElement -from PyQt4 import QtCore + from PyQt4 import QtGui from PyQt4 import uic from PyQt4.QtCore import pyqtSignal -from PyQt4.QtCore import pyqtSlot -from PyQt4.QtCore import Qt -from PyQt4.QtCore import QThread -from PyQt4.QtCore import SIGNAL __author__ = 'Bitcraze AB' __all__ = ['CfConfig'] logger = logging.getLogger(__name__) -service_dialog_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/dialogs/cf2config.ui")[0] +service_dialog_class = uic.loadUiType(cfclient.module_path + + "/ui/dialogs/cf2config.ui")[0] class Cf2ConfigDialog(QtGui.QWidget, service_dialog_class): diff --git a/src/cfclient/ui/dialogs/inputconfigdialogue.py b/src/cfclient/ui/dialogs/inputconfigdialogue.py index 504bf96fe01b608fa299db51ca8fea5e28ebd156..58dd95682327def13cab4141789caff1b6903ee1 100644 --- a/src/cfclient/ui/dialogs/inputconfigdialogue.py +++ b/src/cfclient/ui/dialogs/inputconfigdialogue.py @@ -29,8 +29,8 @@ buttons and axis to match controls for the Crazyflie. """ import json import logging -import sys +import cfclient from cfclient.utils.config_manager import ConfigManager from cfclient.utils.input import JoystickReader from cflib.crtp.exceptions import CommunicationException @@ -48,7 +48,7 @@ __all__ = ['InputConfigDialogue'] logger = logging.getLogger(__name__) (inputconfig_widget_class, connect_widget_base_class) = ( - uic.loadUiType(sys.path[0] + '/cfclient/ui/dialogs/inputconfigdialogue.ui') + uic.loadUiType(cfclient.module_path + '/ui/dialogs/inputconfigdialogue.ui') ) diff --git a/src/cfclient/ui/dialogs/logconfigdialogue.py b/src/cfclient/ui/dialogs/logconfigdialogue.py index 51d1354f880ef4ed1e888c341e8d7ff160c0566d..4f90026e3f85ca06ce7f3859bb96fe19b560f6b4 100644 --- a/src/cfclient/ui/dialogs/logconfigdialogue.py +++ b/src/cfclient/ui/dialogs/logconfigdialogue.py @@ -31,10 +31,10 @@ enable logging of data from the Crazyflie. These can then be used in different views in the UI. """ -import sys import os import logging +import cfclient from PyQt4 import Qt, QtCore, QtGui, uic from PyQt4.QtCore import * from PyQt4.QtGui import * @@ -48,7 +48,7 @@ __all__ = ['LogConfigDialogue'] logger = logging.getLogger(__name__) (logconfig_widget_class, connect_widget_base_class) = ( - uic.loadUiType(sys.path[0] + '/cfclient/ui/dialogs/logconfigdialogue.ui')) + uic.loadUiType(cfclient.module_path + '/ui/dialogs/logconfigdialogue.ui')) NAME_FIELD = 0 ID_FIELD = 1 diff --git a/src/cfclient/ui/main.py b/src/cfclient/ui/main.py index cea132070435555587e3f65c7982a2bb3aab45e8..54cbbc0f293148182fbecdf3578d8d57ec7e51d7 100644 --- a/src/cfclient/ui/main.py +++ b/src/cfclient/ui/main.py @@ -29,6 +29,7 @@ The main file for the Crazyflie control application. import logging import sys +import cfclient import cfclient.ui.tabs import cfclient.ui.toolboxes import cflib.crtp @@ -74,8 +75,8 @@ logger = logging.getLogger(__name__) INTERFACE_PROMPT_TEXT = 'Select an interface' (main_window_class, - main_windows_base_class) = (uic.loadUiType(sys.path[0] + - '/cfclient/ui/main.ui')) + main_windows_base_class) = (uic.loadUiType(cfclient.module_path + + '/ui/main.ui')) class MyDockWidget(QtGui.QDockWidget): @@ -168,8 +169,8 @@ class MainUI(QtGui.QMainWindow, main_window_class): ###################################################### - self.cf = Crazyflie(ro_cache=sys.path[0] + "/cflib/cache", - rw_cache=sys.path[1] + "/cache") + self.cf = Crazyflie(ro_cache=None, + rw_cache=cfclient.config_path + "/cache") cflib.crtp.init_drivers(enable_debug_driver=Config() .get("enable_debug_driver")) @@ -789,8 +790,9 @@ class MainUI(QtGui.QMainWindow, main_window_class): self._update_input_device_footer() def _open_config_folder(self): - QDesktopServices.openUrl(QUrl("file:///" + - QDir.toNativeSeparators(sys.path[1]))) + QDesktopServices.openUrl( + QUrl("file:///" + + QDir.toNativeSeparators(cfclient.config_path))) def closeAppRequest(self): self.close() diff --git a/src/cfclient/ui/tabs/ConsoleTab.py b/src/cfclient/ui/tabs/ConsoleTab.py index 29a77bd6990ec74bff96eaad2bca801829bae231..3e173277332afd1dca34ae3d24b32cda22028630 100644 --- a/src/cfclient/ui/tabs/ConsoleTab.py +++ b/src/cfclient/ui/tabs/ConsoleTab.py @@ -30,13 +30,13 @@ The console tab is used as a console for printouts from the Crazyflie. """ import time -import sys import logging from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import pyqtSlot, pyqtSignal +import cfclient from cfclient.ui.tab import Tab __author__ = 'Bitcraze AB' @@ -44,8 +44,8 @@ __all__ = ['ConsoleTab'] logger = logging.getLogger(__name__) -console_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/consoleTab.ui")[0] +console_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/consoleTab.ui")[0] class ConsoleTab(Tab, console_tab_class): diff --git a/src/cfclient/ui/tabs/ExampleTab.py b/src/cfclient/ui/tabs/ExampleTab.py index 16936bbe29018055812293e0a9e570bfc8c4e703..fe40b5aba74d018d030e6e34f6994686acf03ac4 100644 --- a/src/cfclient/ui/tabs/ExampleTab.py +++ b/src/cfclient/ui/tabs/ExampleTab.py @@ -33,12 +33,12 @@ connects the connected/disconnected callbacks. """ import logging -import sys from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import pyqtSlot, pyqtSignal, QThread, Qt from PyQt4.QtGui import QMessageBox +import cfclient from cfclient.ui.tab import Tab from cflib.crazyflie.log import LogConfig, Log @@ -49,8 +49,8 @@ __all__ = ['ExampleTab'] logger = logging.getLogger(__name__) -example_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/exampleTab.ui")[0] +example_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/exampleTab.ui")[0] class ExampleTab(Tab, example_tab_class): diff --git a/src/cfclient/ui/tabs/FlightTab.py b/src/cfclient/ui/tabs/FlightTab.py index b2ec43983bf9d3d2323b5ad96bd2d759a467a67c..22195db5f743d26b60d31ecb3355d425e335f5f5 100644 --- a/src/cfclient/ui/tabs/FlightTab.py +++ b/src/cfclient/ui/tabs/FlightTab.py @@ -29,8 +29,6 @@ The flight control tab shows telemetry data and flight settings. """ -import sys - import logging from time import time @@ -41,6 +39,7 @@ from PyQt4.QtGui import QMessageBox from cflib.crazyflie import Crazyflie +import cfclient from cfclient.ui.widgets.ai import AttitudeIndicator from cfclient.utils.config import Config @@ -55,8 +54,8 @@ __all__ = ['FlightTab'] logger = logging.getLogger(__name__) -flight_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/flightTab.ui")[0] +flight_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/flightTab.ui")[0] MAX_THRUST = 65365.0 diff --git a/src/cfclient/ui/tabs/GpsTab.py b/src/cfclient/ui/tabs/GpsTab.py index f29370228a805b00004714e4e2eec22eaf4371f8..bb69974ec34e21042c4e9d74c25aa088d1ab6bbe 100644 --- a/src/cfclient/ui/tabs/GpsTab.py +++ b/src/cfclient/ui/tabs/GpsTab.py @@ -30,8 +30,8 @@ pre-configured. """ import logging import math -import sys +import cfclient from cfclient.ui.tab import Tab from cflib.crazyflie.log import LogConfig from PyQt4 import QtCore @@ -47,8 +47,8 @@ __all__ = ['GpsTab'] logger = logging.getLogger(__name__) -gps_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/gpsTab.ui")[0] +gps_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/gpsTab.ui")[0] class GpsTab(Tab, gps_tab_class): @@ -81,7 +81,7 @@ class GpsTab(Tab, gps_tab_class): view.page().mainFrame().addToJavaScriptWindowObject("MainWindow", self) view.page().setLinkDelegationPolicy(QtWebKit.QWebPage.DelegateAllLinks) - view.load(QtCore.QUrl(sys.path[0] + "/cfclient/resources/map.html")) + view.load(QtCore.QUrl(cfclient.module_path + "/resources/map.html")) view.loadFinished.connect(self.onLoadFinished) view.linkClicked.connect(QtGui.QDesktopServices.openUrl) @@ -104,7 +104,7 @@ class GpsTab(Tab, gps_tab_class): self._max_speed = 0.0 def onLoadFinished(self): - with open(sys.path[0] + "/cfclient/resources/map.js", 'r') as f: + with open(cfclient.module_path + "/resources/map.js", 'r') as f: frame = self.view.page().mainFrame() frame.evaluateJavaScript(f.read()) diff --git a/src/cfclient/ui/tabs/LEDTab.py b/src/cfclient/ui/tabs/LEDTab.py index 5751a278fbced482294b09c949e9f03cafd380d5..d8430b63f2f419c3d5b1ddb2e52d7e46a7c6aebf 100644 --- a/src/cfclient/ui/tabs/LEDTab.py +++ b/src/cfclient/ui/tabs/LEDTab.py @@ -31,12 +31,12 @@ Basic tab to be able to set (and test) colors in the LED-ring. """ import logging -import sys from PyQt4 import QtGui, uic from PyQt4.QtCore import pyqtSignal from PyQt4.QtGui import QColorDialog +import cfclient from cfclient.ui.tab import Tab from cflib.crazyflie.mem import MemoryElement @@ -46,8 +46,8 @@ __all__ = ['LEDTab'] logger = logging.getLogger(__name__) -led_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/ledTab.ui")[0] +led_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/ledTab.ui")[0] class LEDTab(Tab, led_tab_class): diff --git a/src/cfclient/ui/tabs/LogBlockDebugTab.py b/src/cfclient/ui/tabs/LogBlockDebugTab.py index 17abd4ed81d248f1e3714ed7a7a2f9d2fb49aa24..3c518b101ef95cc9c61e5318bd4f00114da28fe0 100644 --- a/src/cfclient/ui/tabs/LogBlockDebugTab.py +++ b/src/cfclient/ui/tabs/LogBlockDebugTab.py @@ -31,18 +31,18 @@ to edit them. """ import time -import sys from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL +import cfclient from cfclient.ui.tab import Tab __author__ = 'Bitcraze AB' __all__ = ['LogBlockTab'] -logblock_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/logBlockDebugTab.ui")[0] +logblock_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/logBlockDebugTab.ui")[0] class LogBlockDebugTab(Tab, logblock_tab_class): diff --git a/src/cfclient/ui/tabs/LogBlockTab.py b/src/cfclient/ui/tabs/LogBlockTab.py index 7f557cc8cb238f185015a383eceb780ad5abfbf7..536cdca1f9b900233d0b0b42bc977ab64af1a839 100644 --- a/src/cfclient/ui/tabs/LogBlockTab.py +++ b/src/cfclient/ui/tabs/LogBlockTab.py @@ -31,11 +31,10 @@ This tab shows all log blocks that are registered and can be used to start the logging and also to write the logging data to file. """ -import sys - from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import Qt, pyqtSlot, pyqtSignal, QThread, SIGNAL +import cfclient from cfclient.ui.tab import Tab import logging @@ -52,7 +51,7 @@ __author__ = 'Bitcraze AB' __all__ = ['LogBlockTab'] logblock_tab_class = uic.loadUiType( - sys.path[0] + "/cfclient/ui/tabs/logBlockTab.ui")[0] + cfclient.module_path + "/ui/tabs/logBlockTab.ui")[0] logger = logging.getLogger(__name__) diff --git a/src/cfclient/ui/tabs/LogTab.py b/src/cfclient/ui/tabs/LogTab.py index 3fe977ccb030ef8089731736b6a7a46146d74540..9814f230b12573c65c524ac94f4c20be15e5bf85 100644 --- a/src/cfclient/ui/tabs/LogTab.py +++ b/src/cfclient/ui/tabs/LogTab.py @@ -26,9 +26,9 @@ """ Shows the Log TOC of available variables in the Crazyflie. """ -import sys import time +import cfclient from cfclient.ui.tab import Tab from cflib.crazyflie import Crazyflie from PyQt4 import QtCore @@ -43,8 +43,8 @@ from PyQt4.QtCore import SIGNAL __author__ = 'Bitcraze AB' __all__ = ['LogTab'] -param_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/logTab.ui")[0] +param_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/logTab.ui")[0] class LogTab(Tab, param_tab_class): diff --git a/src/cfclient/ui/tabs/ParamTab.py b/src/cfclient/ui/tabs/ParamTab.py index 23e295e7eda48bff8c9ae07f597a61fcecfc03f7..51aa5f43fa1d5b39306b6f15f5f4830ae6081578 100644 --- a/src/cfclient/ui/tabs/ParamTab.py +++ b/src/cfclient/ui/tabs/ParamTab.py @@ -30,7 +30,6 @@ Shows all the parameters available in the Crazyflie and also gives the ability to edit them. """ -import sys import logging from PyQt4 import QtCore, QtGui, uic @@ -40,13 +39,14 @@ from PyQt4.QtGui import QApplication, QStyledItemDelegate, QAbstractItemView, \ QBrush, QColor from PyQt4.QtGui import QSortFilterProxyModel +import cfclient from cfclient.ui.tab import Tab __author__ = 'Bitcraze AB' __all__ = ['ParamTab'] param_tab_class = uic.loadUiType( - sys.path[0] + "/cfclient/ui/tabs/paramTab.ui")[0] + cfclient.module_path + "/ui/tabs/paramTab.ui")[0] logger = logging.getLogger(__name__) diff --git a/src/cfclient/ui/tabs/PlotTab.py b/src/cfclient/ui/tabs/PlotTab.py index 5af6fbd340f61c68ce89cf196cd14a323b97e58b..be9774a13f3bf1a4cf4a1294fb5aee0fa206e3a9 100644 --- a/src/cfclient/ui/tabs/PlotTab.py +++ b/src/cfclient/ui/tabs/PlotTab.py @@ -52,13 +52,15 @@ from PyQt4.QtGui import QApplication from PyQt4.QtGui import QMessageBox from PyQt4.QtGui import QStyledItemDelegate +import cfclient + __author__ = 'Bitcraze AB' __all__ = ['PlotTab'] logger = logging.getLogger(__name__) -plot_tab_class = uic.loadUiType(sys.path[0] + - "/cfclient/ui/tabs/plotTab.ui")[0] +plot_tab_class = uic.loadUiType(cfclient.module_path + + "/ui/tabs/plotTab.ui")[0] class LogConfigModel(QAbstractItemModel): diff --git a/src/cfclient/ui/toolboxes/ConsoleToolbox.py b/src/cfclient/ui/toolboxes/ConsoleToolbox.py index ab2f5c19550bdeea979e14cc5d1020a5e8e2a730..e636576ec8fb6c0a94204f4d2c52b068f88a6663 100644 --- a/src/cfclient/ui/toolboxes/ConsoleToolbox.py +++ b/src/cfclient/ui/toolboxes/ConsoleToolbox.py @@ -26,8 +26,6 @@ """ A detachable toolbox for showing console printouts from the Crazyflie """ -import sys - from PyQt4 import QtCore from PyQt4 import QtGui from PyQt4 import uic @@ -35,11 +33,13 @@ from PyQt4.QtCore import pyqtSignal from PyQt4.QtCore import pyqtSlot from PyQt4.QtCore import Qt +import cfclient + __author__ = 'Bitcraze AB' __all__ = ['ConsoleToolbox'] console_class = uic.loadUiType( - sys.path[0] + "/cfclient/ui/toolboxes/consoleToolbox.ui")[0] + cfclient.module_path + "/ui/toolboxes/consoleToolbox.ui")[0] class ConsoleToolbox(QtGui.QWidget, console_class): diff --git a/src/cfclient/ui/toolboxes/CrtpSharkToolbox.py b/src/cfclient/ui/toolboxes/CrtpSharkToolbox.py index 046f0130286b6bb306a9da4f0a8724d06f2cf6cb..7b1f1051aa0f7f298fecb2fb1222111ae5a7c418 100644 --- a/src/cfclient/ui/toolboxes/CrtpSharkToolbox.py +++ b/src/cfclient/ui/toolboxes/CrtpSharkToolbox.py @@ -28,7 +28,6 @@ Toolbox for showing packets that is sent via the communication link when debugging. """ import os -import sys from time import time from PyQt4 import QtCore @@ -40,11 +39,13 @@ from PyQt4.QtCore import Qt from PyQt4.QtCore import QThread from PyQt4.QtCore import SIGNAL +import cfclient + __author__ = 'Bitcraze AB' __all__ = ['CrtpSharkBoolbox'] param_tab_class = uic.loadUiType( - sys.path[0] + "/cfclient/ui/toolboxes/crtpSharkToolbox.ui")[0] + cfclient.module_path + "/ui/toolboxes/crtpSharkToolbox.ui")[0] class CrtpSharkToolbox(QtGui.QWidget, param_tab_class): @@ -116,7 +117,7 @@ class CrtpSharkToolbox(QtGui.QWidget, param_tab_class): return Qt.RightDockWidgetArea def _save_data(self): - dir = os.path.join(sys.path[1], "logdata") + dir = os.path.join(cfclient.config_path, "logdata") fname = os.path.join(dir, "shark_data.csv") if not os.path.exists(dir): os.makedirs(dir) diff --git a/src/cfclient/ui/toolboxes/DebugDriverToolbox.py b/src/cfclient/ui/toolboxes/DebugDriverToolbox.py index 615c5b3f604404539556bf82a381ccfe211dd52e..335ad65a22b6fd1ee58eddadc7cf7a79fe8aa1f6 100644 --- a/src/cfclient/ui/toolboxes/DebugDriverToolbox.py +++ b/src/cfclient/ui/toolboxes/DebugDriverToolbox.py @@ -28,7 +28,6 @@ Toolbox used to interact with the DebugDriver using a designated port. It's intended to be used for debugging. """ import struct -import sys import time from cflib.crtp.crtpstack import CRTPPacket @@ -42,12 +41,14 @@ from PyQt4.QtCore import Qt from PyQt4.QtCore import QThread from PyQt4.QtCore import SIGNAL +import cfclient + __author__ = 'Bitcraze AB' __all__ = ['DebugDriverToolbox'] debugdriver_tab_class = uic.loadUiType( - sys.path[0] + - "/cfclient/ui/toolboxes/debugDriverToolbox.ui")[0] + cfclient.module_path + + "/ui/toolboxes/debugDriverToolbox.ui")[0] class DebugDriverToolbox(QtGui.QWidget, debugdriver_tab_class): diff --git a/src/cfclient/ui/widgets/plotwidget.py b/src/cfclient/ui/widgets/plotwidget.py index 7c3cadc58e0ba40b5ba02befd57dffec35c3e410..9c0a7688565cb0eb4887dd490c02acc500b4a3cf 100644 --- a/src/cfclient/ui/widgets/plotwidget.py +++ b/src/cfclient/ui/widgets/plotwidget.py @@ -41,8 +41,6 @@ import math import logging -import sys - from PyQt4 import Qt, QtCore, QtGui, uic from PyQt4.QtGui import QButtonGroup from PyQt4.QtCore import * @@ -50,13 +48,15 @@ from PyQt4.QtGui import * from PyQt4.Qt import * from time import time +import cfclient + __author__ = 'Bitcraze AB' __all__ = ['PlotWidget'] logger = logging.getLogger(__name__) (plot_widget_class, connect_widget_base_class) = ( - uic.loadUiType(sys.path[0] + '/cfclient/ui/widgets/plotter.ui')) + uic.loadUiType(cfclient.module_path + '/ui/widgets/plotter.ui')) # Try the imports for PyQtGraph to see if it is installed try: diff --git a/src/cfclient/utils/config.py b/src/cfclient/utils/config.py index cbbe3ef2e460123e72e93a0f902e367a24095fa3..2bfe88af00037e1518de1f70f2ef051d97f13cd8 100644 --- a/src/cfclient/utils/config.py +++ b/src/cfclient/utils/config.py @@ -29,12 +29,12 @@ """ Gives access for reading and writing application configuration parameters """ - -import sys import json import logging from .singleton import Singleton +import cfclient + __author__ = 'Bitcraze AB' __all__ = ['Config'] @@ -46,8 +46,8 @@ class Config(metaclass=Singleton): def __init__(self): """ Initializes the singleton and reads the config files """ - self._dist_config = sys.path[0] + "/cfclient/configs/config.json" - self._config = sys.path[1] + "/config.json" + self._dist_config = cfclient.module_path + "/configs/config.json" + self._config = cfclient.config_path + "/config.json" [self._readonly, self._data] = self._read_distfile() diff --git a/src/cfclient/utils/config_manager.py b/src/cfclient/utils/config_manager.py index f92414054d8836ef3cd3ccb71c738e91945702ee..b535240501653b0805cdb74225fb06a5792a801b 100644 --- a/src/cfclient/utils/config_manager.py +++ b/src/cfclient/utils/config_manager.py @@ -41,6 +41,8 @@ import copy from .singleton import Singleton from cflib.utils.callbacks import Caller +import cfclient + __author__ = 'Bitcraze AB/Allyn Bauer' __all__ = ['ConfigManager'] @@ -50,7 +52,7 @@ logger = logging.getLogger(__name__) class ConfigManager(metaclass=Singleton): """ Singleton class for managing input processing """ conf_needs_reload = Caller() - configs_dir = sys.path[1] + "/input" + configs_dir = cfclient.config_path + "/input" def __init__(self): """Initialize and create empty config list""" diff --git a/src/cfclient/utils/input/__init__.py b/src/cfclient/utils/input/__init__.py index ccb06c3262ddb3786dad9037d25dee95803da7ab..3024c4d9ad8c98428b08c3e7d881d714f5323e3c 100644 --- a/src/cfclient/utils/input/__init__.py +++ b/src/cfclient/utils/input/__init__.py @@ -38,8 +38,6 @@ Windows drivers. The input device's axes and buttons are mapped to software inputs using a configuration file. """ - -import sys import os import re import glob @@ -50,6 +48,7 @@ import shutil from . import inputreaders as readers from . import inputinterfaces as interfaces +import cfclient from cfclient.utils.config import Config from cfclient.utils.config_manager import ConfigManager @@ -144,7 +143,7 @@ class JoystickReader(object): os.makedirs(ConfigManager().configs_dir) for f in glob.glob( - sys.path[0] + "/cfclient/configs/input/[A-Za-z]*.json"): + cfclient.module_path + "/configs/input/[A-Za-z]*.json"): dest = os.path.join(ConfigManager(). configs_dir, os.path.basename(f)) if not os.path.isfile(dest): diff --git a/src/cfclient/utils/logconfigreader.py b/src/cfclient/utils/logconfigreader.py index 36d539266053db1c173bbaf5ac2d06c5676f32e3..c3a8403bc093fd7cd8c9940914c00bad81c2e4bb 100644 --- a/src/cfclient/utils/logconfigreader.py +++ b/src/cfclient/utils/logconfigreader.py @@ -40,15 +40,15 @@ import json import logging import os import shutil -import sys from PyQt4 import QtCore, QtGui, uic from PyQt4.QtCore import pyqtSlot, pyqtSignal +import cfclient from cflib.crazyflie.log import LogVariable, LogConfig __author__ = 'Bitcraze AB' -__all__ = ['LogVariable', 'LogConfigReader', 'LogConfigRemoveThis'] +__all__ = ['LogVariable', 'LogConfigReader'] logger = logging.getLogger(__name__) @@ -59,24 +59,25 @@ class LogConfigReader(): def __init__(self, crazyflie): self.dsList = [] # Check if user config exists, otherwise copy files - if (not os.path.exists(sys.path[1] + "/log")): + if (not os.path.exists(cfclient.config_path + "/log")): logger.info("No user config found, copying dist files") - os.makedirs(sys.path[1] + "/log") + os.makedirs(cfclient.config_path + "/log") for f in glob.glob( - sys.path[0] + "/cfclient/configs/log/[A-Za-z]*.json"): - shutil.copy2(f, sys.path[1] + "/log") + cfclient.module_path + "/configs/log/[A-Za-z]*.json"): + shutil.copy2(f, cfclient.config_path + "/log") self._cf = crazyflie self._cf.connected.add_callback(self._connected) def _read_config_files(self): """Read and parse log configurations""" configsfound = [os.path.basename(f) for f in - glob.glob(sys.path[1] + "/log/[A-Za-z_-]*.json")] + glob.glob(cfclient.config_path + + "/log/[A-Za-z_-]*.json")] new_dsList = [] for conf in configsfound: try: logger.info("Parsing [%s]", conf) - json_data = open(sys.path[1] + "/log/%s" % conf) + json_data = open(cfclient.config_path + "/log/%s" % conf) self.data = json.load(json_data) infoNode = self.data["logconfig"]["logblock"] @@ -115,7 +116,7 @@ class LogConfigReader(): def saveLogConfigFile(self, logconfig): """Save a log configuration to file""" - filename = sys.path[1] + "/log/" + logconfig.name + ".json" + filename = cfclient.config_path + "/log/" + logconfig.name + ".json" logger.info("Saving config for [%s]", filename) # Build tree for JSON diff --git a/src/cfclient/utils/logdatawriter.py b/src/cfclient/utils/logdatawriter.py index 3a7d365d30c2cedf313131c5ffb1cdb022d8bf7b..924934fc8967289981c6af6e9822541743e38614 100644 --- a/src/cfclient/utils/logdatawriter.py +++ b/src/cfclient/utils/logdatawriter.py @@ -55,7 +55,7 @@ class LogWriter(): self._dir = directory self._connected_ts = connected_ts - self._dir = os.path.join(sys.path[1], "logdata", + self._dir = os.path.join(cfclient.config_path, "logdata", connected_ts.strftime("%Y%m%dT%H-%M-%S")) self._file = None self._header_written = False diff --git a/src/cflib/cflib/__init__.py b/src/cflib/cflib/__init__.py deleted file mode 100644 index 536acc1111695aade66f938f03689f8a10d71d4c..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -The Crazyflie Micro Quadcopter library API used to communicate with the -Crazyflie Micro Quadcopter via a communication link. - -The API takes care of scanning, opening and closing the communication link -as well as sending/receiving data from the Crazyflie. - -A link is described using an URI of the following format: - <interface>://<interface defined data>. -See each link for the data that can be included in the URI for that interface. - -The two main uses-cases are scanning for Crazyflies available on a -communication link and opening a communication link to a Crazyflie. - -Example of scanning for available Crazyflies on all communication links: -cflib.crtp.init_drivers() -available = cflib.crtp.scan_interfaces() -for i in available: - print "Found Crazyflie on URI [%s] with comment [%s]" - % (available[0], available[1]) - -Example of connecting to a Crazyflie with know URI (radio dongle 0 and -radio channel 125): -cf = Crazyflie() -cf.open_link("radio://0/125") -... -cf.close_link() -""" diff --git a/src/cflib/cflib/bootloader/__init__.py b/src/cflib/cflib/bootloader/__init__.py deleted file mode 100644 index 4e6bec5ca53592c658692e144dd7035a6e7c9c98..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/bootloader/__init__.py +++ /dev/null @@ -1,388 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Bootloading utilities for the Crazyflie. -""" - -from cflib.utils.callbacks import Caller -from .cloader import Cloader -from .boottypes import BootVersion, TargetTypes, Target -import zipfile -import json -import sys -import time -import logging - -logger = logging.getLogger(__name__) - -__author__ = 'Bitcraze AB' -__all__ = ['Bootloader'] - - -class Bootloader: - """Bootloader utility for the Crazyflie""" - - def __init__(self, clink=None): - """Init the communication class by starting to communicate with the - link given. clink is the link address used after resetting to the - bootloader. - - The device is actually considered in firmware mode. - """ - self.clink = clink - self.in_loader = False - - self.page_size = 0 - self.buffer_pages = 0 - self.flash_pages = 0 - self.start_page = 0 - self.cpuid = "N/A" - self.error_code = 0 - self.protocol_version = 0 - - # Outgoing callbacks for progress - # int - self.progress_cb = None - # msg - self.error_cb = None - # bool - self.in_bootloader_cb = None - # Target - self.dev_info_cb = None - - # self.dev_info_cb.add_callback(self._dev_info) - # self.in_bootloader_cb.add_callback(self._bootloader_info) - - self._boot_plat = None - - self._cload = Cloader(clink, - info_cb=self.dev_info_cb, - in_boot_cb=self.in_bootloader_cb) - - def start_bootloader(self, warm_boot=False): - if warm_boot: - self._cload.open_bootloader_uri(self.clink) - started = self._cload.reset_to_bootloader(TargetTypes.NRF51) - if started: - started = self._cload.check_link_and_get_info() - else: - uri = self._cload.scan_for_bootloader() - - # Workaround for libusb on Windows (open/close too fast) - time.sleep(1) - - if uri: - self._cload.open_bootloader_uri(uri) - started = self._cload.check_link_and_get_info() - else: - started = False - - if started: - self.protocol_version = self._cload.protocol_version - - if (self.protocol_version == BootVersion.CF1_PROTO_VER_0 or - self.protocol_version == BootVersion.CF1_PROTO_VER_1): - # Nothing more to do - pass - elif self.protocol_version == BootVersion.CF2_PROTO_VER: - self._cload.request_info_update(TargetTypes.NRF51) - else: - print("Bootloader protocol 0x{:X} not " - "supported!".self.protocol_version) - - return started - - def get_target(self, target_id): - return self._cload.request_info_update(target_id) - - def read_cf1_config(self): - """Read a flash page from the specified target""" - target = self._cload.targets[0xFF] - config_page = target.flash_pages - 1 - - return self._cload.read_flash(addr=0xFF, page=config_page) - - def write_cf1_config(self, data): - target = self._cload.targets[0xFF] - config_page = target.flash_pages - 1 - - to_flash = {"target": target, "data": data, "type": "CF1 config", - "start_page": config_page} - - self._internal_flash(target=to_flash) - - def flash(self, filename, targets): - for target in targets: - if TargetTypes.from_string(target) not in self._cload.targets: - print("Target {} not found by bootloader".format(target)) - return False - - files_to_flash = () - if zipfile.is_zipfile(filename): - # Read the manifest (don't forget to check so there is one!) - try: - zf = zipfile.ZipFile(filename) - js = zf.read("manifest.json").decode("UTF-8") - j = json.loads(js) - files = j["files"] - platform_id = self._get_platform_id() - files_for_platform = self._filter_platform(files, platform_id) - if len(targets) == 0: - targets = self._extract_targets_from_manifest_files( - files_for_platform) - - zip_targets = self._extract_zip_targets(files_for_platform) - except KeyError as e: - print(e) - print("No manifest.json in {}".format(filename)) - return - - try: - # Match and create targets - for target in targets: - t = targets[target] - for type in t: - file_to_flash = {} - current_target = "{}-{}".format(target, type) - file_to_flash["type"] = type - # Read the data, if this fails we bail - file_to_flash["target"] = self._cload.targets[ - TargetTypes.from_string(target)] - file_to_flash["data"] = zf.read( - zip_targets[target][type]["filename"]) - file_to_flash["start_page"] = file_to_flash[ - "target"].start_page - files_to_flash += (file_to_flash,) - except KeyError as e: - print("Could not find a file for {} in {}".format( - current_target, filename)) - return False - - else: - if len(targets) != 1: - print("Not an archive, must supply one target to flash") - else: - file_to_flash = {} - file_to_flash["type"] = "binary" - f = open(filename, 'rb') - for t in targets: - file_to_flash["target"] = self._cload.targets[ - TargetTypes.from_string(t)] - file_to_flash["type"] = targets[t][0] - file_to_flash["start_page"] = file_to_flash[ - "target"].start_page - file_to_flash["data"] = f.read() - f.close() - files_to_flash += (file_to_flash,) - - if not self.progress_cb: - print("") - - file_counter = 0 - for target in files_to_flash: - file_counter += 1 - self._internal_flash(target, file_counter, len(files_to_flash)) - - def _filter_platform(self, files, platform_id): - result = {} - for file in files: - file_info = files[file] - file_platform = file_info["platform"] - if platform_id == file_platform: - result[file] = file_info - return result - - def _extract_zip_targets(self, files): - zip_targets = {} - for file in files: - file_name = file - file_info = files[file] - file_target = file_info["target"] - file_type = file_info["type"] - if file_target not in zip_targets: - zip_targets[file_target] = {} - zip_targets[file_target][file_type] = { - "filename": file_name} - return zip_targets - - def _extract_targets_from_manifest_files(self, files): - targets = {} - for file in files: - file_info = files[file] - file_target = file_info["target"] - file_type = file_info["type"] - if file_target in targets: - targets[file_target] += (file_type,) - else: - targets[file_target] = (file_type,) - - return targets - - def reset_to_firmware(self): - if self._cload.protocol_version == BootVersion.CF2_PROTO_VER: - self._cload.reset_to_firmware(TargetTypes.NRF51) - else: - self._cload.reset_to_firmware(TargetTypes.STM32) - - def close(self): - if self._cload: - self._cload.close() - - def _internal_flash(self, target, current_file_number=1, total_files=1): - - image = target["data"] - t_data = target["target"] - - start_page = target["start_page"] - - # If used from a UI we need some extra things for reporting progress - factor = (100.0 * t_data.page_size) / len(image) - progress = 0 - - if self.progress_cb: - self.progress_cb( - "({}/{}) Starting...".format(current_file_number, total_files), - int(progress)) - else: - sys.stdout.write( - "Flashing {} of {} to {} ({}): ".format( - current_file_number, total_files, - TargetTypes.to_string(t_data.id), target["type"])) - sys.stdout.flush() - - if len(image) > ((t_data.flash_pages - start_page) * - t_data.page_size): - if self.progress_cb: - self.progress_cb( - "Error: Not enough space to flash the image file.", - int(progress)) - else: - print("Error: Not enough space to flash the image file.") - raise Exception() - - if not self.progress_cb: - logger.info(("%d bytes (%d pages) " % ( - (len(image) - 1), int(len(image) / t_data.page_size) + 1))) - sys.stdout.write(("%d bytes (%d pages) " % ( - (len(image) - 1), int(len(image) / t_data.page_size) + 1))) - sys.stdout.flush() - - # For each page - ctr = 0 # Buffer counter - for i in range(0, int((len(image) - 1) / t_data.page_size) + 1): - # Load the buffer - if ((i + 1) * t_data.page_size) > len(image): - self._cload.upload_buffer( - t_data.addr, ctr, 0, image[i * t_data.page_size:]) - else: - self._cload.upload_buffer( - t_data.addr, ctr, 0, - image[i * t_data.page_size: (i + 1) * t_data.page_size]) - - ctr += 1 - - if self.progress_cb: - progress += factor - self.progress_cb("({}/{}) Uploading buffer to {}...".format( - current_file_number, - total_files, - TargetTypes.to_string(t_data.id)), - - int(progress)) - else: - sys.stdout.write(".") - sys.stdout.flush() - - # Flash when the complete buffers are full - if ctr >= t_data.buffer_pages: - if self.progress_cb: - self.progress_cb("({}/{}) Writing buffer to {}...".format( - current_file_number, - total_files, - TargetTypes.to_string(t_data.id)), - - int(progress)) - else: - sys.stdout.write("%d" % ctr) - sys.stdout.flush() - if not self._cload.write_flash(t_data.addr, 0, - start_page + i - (ctr - 1), - ctr): - if self.progress_cb: - self.progress_cb( - "Error during flash operation (code %d)".format( - self._cload.error_code), - int(progress)) - else: - print("\nError during flash operation (code %d). " - "Maybe wrong radio link?" % - self._cload.error_code) - raise Exception() - - ctr = 0 - - if ctr > 0: - if self.progress_cb: - self.progress_cb("({}/{}) Writing buffer to {}...".format( - current_file_number, - total_files, - TargetTypes.to_string(t_data.id)), - int(progress)) - else: - sys.stdout.write("%d" % ctr) - sys.stdout.flush() - if not self._cload.write_flash( - t_data.addr, 0, - (start_page + (int((len(image) - 1) / t_data.page_size)) - - (ctr - 1)), ctr): - if self.progress_cb: - self.progress_cb( - "Error during flash operation (code %d)".format( - self._cload.error_code), - int(progress)) - else: - print("\nError during flash operation (code %d). Maybe" - " wrong radio link?" % self._cload.error_code) - raise Exception() - - if self.progress_cb: - self.progress_cb( - "({}/{}) Flashing done!".format(current_file_number, - total_files), - int(100)) - else: - print("") - - def _get_platform_id(self): - """Get platform identifier used in the zip manifest for curr copter""" - identifier = "cf1" - if (BootVersion.is_cf2(self.protocol_version)): - identifier = "cf2" - - return identifier diff --git a/src/cflib/cflib/bootloader/boottypes.py b/src/cflib/cflib/bootloader/boottypes.py deleted file mode 100644 index c762bb70e6db38230636137fa7eae2e6fc08ae1c..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/bootloader/boottypes.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Bootloading utilities for the Crazyflie. -""" - -__author__ = 'Bitcraze AB' -__all__ = ['BootVersion', 'TargetTypes', 'Target'] - - -class BootVersion: - CF1_PROTO_VER_0 = 0x00 - CF1_PROTO_VER_1 = 0x01 - CF2_PROTO_VER = 0x10 - - @staticmethod - def to_ver_string(ver): - if (ver == BootVersion.CF1_PROTO_VER_0 or ver == BootVersion. - CF1_PROTO_VER_1): - return "Crazyflie Nano Quadcopter (1.0)" - if ver == BootVersion.CF2_PROTO_VER: - return "Crazyflie 2.0" - return "Unknown" - - @staticmethod - def is_cf2(ver): - return ver == BootVersion.CF2_PROTO_VER - - -class TargetTypes: - STM32 = 0xFF - NRF51 = 0xFE - - @staticmethod - def to_string(target): - if target == TargetTypes.STM32: - return "stm32" - if target == TargetTypes.NRF51: - return "nrf51" - return "Unknown" - - @staticmethod - def from_string(name): - if name == "stm32": - return TargetTypes.STM32 - if name == "nrf51": - return TargetTypes.NRF51 - return 0 - - -class Target: - - def __init__(self, id): - self.id = id - self.protocol_version = 0xFF - self.page_size = 0 - self.buffer_pages = 0 - self.flash_pages = 0 - self.start_page = 0 - self.cpuid = "" - self.data = None - - def __str__(self): - ret = "" - ret += "Target info: {} (0x{:X})\n".format( - TargetTypes.to_string(self.id), self.id) - ret += "Flash pages: %d | Page size: %d | Buffer pages: %d |" \ - " Start page: %d\n" % (self.flash_pages, self.page_size, - self.buffer_pages, self.start_page) - ret += "%d KBytes of flash available for firmware image." % ( - (self.flash_pages - self.start_page) * self.page_size / 1024) - return ret diff --git a/src/cflib/cflib/bootloader/cloader.py b/src/cflib/cflib/bootloader/cloader.py deleted file mode 100644 index 1bffeb15bfd1b741f8960920deb587550ec9ff73..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/bootloader/cloader.py +++ /dev/null @@ -1,423 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Crazyflie radio bootloader for flashing firmware. -""" - -import logging - -import time -import struct -import math -import random - -import cflib.crtp -from cflib.crtp.crtpstack import CRTPPacket, CRTPPort - -from .boottypes import TargetTypes, Target - -__author__ = 'Bitcraze AB' -__all__ = ['Cloader'] - -logger = logging.getLogger(__name__) - - -class Cloader: - """Bootloader utility for the Crazyflie""" - - def __init__(self, link, info_cb=None, in_boot_cb=None): - """Init the communication class by starting to communicate with the - link given. clink is the link address used after resetting to the - bootloader. - - The device is actually considered in firmware mode. - """ - self.link = None - self.uri = link - self.in_loader = False - - self.page_size = 0 - self.buffer_pages = 0 - self.flash_pages = 0 - self.start_page = 0 - self.cpuid = "N/A" - self.error_code = 0 - self.protocol_version = 0xFF - - self._info_cb = info_cb - self._in_boot_cb = in_boot_cb - - self.targets = {} - self.mapping = None - self._available_boot_uri = ("radio://0/110/2M", "radio://0/0/2M") - - def close(self): - """ Close the link """ - if self.link: - self.link.close() - - def scan_for_bootloader(self): - link = cflib.crtp.get_link_driver("radio://0") - ts = time.time() - res = () - while len(res) == 0 and (time.time() - ts) < 10: - res = link.scan_selected(self._available_boot_uri) - - link.close() - - if len(res) > 0: - return res[0] - return None - - def reset_to_bootloader(self, target_id): - retry_counter = 5 - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = (target_id, 0xFF) - self.link.send_packet(pk) - - pk = self.link.receive_packet(1) - - while ((not pk or pk.header != 0xFF or - struct.unpack("<BB", pk.data[0:2]) != (target_id, 0xFF) - ) and retry_counter >= 0): - pk = self.link.receive_packet(1) - retry_counter -= 1 - - if pk: - new_address = (0xb1,) + struct.unpack("<BBBB", pk.data[2:6][::-1]) - - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = (target_id, 0xF0, 0x00) - self.link.send_packet(pk) - - addr = int(struct.pack("B" * 5, *new_address).encode('hex'), 16) - - time.sleep(0.2) - self.link.close() - time.sleep(0.2) - self.link = cflib.crtp.get_link_driver( - "radio://0/0/2M/{}".format(addr)) - - return True - else: - return False - - def reset_to_bootloader1(self, cpu_id): - """ Reset to the bootloader - The parameter cpuid shall correspond to the device to reset. - - Return true if the reset has been done and the contact with the - bootloader is established. - """ - # Send an echo request and wait for the answer - # Mainly aim to bypass a bug of the crazyflie firmware that prevents - # reset before normal CRTP communication - pk = CRTPPacket() - pk.port = CRTPPort.LINKCTRL - pk.data = (1, 2, 3) + cpu_id - self.link.send_packet(pk) - - pk = None - while True: - pk = self.link.receive_packet(2) - if not pk: - return False - - if pk.port == CRTPPort.LINKCTRL: - break - - # Send the reset to bootloader request - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = (0xFF, 0xFE) + cpu_id - self.link.send_packet(pk) - - # Wait to ack the reset ... - pk = None - while True: - pk = self.link.receive_packet(2) - if not pk: - return False - - if pk.port == 0xFF and tuple(pk.data) == (0xFF, 0xFE) + cpu_id: - pk.data = (0xFF, 0xF0) + cpu_id - self.link.send_packet(pk) - break - - time.sleep(0.1) - self.link.close() - self.link = cflib.crtp.get_link_driver(self.clink_address) - # time.sleep(0.1) - - return self._update_info() - - def reset_to_firmware(self, target_id): - """ Reset to firmware - The parameter cpuid shall correspond to the device to reset. - - Return true if the reset has been done - """ - # The fake CPU ID is legacy from the Crazyflie 1.0 - # In order to reset the CPU id had to be sent, but this - # was removed before launching it. But the length check is - # still in the bootloader. So to work around this bug so - # some extra data needs to be sent. - fake_cpu_id = (1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12) - # Send the reset to bootloader request - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = (target_id, 0xFF) + fake_cpu_id - self.link.send_packet(pk) - - # Wait to ack the reset ... - pk = None - while True: - pk = self.link.receive_packet(2) - if not pk: - return False - - if (pk.header == 0xFF and struct.unpack( - "B" * len(pk.data), pk.data)[:2] == (target_id, 0xFF)): - # Difference in CF1 and CF2 (CPU ID) - if target_id == 0xFE: - pk.data = (target_id, 0xF0, 0x01) - else: - pk.data = (target_id, 0xF0) + fake_cpu_id - self.link.send_packet(pk) - break - - time.sleep(0.1) - - def open_bootloader_uri(self, uri=None): - if self.link: - self.link.close() - if uri: - self.link = cflib.crtp.get_link_driver(uri) - else: - self.link = cflib.crtp.get_link_driver(self.clink_address) - - def check_link_and_get_info(self, target_id=0xFF): - """Try to get a connection with the bootloader by requesting info - 5 times. This let roughly 10 seconds to boot the copter ...""" - for _ in range(0, 5): - if self._update_info(target_id): - if self._in_boot_cb: - self._in_boot_cb.call(True, self.targets[ - target_id].protocol_version) - if self._info_cb: - self._info_cb.call(self.targets[target_id]) - if self.protocol_version != 1: - return True - # Set radio link to a random address - addr = [0xbc] + [random.randint(0, 255) for x in range(4)] - return self._set_address(addr) - return False - - def _set_address(self, new_address): - """ Change copter radio address. - This function works only with crazyradio CRTP link. - """ - - logging.debug("Setting bootloader radio address to" - " {}".format(new_address)) - - if len(new_address) != 5: - raise Exception("Radio address should be 5 bytes long") - - self.link.pause() - - for _ in range(10): - logging.debug("Trying to set new radio address") - self.link.cradio.set_address((0xE7,) * 5) - pkdata = (0xFF, 0xFF, 0x11) + tuple(new_address) - self.link.cradio.send_packet(pkdata) - self.link.cradio.set_address(tuple(new_address)) - if self.link.cradio.send_packet((0xff,)).ack: - logging.info("Bootloader set to radio address" - " {}".format(new_address)) - self.link.restart() - return True - - self.link.restart() - return False - - def request_info_update(self, target_id): - if target_id not in self.targets: - self._update_info(target_id) - if self._info_cb: - self._info_cb.call(self.targets[target_id]) - return self.targets[target_id] - - def _update_info(self, target_id): - """ Call the command getInfo and fill up the information received in - the fields of the object - """ - - # Call getInfo ... - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = (target_id, 0x10) - self.link.send_packet(pk) - - # Wait for the answer - pk = self.link.receive_packet(2) - - if (pk and pk.header == 0xFF and struct.unpack("<BB", pk.data[0:2]) == - (target_id, 0x10)): - tab = struct.unpack("BBHHHH", pk.data[0:10]) - cpuid = struct.unpack("B" * 12, pk.data[10:22]) - if target_id not in self.targets: - self.targets[target_id] = Target(target_id) - self.targets[target_id].addr = target_id - if len(pk.data) > 22: - self.targets[target_id].protocol_version = pk.datat[22] - self.protocol_version = pk.datat[22] - self.targets[target_id].page_size = tab[2] - self.targets[target_id].buffer_pages = tab[3] - self.targets[target_id].flash_pages = tab[4] - self.targets[target_id].start_page = tab[5] - self.targets[target_id].cpuid = "%02X" % cpuid[0] - for i in cpuid[1:]: - self.targets[target_id].cpuid += ":%02X" % i - - if (self.protocol_version == 0x10 and - target_id == TargetTypes.STM32): - self._update_mapping(target_id) - - return True - - return False - - def _update_mapping(self, target_id): - pk = CRTPPacket() - pk.set_header(0xff, 0xff) - pk.data = (target_id, 0x12) - self.link.send_packet(pk) - - pk = self.link.receive_packet(2) - - if (pk and pk.header == 0xFF and struct.unpack("<BB", pk.data[0:2]) == - (target_id, 0x12)): - m = pk.datat[2:] - - if (len(m) % 2) != 0: - raise Exception("Malformed flash mapping packet") - - self.mapping = [] - page = 0 - for i in range(int(len(m) / 2)): - for j in range(m[2 * i]): - self.mapping.append(page) - page += m[(2 * i) + 1] - - def upload_buffer(self, target_id, page, address, buff): - """Upload data into a buffer on the Crazyflie""" - # print len(buff) - count = 0 - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = struct.pack("=BBHH", target_id, 0x14, page, address) - - for i in range(0, len(buff)): - pk.data.append(buff[i]) - - count += 1 - - if count > 24: - self.link.send_packet(pk) - count = 0 - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = struct.pack("=BBHH", target_id, 0x14, page, - i + address + 1) - - self.link.send_packet(pk) - - def read_flash(self, addr=0xFF, page=0x00): - """Read back a flash page from the Crazyflie and return it""" - buff = bytearray() - - page_size = self.targets[addr].page_size - - for i in range(0, int(math.ceil(page_size / 25.0))): - pk = None - retry_counter = 5 - while ((not pk or pk.header != 0xFF or - struct.unpack("<BB", pk.data[0:2]) != (addr, 0x1C)) and - retry_counter >= 0): - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = struct.pack("<BBHH", addr, 0x1C, page, (i * 25)) - self.link.send_packet(pk) - - pk = self.link.receive_packet(1) - retry_counter -= 1 - if (retry_counter < 0): - return None - else: - buff += pk.data[6:] - - # For some reason we get one byte extra here... - return buff[0:page_size] - - def write_flash(self, addr, page_buffer, target_page, page_count): - """Initiate flashing of data in the buffer to flash.""" - # print "Write page", flashPage - # print "Writing page [%d] and [%d] forward" % (flashPage, nPage) - pk = None - retry_counter = 5 - # print "Flasing to 0x{:X}".format(addr) - while ((not pk or pk.header != 0xFF or - struct.unpack("<BB", pk.data[0:2]) != (addr, 0x18)) and - retry_counter >= 0): - pk = CRTPPacket() - pk.set_header(0xFF, 0xFF) - pk.data = struct.pack("<BBHHH", addr, 0x18, page_buffer, - target_page, page_count) - self.link.send_packet(pk) - pk = self.link.receive_packet(1) - retry_counter -= 1 - - if retry_counter < 0: - self.error_code = -1 - return False - - self.error_code = pk.data[3] - - return pk.data[2] == 1 - - def decode_cpu_id(self, cpuid): - """Decode the CPU id into a string""" - ret = () - for i in cpuid.split(':'): - ret += (eval("0x" + i),) - - return ret diff --git a/src/cflib/cflib/cache/27A2C4BA.json b/src/cflib/cflib/cache/27A2C4BA.json deleted file mode 100644 index 50c15e65834d9f0795439a4e3196dff1f6ddcb37..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/cache/27A2C4BA.json +++ /dev/null @@ -1,257 +0,0 @@ -{ - "sensorfusion6": { - "ki": { - "ident": 24, - "group": "sensorfusion6", - "name": "ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kp": { - "ident": 23, - "group": "sensorfusion6", - "name": "kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "cpu": { - "flash": { - "ident": 1, - "group": "cpu", - "name": "flash", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "id2": { - "ident": 4, - "group": "cpu", - "name": "id2", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 0 - }, - "id0": { - "ident": 2, - "group": "cpu", - "name": "id0", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 0 - }, - "id1": { - "ident": 3, - "group": "cpu", - "name": "id1", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 0 - } - }, - "version": { - "revision0": { - "ident": 25, - "group": "version", - "name": "revision0", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 0 - }, - "revision1": { - "ident": 26, - "group": "version", - "name": "revision1", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - } - }, - "pid_rate": { - "yaw_kp": { - "ident": 11, - "group": "pid_rate", - "name": "yaw_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kd": { - "ident": 7, - "group": "pid_rate", - "name": "roll_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kp": { - "ident": 8, - "group": "pid_rate", - "name": "pitch_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_ki": { - "ident": 6, - "group": "pid_rate", - "name": "roll_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_ki": { - "ident": 9, - "group": "pid_rate", - "name": "pitch_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kp": { - "ident": 5, - "group": "pid_rate", - "name": "roll_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_kd": { - "ident": 13, - "group": "pid_rate", - "name": "yaw_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_ki": { - "ident": 12, - "group": "pid_rate", - "name": "yaw_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kd": { - "ident": 10, - "group": "pid_rate", - "name": "pitch_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "pid_attitude": { - "yaw_kp": { - "ident": 20, - "group": "pid_attitude", - "name": "yaw_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kd": { - "ident": 16, - "group": "pid_attitude", - "name": "roll_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kp": { - "ident": 17, - "group": "pid_attitude", - "name": "pitch_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_ki": { - "ident": 15, - "group": "pid_attitude", - "name": "roll_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_ki": { - "ident": 18, - "group": "pid_attitude", - "name": "pitch_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kp": { - "ident": 14, - "group": "pid_attitude", - "name": "roll_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_kd": { - "ident": 22, - "group": "pid_attitude", - "name": "yaw_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_ki": { - "ident": 21, - "group": "pid_attitude", - "name": "yaw_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kd": { - "ident": 19, - "group": "pid_attitude", - "name": "pitch_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "imu_acc_lpf": { - "factor": { - "ident": 0, - "group": "imu_acc_lpf", - "name": "factor", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 0 - } - } -} diff --git a/src/cflib/cflib/cache/81204C68.json b/src/cflib/cflib/cache/81204C68.json deleted file mode 100644 index 9394972b2b2dacc541956b54eebf9aedf4183fcb..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/cache/81204C68.json +++ /dev/null @@ -1,317 +0,0 @@ -{ - "acc": { - "y": { - "ident": 26, - "group": "acc", - "name": "y", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "x": { - "ident": 25, - "group": "acc", - "name": "x", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "z": { - "ident": 27, - "group": "acc", - "name": "z", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "zw": { - "ident": 28, - "group": "acc", - "name": "zw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "gyro": { - "y": { - "ident": 4, - "group": "gyro", - "name": "y", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "x": { - "ident": 3, - "group": "gyro", - "name": "x", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "z": { - "ident": 5, - "group": "gyro", - "name": "z", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "sys": { - "canfly": { - "ident": 2, - "group": "sys", - "name": "canfly", - "pytype": "<b", - "__class__": "LogTocElement", - "ctype": "int8_t", - "access": 0 - } - }, - "stabilizer": { - "thrust": { - "ident": 9, - "group": "stabilizer", - "name": "thrust", - "pytype": "<H", - "__class__": "LogTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "yaw": { - "ident": 8, - "group": "stabilizer", - "name": "yaw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "roll": { - "ident": 6, - "group": "stabilizer", - "name": "roll", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pitch": { - "ident": 7, - "group": "stabilizer", - "name": "pitch", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "motor": { - "m4": { - "ident": 21, - "group": "motor", - "name": "m4", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m1": { - "ident": 22, - "group": "motor", - "name": "m1", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m3": { - "ident": 24, - "group": "motor", - "name": "m3", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m2": { - "ident": 23, - "group": "motor", - "name": "m2", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - } - }, - "altHold": { - "vSpeed": { - "ident": 18, - "group": "altHold", - "name": "vSpeed", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "target": { - "ident": 16, - "group": "altHold", - "name": "target", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "err": { - "ident": 15, - "group": "altHold", - "name": "err", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedASL": { - "ident": 19, - "group": "altHold", - "name": "vSpeedASL", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedAcc": { - "ident": 20, - "group": "altHold", - "name": "vSpeedAcc", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "zSpeed": { - "ident": 17, - "group": "altHold", - "name": "zSpeed", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "vpid": { - "i": { - "ident": 31, - "group": "vpid", - "name": "i", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "p": { - "ident": 30, - "group": "vpid", - "name": "p", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pid": { - "ident": 29, - "group": "vpid", - "name": "pid", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "d": { - "ident": 32, - "group": "vpid", - "name": "d", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "baro": { - "aslRaw": { - "ident": 11, - "group": "baro", - "name": "aslRaw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "aslLong": { - "ident": 12, - "group": "baro", - "name": "aslLong", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pressure": { - "ident": 14, - "group": "baro", - "name": "pressure", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "temp": { - "ident": 13, - "group": "baro", - "name": "temp", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "asl": { - "ident": 10, - "group": "baro", - "name": "asl", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "pm": { - "state": { - "ident": 1, - "group": "pm", - "name": "state", - "pytype": "<b", - "__class__": "LogTocElement", - "ctype": "int8_t", - "access": 0 - }, - "vbat": { - "ident": 0, - "group": "pm", - "name": "vbat", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - } -} diff --git a/src/cflib/cflib/cache/892049D2.json b/src/cflib/cflib/cache/892049D2.json deleted file mode 100644 index 3d6fa5f288fb998266b3c0966f7b17701561000a..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/cache/892049D2.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "stabilizer": { - "thrust": { - "ident": 4, - "group": "stabilizer", - "name": "thrust", - "pytype": "<H", - "__class__": "LogTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "yaw": { - "ident": 3, - "group": "stabilizer", - "name": "yaw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "roll": { - "ident": 1, - "group": "stabilizer", - "name": "roll", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pitch": { - "ident": 2, - "group": "stabilizer", - "name": "pitch", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "motor": { - "m4": { - "ident": 5, - "group": "motor", - "name": "m4", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m1": { - "ident": 6, - "group": "motor", - "name": "m1", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m3": { - "ident": 8, - "group": "motor", - "name": "m3", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m2": { - "ident": 7, - "group": "motor", - "name": "m2", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - } - }, - "pm": { - "vbat": { - "ident": 0, - "group": "pm", - "name": "vbat", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - } -} diff --git a/src/cflib/cflib/cache/9E5F2E7D.json b/src/cflib/cflib/cache/9E5F2E7D.json deleted file mode 100644 index a960b53caeab96ec5cfecacab2dc59b5738bfcb9..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/cache/9E5F2E7D.json +++ /dev/null @@ -1,355 +0,0 @@ -{ - "acc": { - "y": { - "ident": 4, - "group": "acc", - "name": "y", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "x": { - "ident": 3, - "group": "acc", - "name": "x", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "z": { - "ident": 5, - "group": "acc", - "name": "z", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "zw": { - "ident": 6, - "group": "acc", - "name": "zw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "mag2": { - "ident": 7, - "group": "acc", - "name": "mag2", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "mag": { - "y": { - "ident": 35, - "group": "mag", - "name": "y", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "x": { - "ident": 34, - "group": "mag", - "name": "x", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "z": { - "ident": 36, - "group": "mag", - "name": "z", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "gyro": { - "y": { - "ident": 28, - "group": "gyro", - "name": "y", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "x": { - "ident": 27, - "group": "gyro", - "name": "x", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "z": { - "ident": 29, - "group": "gyro", - "name": "z", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "sys": { - "canfly": { - "ident": 2, - "group": "sys", - "name": "canfly", - "pytype": "<b", - "__class__": "LogTocElement", - "ctype": "int8_t", - "access": 0 - } - }, - "stabilizer": { - "thrust": { - "ident": 20, - "group": "stabilizer", - "name": "thrust", - "pytype": "<H", - "__class__": "LogTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "yaw": { - "ident": 19, - "group": "stabilizer", - "name": "yaw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "roll": { - "ident": 17, - "group": "stabilizer", - "name": "roll", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pitch": { - "ident": 18, - "group": "stabilizer", - "name": "pitch", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "motor": { - "m4": { - "ident": 13, - "group": "motor", - "name": "m4", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m1": { - "ident": 14, - "group": "motor", - "name": "m1", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m3": { - "ident": 16, - "group": "motor", - "name": "m3", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - }, - "m2": { - "ident": 15, - "group": "motor", - "name": "m2", - "pytype": "<i", - "__class__": "LogTocElement", - "ctype": "int32_t", - "access": 0 - } - }, - "altHold": { - "vSpeed": { - "ident": 24, - "group": "altHold", - "name": "vSpeed", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "target": { - "ident": 22, - "group": "altHold", - "name": "target", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "err": { - "ident": 21, - "group": "altHold", - "name": "err", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedASL": { - "ident": 25, - "group": "altHold", - "name": "vSpeedASL", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedAcc": { - "ident": 26, - "group": "altHold", - "name": "vSpeedAcc", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "zSpeed": { - "ident": 23, - "group": "altHold", - "name": "zSpeed", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "vpid": { - "i": { - "ident": 32, - "group": "vpid", - "name": "i", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "p": { - "ident": 31, - "group": "vpid", - "name": "p", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pid": { - "ident": 30, - "group": "vpid", - "name": "pid", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "d": { - "ident": 33, - "group": "vpid", - "name": "d", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "baro": { - "aslRaw": { - "ident": 9, - "group": "baro", - "name": "aslRaw", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "aslLong": { - "ident": 10, - "group": "baro", - "name": "aslLong", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "pressure": { - "ident": 12, - "group": "baro", - "name": "pressure", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "temp": { - "ident": 11, - "group": "baro", - "name": "temp", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - }, - "asl": { - "ident": 8, - "group": "baro", - "name": "asl", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - }, - "pm": { - "state": { - "ident": 1, - "group": "pm", - "name": "state", - "pytype": "<b", - "__class__": "LogTocElement", - "ctype": "int8_t", - "access": 0 - }, - "vbat": { - "ident": 0, - "group": "pm", - "name": "vbat", - "pytype": "<f", - "__class__": "LogTocElement", - "ctype": "float", - "access": 0 - } - } -} diff --git a/src/cflib/cflib/cache/E20076B8.json b/src/cflib/cflib/cache/E20076B8.json deleted file mode 100644 index 1d743ab0fe9dc58a1f51f2f46fea04e18a064092..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/cache/E20076B8.json +++ /dev/null @@ -1,499 +0,0 @@ -{ - "imu_sensors": { - "HMC5883L": { - "ident": 3, - "group": "imu_sensors", - "name": "HMC5883L", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - }, - "MS5611": { - "ident": 4, - "group": "imu_sensors", - "name": "MS5611", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - } - }, - "sensorfusion6": { - "ki": { - "ident": 30, - "group": "sensorfusion6", - "name": "ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kp": { - "ident": 29, - "group": "sensorfusion6", - "name": "kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "flightmode": { - "althold": { - "ident": 10, - "group": "flightmode", - "name": "althold", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 0 - } - }, - "firmware": { - "revision0": { - "ident": 50, - "group": "firmware", - "name": "revision0", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - }, - "revision1": { - "ident": 51, - "group": "firmware", - "name": "revision1", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 1 - }, - "modified": { - "ident": 52, - "group": "firmware", - "name": "modified", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - } - }, - "cpu": { - "flash": { - "ident": 6, - "group": "cpu", - "name": "flash", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 1 - }, - "id2": { - "ident": 9, - "group": "cpu", - "name": "id2", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - }, - "id0": { - "ident": 7, - "group": "cpu", - "name": "id0", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - }, - "id1": { - "ident": 8, - "group": "cpu", - "name": "id1", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - } - }, - "pid_rate": { - "yaw_kp": { - "ident": 17, - "group": "pid_rate", - "name": "yaw_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kd": { - "ident": 13, - "group": "pid_rate", - "name": "roll_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kp": { - "ident": 14, - "group": "pid_rate", - "name": "pitch_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_ki": { - "ident": 12, - "group": "pid_rate", - "name": "roll_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_ki": { - "ident": 15, - "group": "pid_rate", - "name": "pitch_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kp": { - "ident": 11, - "group": "pid_rate", - "name": "roll_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_kd": { - "ident": 19, - "group": "pid_rate", - "name": "yaw_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_ki": { - "ident": 18, - "group": "pid_rate", - "name": "yaw_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kd": { - "ident": 16, - "group": "pid_rate", - "name": "pitch_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "pid_attitude": { - "yaw_kp": { - "ident": 26, - "group": "pid_attitude", - "name": "yaw_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kd": { - "ident": 22, - "group": "pid_attitude", - "name": "roll_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kp": { - "ident": 23, - "group": "pid_attitude", - "name": "pitch_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_ki": { - "ident": 21, - "group": "pid_attitude", - "name": "roll_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_ki": { - "ident": 24, - "group": "pid_attitude", - "name": "pitch_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kp": { - "ident": 20, - "group": "pid_attitude", - "name": "roll_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_kd": { - "ident": 28, - "group": "pid_attitude", - "name": "yaw_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_ki": { - "ident": 27, - "group": "pid_attitude", - "name": "yaw_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kd": { - "ident": 25, - "group": "pid_attitude", - "name": "pitch_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "altHold": { - "vSpeedAccFac": { - "ident": 43, - "group": "altHold", - "name": "vSpeedAccFac", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedASLDeadband": { - "ident": 44, - "group": "altHold", - "name": "vSpeedASLDeadband", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "errDeadband": { - "ident": 33, - "group": "altHold", - "name": "errDeadband", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "altHoldErrMax": { - "ident": 35, - "group": "altHold", - "name": "altHoldErrMax", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kd": { - "ident": 36, - "group": "altHold", - "name": "kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pidAslFac": { - "ident": 40, - "group": "altHold", - "name": "pidAslFac", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "maxThrust": { - "ident": 48, - "group": "altHold", - "name": "maxThrust", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "ki": { - "ident": 37, - "group": "altHold", - "name": "ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedLimit": { - "ident": 46, - "group": "altHold", - "name": "vSpeedLimit", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "aslAlpha": { - "ident": 31, - "group": "altHold", - "name": "aslAlpha", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kp": { - "ident": 38, - "group": "altHold", - "name": "kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "baseThrust": { - "ident": 47, - "group": "altHold", - "name": "baseThrust", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "vBiasAlpha": { - "ident": 42, - "group": "altHold", - "name": "vBiasAlpha", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pidAlpha": { - "ident": 39, - "group": "altHold", - "name": "pidAlpha", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "altHoldChangeSens": { - "ident": 34, - "group": "altHold", - "name": "altHoldChangeSens", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "minThrust": { - "ident": 49, - "group": "altHold", - "name": "minThrust", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "vAccDeadband": { - "ident": 41, - "group": "altHold", - "name": "vAccDeadband", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "aslAlphaLong": { - "ident": 32, - "group": "altHold", - "name": "aslAlphaLong", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedASLFac": { - "ident": 45, - "group": "altHold", - "name": "vSpeedASLFac", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "imu_tests": { - "HMC5883L": { - "ident": 1, - "group": "imu_tests", - "name": "HMC5883L", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - }, - "MS5611": { - "ident": 2, - "group": "imu_tests", - "name": "MS5611", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - }, - "MPU6050": { - "ident": 0, - "group": "imu_tests", - "name": "MPU6050", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - } - }, - "imu_acc_lpf": { - "factor": { - "ident": 5, - "group": "imu_acc_lpf", - "name": "factor", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 0 - } - } -} diff --git a/src/cflib/cflib/cache/E8BC7DAD.json b/src/cflib/cflib/cache/E8BC7DAD.json deleted file mode 100644 index 1d743ab0fe9dc58a1f51f2f46fea04e18a064092..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/cache/E8BC7DAD.json +++ /dev/null @@ -1,499 +0,0 @@ -{ - "imu_sensors": { - "HMC5883L": { - "ident": 3, - "group": "imu_sensors", - "name": "HMC5883L", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - }, - "MS5611": { - "ident": 4, - "group": "imu_sensors", - "name": "MS5611", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - } - }, - "sensorfusion6": { - "ki": { - "ident": 30, - "group": "sensorfusion6", - "name": "ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kp": { - "ident": 29, - "group": "sensorfusion6", - "name": "kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "flightmode": { - "althold": { - "ident": 10, - "group": "flightmode", - "name": "althold", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 0 - } - }, - "firmware": { - "revision0": { - "ident": 50, - "group": "firmware", - "name": "revision0", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - }, - "revision1": { - "ident": 51, - "group": "firmware", - "name": "revision1", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 1 - }, - "modified": { - "ident": 52, - "group": "firmware", - "name": "modified", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - } - }, - "cpu": { - "flash": { - "ident": 6, - "group": "cpu", - "name": "flash", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 1 - }, - "id2": { - "ident": 9, - "group": "cpu", - "name": "id2", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - }, - "id0": { - "ident": 7, - "group": "cpu", - "name": "id0", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - }, - "id1": { - "ident": 8, - "group": "cpu", - "name": "id1", - "pytype": "<L", - "__class__": "ParamTocElement", - "ctype": "uint32_t", - "access": 1 - } - }, - "pid_rate": { - "yaw_kp": { - "ident": 17, - "group": "pid_rate", - "name": "yaw_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kd": { - "ident": 13, - "group": "pid_rate", - "name": "roll_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kp": { - "ident": 14, - "group": "pid_rate", - "name": "pitch_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_ki": { - "ident": 12, - "group": "pid_rate", - "name": "roll_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_ki": { - "ident": 15, - "group": "pid_rate", - "name": "pitch_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kp": { - "ident": 11, - "group": "pid_rate", - "name": "roll_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_kd": { - "ident": 19, - "group": "pid_rate", - "name": "yaw_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_ki": { - "ident": 18, - "group": "pid_rate", - "name": "yaw_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kd": { - "ident": 16, - "group": "pid_rate", - "name": "pitch_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "pid_attitude": { - "yaw_kp": { - "ident": 26, - "group": "pid_attitude", - "name": "yaw_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kd": { - "ident": 22, - "group": "pid_attitude", - "name": "roll_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kp": { - "ident": 23, - "group": "pid_attitude", - "name": "pitch_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_ki": { - "ident": 21, - "group": "pid_attitude", - "name": "roll_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_ki": { - "ident": 24, - "group": "pid_attitude", - "name": "pitch_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "roll_kp": { - "ident": 20, - "group": "pid_attitude", - "name": "roll_kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_kd": { - "ident": 28, - "group": "pid_attitude", - "name": "yaw_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "yaw_ki": { - "ident": 27, - "group": "pid_attitude", - "name": "yaw_ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pitch_kd": { - "ident": 25, - "group": "pid_attitude", - "name": "pitch_kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "altHold": { - "vSpeedAccFac": { - "ident": 43, - "group": "altHold", - "name": "vSpeedAccFac", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedASLDeadband": { - "ident": 44, - "group": "altHold", - "name": "vSpeedASLDeadband", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "errDeadband": { - "ident": 33, - "group": "altHold", - "name": "errDeadband", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "altHoldErrMax": { - "ident": 35, - "group": "altHold", - "name": "altHoldErrMax", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kd": { - "ident": 36, - "group": "altHold", - "name": "kd", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pidAslFac": { - "ident": 40, - "group": "altHold", - "name": "pidAslFac", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "maxThrust": { - "ident": 48, - "group": "altHold", - "name": "maxThrust", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "ki": { - "ident": 37, - "group": "altHold", - "name": "ki", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedLimit": { - "ident": 46, - "group": "altHold", - "name": "vSpeedLimit", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "aslAlpha": { - "ident": 31, - "group": "altHold", - "name": "aslAlpha", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "kp": { - "ident": 38, - "group": "altHold", - "name": "kp", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "baseThrust": { - "ident": 47, - "group": "altHold", - "name": "baseThrust", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "vBiasAlpha": { - "ident": 42, - "group": "altHold", - "name": "vBiasAlpha", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "pidAlpha": { - "ident": 39, - "group": "altHold", - "name": "pidAlpha", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "altHoldChangeSens": { - "ident": 34, - "group": "altHold", - "name": "altHoldChangeSens", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "minThrust": { - "ident": 49, - "group": "altHold", - "name": "minThrust", - "pytype": "<H", - "__class__": "ParamTocElement", - "ctype": "uint16_t", - "access": 0 - }, - "vAccDeadband": { - "ident": 41, - "group": "altHold", - "name": "vAccDeadband", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "aslAlphaLong": { - "ident": 32, - "group": "altHold", - "name": "aslAlphaLong", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - }, - "vSpeedASLFac": { - "ident": 45, - "group": "altHold", - "name": "vSpeedASLFac", - "pytype": "<f", - "__class__": "ParamTocElement", - "ctype": "float", - "access": 0 - } - }, - "imu_tests": { - "HMC5883L": { - "ident": 1, - "group": "imu_tests", - "name": "HMC5883L", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - }, - "MS5611": { - "ident": 2, - "group": "imu_tests", - "name": "MS5611", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - }, - "MPU6050": { - "ident": 0, - "group": "imu_tests", - "name": "MPU6050", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 1 - } - }, - "imu_acc_lpf": { - "factor": { - "ident": 5, - "group": "imu_acc_lpf", - "name": "factor", - "pytype": "<B", - "__class__": "ParamTocElement", - "ctype": "uint8_t", - "access": 0 - } - } -} diff --git a/src/cflib/cflib/crazyflie/__init__.py b/src/cflib/cflib/crazyflie/__init__.py deleted file mode 100644 index 698fef1eca0b632a9b1f62c83401ab91f4bc1ef6..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/__init__.py +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -The Crazyflie module is used to easily connect/send/receive data -from a Crazyflie. - -Each function in the Crazyflie has a class in the module that can be used -to access that functionality. The same design is then used in the Crazyflie -firmware which makes the mapping 1:1 in most cases. -""" - -import logging - -import time -import datetime -from threading import Thread -from threading import Timer, Lock -from collections import namedtuple - -from .commander import Commander -from .console import Console -from .param import Param -from .log import Log -from .toccache import TocCache -from .mem import Memory -from .platformservice import PlatformService - -import cflib.crtp - -from cflib.utils.callbacks import Caller - -__author__ = 'Bitcraze AB' -__all__ = ['Crazyflie'] - -logger = logging.getLogger(__name__) - - -class State: - """Stat of the connection procedure""" - DISCONNECTED = 0 - INITIALIZED = 1 - CONNECTED = 2 - SETUP_FINISHED = 3 - - -class Crazyflie(): - """The Crazyflie class""" - - # Called on disconnect, no matter the reason - disconnected = Caller() - # Called on unintentional disconnect only - connection_lost = Caller() - # Called when the first packet in a new link is received - link_established = Caller() - # Called when the user requests a connection - connection_requested = Caller() - # Called when the link is established and the TOCs (that are not cached) - # have been downloaded - connected = Caller() - # Called if establishing of the link fails (i.e times out) - connection_failed = Caller() - # Called for every packet received - packet_received = Caller() - # Called for every packet sent - packet_sent = Caller() - # Called when the link driver updates the link quality measurement - link_quality_updated = Caller() - - state = State.DISCONNECTED - - def __init__(self, link=None, ro_cache=None, rw_cache=None): - """ - Create the objects from this module and register callbacks. - - ro_cache -- Path to read-only cache (string) - rw_cache -- Path to read-write cache (string) - """ - self.link = link - self._toc_cache = TocCache(ro_cache=ro_cache, - rw_cache=rw_cache) - - self.incoming = _IncomingPacketHandler(self) - self.incoming.setDaemon(True) - self.incoming.start() - - self.commander = Commander(self) - self.log = Log(self) - self.console = Console(self) - self.param = Param(self) - self.mem = Memory(self) - self.platform = PlatformService(self) - - self.link_uri = "" - - # Used for retry when no reply was sent back - self.packet_received.add_callback(self._check_for_initial_packet_cb) - self.packet_received.add_callback(self._check_for_answers) - - self._answer_patterns = {} - - self._send_lock = Lock() - - self.connected_ts = None - - # Connect callbacks to logger - self.disconnected.add_callback( - lambda uri: logger.info("Callback->Disconnected from [%s]", uri)) - self.disconnected.add_callback(self._disconnected) - self.link_established.add_callback( - lambda uri: logger.info("Callback->Connected to [%s]", uri)) - self.connection_lost.add_callback( - lambda uri, errmsg: logger.info( - "Callback->Connection lost to [%s]: %s", uri, errmsg)) - self.connection_failed.add_callback( - lambda uri, errmsg: logger.info( - "Callback->Connected failed to [%s]: %s", uri, errmsg)) - self.connection_requested.add_callback( - lambda uri: logger.info( - "Callback->Connection initialized[%s]", uri)) - self.connected.add_callback( - lambda uri: logger.info( - "Callback->Connection setup finished [%s]", uri)) - - def _disconnected(self, link_uri): - """ Callback when disconnected.""" - self.connected_ts = None - - def _start_connection_setup(self): - """Start the connection setup by refreshing the TOCs""" - logger.info("We are connected[%s], request connection setup", - self.link_uri) - self.log.refresh_toc(self._log_toc_updated_cb, self._toc_cache) - - def _param_toc_updated_cb(self): - """Called when the param TOC has been fully updated""" - logger.info("Param TOC finished updating") - self.connected_ts = datetime.datetime.now() - self.connected.call(self.link_uri) - # Trigger the update for all the parameters - self.param.request_update_of_all_params() - - def _mems_updated_cb(self): - """Called when the memories have been identified""" - logger.info("Memories finished updating") - self.param.refresh_toc(self._param_toc_updated_cb, self._toc_cache) - - def _log_toc_updated_cb(self): - """Called when the log TOC has been fully updated""" - logger.info("Log TOC finished updating") - self.mem.refresh(self._mems_updated_cb) - - def _link_error_cb(self, errmsg): - """Called from the link driver when there's an error""" - logger.warning("Got link error callback [%s] in state [%s]", - errmsg, self.state) - if (self.link is not None): - self.link.close() - self.link = None - if (self.state == State.INITIALIZED): - self.connection_failed.call(self.link_uri, errmsg) - if (self.state == State.CONNECTED or - self.state == State.SETUP_FINISHED): - self.disconnected.call(self.link_uri) - self.connection_lost.call(self.link_uri, errmsg) - self.state = State.DISCONNECTED - - def _link_quality_cb(self, percentage): - """Called from link driver to report link quality""" - self.link_quality_updated.call(percentage) - - def _check_for_initial_packet_cb(self, data): - """ - Called when first packet arrives from Crazyflie. - - This is used to determine if we are connected to something that is - answering. - """ - self.state = State.CONNECTED - self.link_established.call(self.link_uri) - self.packet_received.remove_callback(self._check_for_initial_packet_cb) - - def open_link(self, link_uri): - """ - Open the communication link to a copter at the given URI and setup the - connection (download log/parameter TOC). - """ - self.connection_requested.call(link_uri) - self.state = State.INITIALIZED - self.link_uri = link_uri - try: - self.link = cflib.crtp.get_link_driver( - link_uri, self._link_quality_cb, self._link_error_cb) - - if not self.link: - message = "No driver found or malformed URI: {}" \ - .format(link_uri) - logger.warning(message) - self.connection_failed.call(link_uri, message) - else: - # Add a callback so we can check that any data is coming - # back from the copter - self.packet_received.add_callback( - self._check_for_initial_packet_cb) - - self._start_connection_setup() - except Exception as ex: # pylint: disable=W0703 - # We want to catch every possible exception here and show - # it in the user interface - import traceback - - logger.error("Couldn't load link driver: %s\n\n%s", - ex, traceback.format_exc()) - exception_text = "Couldn't load link driver: %s\n\n%s" % ( - ex, traceback.format_exc()) - if self.link: - self.link.close() - self.link = None - self.connection_failed.call(link_uri, exception_text) - - def close_link(self): - """Close the communication link.""" - logger.info("Closing link") - if (self.link is not None): - self.commander.send_setpoint(0, 0, 0, 0) - if (self.link is not None): - self.link.close() - self.link = None - self._answer_patterns = {} - self.disconnected.call(self.link_uri) - - def add_port_callback(self, port, cb): - """Add a callback to cb on port""" - self.incoming.add_port_callback(port, cb) - - def remove_port_callback(self, port, cb): - """Remove the callback cb on port""" - self.incoming.remove_port_callback(port, cb) - - def _no_answer_do_retry(self, pk, pattern): - """Resend packets that we have not gotten answers to""" - logger.info("Resending for pattern %s", pattern) - # Set the timer to None before trying to send again - self.send_packet(pk, expected_reply=pattern, resend=True) - - def _check_for_answers(self, pk): - """ - Callback called for every packet received to check if we are - waiting for an answer on this port. If so, then cancel the retry - timer. - """ - longest_match = () - if len(self._answer_patterns) > 0: - data = (pk.header,) + tuple(pk.data) - for p in list(self._answer_patterns.keys()): - logger.debug("Looking for pattern match on %s vs %s", p, data) - if len(p) <= len(data): - if p == data[0:len(p)]: - match = data[0:len(p)] - if len(match) >= len(longest_match): - logger.debug("Found new longest match %s", match) - longest_match = match - if len(longest_match) > 0: - self._answer_patterns[longest_match].cancel() - del self._answer_patterns[longest_match] - - def send_packet(self, pk, expected_reply=(), resend=False, timeout=0.2): - """ - Send a packet through the link interface. - - pk -- Packet to send - expect_answer -- True if a packet from the Crazyflie is expected to - be sent back, otherwise false - - """ - self._send_lock.acquire() - if self.link is not None: - if len(expected_reply) > 0 and not resend and \ - self.link.needs_resending: - pattern = (pk.header,) + expected_reply - logger.debug( - "Sending packet and expecting the %s pattern back", - pattern) - new_timer = Timer(timeout, - lambda: self._no_answer_do_retry(pk, - pattern)) - self._answer_patterns[pattern] = new_timer - new_timer.start() - elif resend: - # Check if we have gotten an answer, if not try again - pattern = expected_reply - if pattern in self._answer_patterns: - logger.debug("We want to resend and the pattern is there") - if self._answer_patterns[pattern]: - new_timer = Timer(timeout, - lambda: - self._no_answer_do_retry( - pk, pattern)) - self._answer_patterns[pattern] = new_timer - new_timer.start() - else: - logger.debug("Resend requested, but no pattern found: %s", - self._answer_patterns) - self.link.send_packet(pk) - self.packet_sent.call(pk) - self._send_lock.release() - - -_CallbackContainer = namedtuple("CallbackConstainer", - "port port_mask channel channel_mask callback") - - -class _IncomingPacketHandler(Thread): - """Handles incoming packets and sends the data to the correct receivers""" - - def __init__(self, cf): - Thread.__init__(self) - self.cf = cf - self.cb = [] - - def add_port_callback(self, port, cb): - """Add a callback for data that comes on a specific port""" - logger.debug("Adding callback on port [%d] to [%s]", port, cb) - self.add_header_callback(cb, port, 0, 0xff, 0x0) - - def remove_port_callback(self, port, cb): - """Remove a callback for data that comes on a specific port""" - logger.debug("Removing callback on port [%d] to [%s]", port, cb) - for port_callback in self.cb: - if port_callback.port == port and port_callback.callback == cb: - self.cb.remove(port_callback) - - def add_header_callback(self, cb, port, channel, port_mask=0xFF, - channel_mask=0xFF): - """ - Add a callback for a specific port/header callback with the - possibility to add a mask for channel and port for multiple - hits for same callback. - """ - self.cb.append(_CallbackContainer(port, port_mask, - channel, channel_mask, cb)) - - def run(self): - while True: - if self.cf.link is None: - time.sleep(1) - continue - pk = self.cf.link.receive_packet(1) - - if pk is None: - continue - - # All-packet callbacks - self.cf.packet_received.call(pk) - - found = False - for cb in (cb for cb in self.cb - if cb.port == (pk.port & cb.port_mask) and - cb.channel == (pk.channel & cb.channel_mask)): - try: - cb.callback(pk) - except Exception: # pylint: disable=W0703 - # Disregard pylint warning since we want to catch all - # exceptions and we can't know what will happen in - # the callbacks. - import traceback - - logger.warning("Exception while doing callback on port" - " [%d]\n\n%s", pk.port, - traceback.format_exc()) - if cb.port != 0xFF: - found = True - - if not found: - pass diff --git a/src/cflib/cflib/crazyflie/commander.py b/src/cflib/cflib/crazyflie/commander.py deleted file mode 100644 index c9477212c5f1b5175fd14e34319e167fcc805128..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/commander.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Used for sending control setpoints to the Crazyflie -""" -import struct - -from cflib.crtp.crtpstack import CRTPPacket -from cflib.crtp.crtpstack import CRTPPort - -__author__ = 'Bitcraze AB' -__all__ = ['Commander'] - - -class Commander(): - """ - Used for sending control setpoints to the Crazyflie - """ - - def __init__(self, crazyflie=None): - """ - Initialize the commander object. By default the commander is in - +-mode (not x-mode). - """ - self._cf = crazyflie - self._x_mode = False - - def set_client_xmode(self, enabled): - """ - Enable/disable the client side X-mode. When enabled this recalculates - the setpoints before sending them to the Crazyflie. - """ - self._x_mode = enabled - - def send_setpoint(self, roll, pitch, yaw, thrust): - """ - Send a new control setpoint for roll/pitch/yaw/thrust to the copter - - The arguments roll/pitch/yaw/trust is the new setpoints that should - be sent to the copter - """ - if thrust > 0xFFFF or thrust < 0: - raise ValueError("Thrust must be between 0 and 0xFFFF") - - if self._x_mode: - roll, pitch = 0.707 * (roll - pitch), 0.707 * (roll + pitch) - - pk = CRTPPacket() - pk.port = CRTPPort.COMMANDER - pk.data = struct.pack('<fffH', roll, -pitch, yaw, thrust) - self._cf.send_packet(pk) diff --git a/src/cflib/cflib/crazyflie/console.py b/src/cflib/cflib/crazyflie/console.py deleted file mode 100644 index 26b9f9fda5e478178b95dffa4308bcbc2a85d669..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/console.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Crazyflie console is used to receive characters printed using printf -from the firmware. -""" - -import struct -from cflib.utils.callbacks import Caller -from cflib.crtp.crtpstack import CRTPPort - -__author__ = 'Bitcraze AB' -__all__ = ['Console'] - - -class Console: - """ - Crazyflie console is used to receive characters printed using printf - from the firmware. - """ - - receivedChar = Caller() - - def __init__(self, crazyflie): - """ - Initialize the console and register it to receive data from the copter. - """ - self.cf = crazyflie - self.cf.add_port_callback(CRTPPort.CONSOLE, self.incoming) - - def incoming(self, packet): - """ - Callback for data received from the copter. - """ - # This might be done prettier ;-) - console_text = packet.data.decode("UTF-8") - - self.receivedChar.call(console_text) diff --git a/src/cflib/cflib/crazyflie/log.py b/src/cflib/cflib/crazyflie/log.py deleted file mode 100644 index 27a8c2e5acc69d3d623b78aedf65c77a31fef892..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/log.py +++ /dev/null @@ -1,557 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Enables logging of variables from the Crazyflie. - -When a Crazyflie is connected it's possible to download a TableOfContent of all -the variables that can be logged. Using this it's possible to add logging -configurations where selected variables are sent to the client at a -specified period. - -Terminology: - Log configuration - A configuration with a period and a number of variables - that are present in the TOC. - Stored as - The size and type of the variable as declared in the - Crazyflie firmware - Fetch as - The size and type that a variable should be fetched as. - This does not have to be the same as the size and type - it's stored as. - -States of a configuration: - Created on host - When a configuration is created the contents is checked - so that all the variables are present in the TOC. If not - then the configuration cannot be created. - Created on CF - When the configuration is deemed valid it is added to the - Crazyflie. At this time the memory constraint is checked - and the status returned. - Started on CF - Any added block that is not started can be started. Once - started the Crazyflie will send back logdata periodically - according to the specified period when it's created. - Stopped on CF - Any started configuration can be stopped. The memory taken - by the configuration on the Crazyflie is NOT freed, the - only effect is that the Crazyflie will stop sending - logdata back to the host. - Deleted on CF - Any block that is added can be deleted. When this is done - the memory taken by the configuration is freed on the - Crazyflie. The configuration will have to be re-added to - be used again. -""" - -import struct -import errno -import sys -from cflib.crtp.crtpstack import CRTPPacket, CRTPPort -from cflib.utils.callbacks import Caller -from .toc import Toc, TocFetcher -import logging - -__author__ = 'Bitcraze AB' -__all__ = ['Log', 'LogTocElement'] - -# Channels used for the logging port -CHAN_TOC = 0 -CHAN_SETTINGS = 1 -CHAN_LOGDATA = 2 - -# Commands used when accessing the Table of Contents -CMD_TOC_ELEMENT = 0 -CMD_TOC_INFO = 1 - -# Commands used when accessing the Log configurations -CMD_CREATE_BLOCK = 0 -CMD_APPEND_BLOCK = 1 -CMD_DELETE_BLOCK = 2 -CMD_START_LOGGING = 3 -CMD_STOP_LOGGING = 4 -CMD_RESET_LOGGING = 5 - -# Possible states when receiving TOC -IDLE = "IDLE" -GET_TOC_INF = "GET_TOC_INFO" -GET_TOC_ELEMENT = "GET_TOC_ELEMENT" - -# The max size of a CRTP packet payload -MAX_LOG_DATA_PACKET_SIZE = 30 - - -logger = logging.getLogger(__name__) - - -class LogVariable(): - """A logging variable""" - - TOC_TYPE = 0 - MEM_TYPE = 1 - - def __init__(self, name="", fetchAs="uint8_t", varType=TOC_TYPE, - storedAs="", address=0): - self.name = name - self.fetch_as = LogTocElement.get_id_from_cstring(fetchAs) - if (len(storedAs) == 0): - self.stored_as = self.fetch_as - else: - self.stored_as = LogTocElement.get_id_from_cstring(storedAs) - self.address = address - self.type = varType - self.stored_as_string = storedAs - self.fetch_as_string = fetchAs - - def is_toc_variable(self): - """ - Return true if the variable should be in the TOC, false if raw memory - variable - """ - return self.type == LogVariable.TOC_TYPE - - def get_storage_and_fetch_byte(self): - """Return what the variable is stored as and fetched as""" - return (self.fetch_as | (self.stored_as << 4)) - - def __str__(self): - return ("LogVariable: name=%s, store=%s, fetch=%s" % - (self.name, LogTocElement.get_cstring_from_id(self.stored_as), - LogTocElement.get_cstring_from_id(self.fetch_as))) - - -class LogConfig(object): - """Representation of one log configuration that enables logging - from the Crazyflie""" - _config_id_counter = 1 - - def __init__(self, name, period_in_ms): - """Initialize the entry""" - self.data_received_cb = Caller() - self.error_cb = Caller() - self.started_cb = Caller() - self.added_cb = Caller() - self.err_no = 0 - - self.id = LogConfig._config_id_counter - LogConfig._config_id_counter = (LogConfig._config_id_counter + 1) % 255 - self.cf = None - self.period = int(period_in_ms / 10) - self.period_in_ms = period_in_ms - self._added = False - self._started = False - self.valid = False - self.variables = [] - self.default_fetch_as = [] - self.name = name - - def add_variable(self, name, fetch_as=None): - """Add a new variable to the configuration. - - name - Complete name of the variable in the form group.name - fetch_as - String representation of the type the variable should be - fetched as (i.e uint8_t, float, FP16, etc) - - If no fetch_as type is supplied, then the stored as type will be used - (i.e the type of the fetched variable is the same as it's stored in the - Crazyflie).""" - if fetch_as: - self.variables.append(LogVariable(name, fetch_as)) - else: - # We cannot determine the default type until we have connected. So - # save the name and we will add these once we are connected. - self.default_fetch_as.append(name) - - def add_memory(self, name, fetch_as, stored_as, address): - """Add a raw memory position to log. - - name - Arbitrary name of the variable - fetch_as - String representation of the type of the data the memory - should be fetch as (i.e uint8_t, float, FP16) - stored_as - String representation of the type the data is stored as - in the Crazyflie - address - The address of the data - """ - self.variables.append(LogVariable(name, fetch_as, LogVariable.MEM_TYPE, - stored_as, address)) - - def _set_added(self, added): - if added != self._added: - self.added_cb.call(self, added) - self._added = added - - def _get_added(self): - return self._added - - def _set_started(self, started): - if started != self._started: - self.started_cb.call(self, started) - self._started = started - - def _get_started(self): - return self._started - - added = property(_get_added, _set_added) - started = property(_get_started, _set_started) - - def create(self): - """Save the log configuration in the Crazyflie""" - pk = CRTPPacket() - pk.set_header(5, CHAN_SETTINGS) - pk.data = (CMD_CREATE_BLOCK, self.id) - for var in self.variables: - if (var.is_toc_variable() is False): # Memory location - logger.debug("Logging to raw memory %d, 0x%04X", - var.get_storage_and_fetch_byte(), var.address) - pk.data.append(struct.pack('<B', - var.get_storage_and_fetch_byte())) - pk.data.append(struct.pack('<I', var.address)) - else: # Item in TOC - logger.debug("Adding %s with id=%d and type=0x%02X", - var.name, - self.cf.log.toc.get_element_id( - var.name), var.get_storage_and_fetch_byte()) - pk.data.append(var.get_storage_and_fetch_byte()) - pk.data.append(self.cf.log.toc.get_element_id(var.name)) - logger.debug("Adding log block id {}".format(self.id)) - self.cf.send_packet(pk, expected_reply=(CMD_CREATE_BLOCK, self.id)) - - def start(self): - """Start the logging for this entry""" - if (self.cf.link is not None): - if (self._added is False): - self.create() - logger.debug("First time block is started, add block") - else: - logger.debug("Block already registered, starting logging" - " for id=%d", self.id) - pk = CRTPPacket() - pk.set_header(5, CHAN_SETTINGS) - pk.data = (CMD_START_LOGGING, self.id, self.period) - self.cf.send_packet(pk, expected_reply=( - CMD_START_LOGGING, self.id)) - - def stop(self): - """Stop the logging for this entry""" - if (self.cf.link is not None): - if (self.id is None): - logger.warning("Stopping block, but no block registered") - else: - logger.debug("Sending stop logging for block id=%d", self.id) - pk = CRTPPacket() - pk.set_header(5, CHAN_SETTINGS) - pk.data = (CMD_STOP_LOGGING, self.id) - self.cf.send_packet( - pk, expected_reply=(CMD_STOP_LOGGING, self.id)) - - def delete(self): - """Delete this entry in the Crazyflie""" - if (self.cf.link is not None): - if (self.id is None): - logger.warning("Delete block, but no block registered") - else: - logger.debug("LogEntry: Sending delete logging for block id=%d" - % self.id) - pk = CRTPPacket() - pk.set_header(5, CHAN_SETTINGS) - pk.data = (CMD_DELETE_BLOCK, self.id) - self.cf.send_packet( - pk, expected_reply=(CMD_DELETE_BLOCK, self.id)) - - def unpack_log_data(self, log_data, timestamp): - """Unpack received logging data so it represent real values according - to the configuration in the entry""" - ret_data = {} - data_index = 0 - for var in self.variables: - size = LogTocElement.get_size_from_id(var.fetch_as) - name = var.name - unpackstring = LogTocElement.get_unpack_string_from_id( - var.fetch_as) - value = struct.unpack( - unpackstring, log_data[data_index:data_index + size])[0] - data_index += size - ret_data[name] = value - self.data_received_cb.call(timestamp, ret_data, self) - - -class LogTocElement: - """An element in the Log TOC.""" - types = {0x01: ("uint8_t", '<B', 1), - 0x02: ("uint16_t", '<H', 2), - 0x03: ("uint32_t", '<L', 4), - 0x04: ("int8_t", '<b', 1), - 0x05: ("int16_t", '<h', 2), - 0x06: ("int32_t", '<i', 4), - 0x08: ("FP16", '<h', 2), - 0x07: ("float", '<f', 4)} - - @staticmethod - def get_id_from_cstring(name): - """Return variable type id given the C-storage name""" - for key in list(LogTocElement.types.keys()): - if (LogTocElement.types[key][0] == name): - return key - raise KeyError("Type [%s] not found in LogTocElement.types!" % name) - - @staticmethod - def get_cstring_from_id(ident): - """Return the C-storage name given the variable type id""" - try: - return LogTocElement.types[ident][0] - except KeyError: - raise KeyError("Type [%d] not found in LogTocElement.types" - "!" % ident) - - @staticmethod - def get_size_from_id(ident): - """Return the size in bytes given the variable type id""" - try: - return LogTocElement.types[ident][2] - except KeyError: - raise KeyError("Type [%d] not found in LogTocElement.types" - "!" % ident) - - @staticmethod - def get_unpack_string_from_id(ident): - """Return the Python unpack string given the variable type id""" - try: - return LogTocElement.types[ident][1] - except KeyError: - raise KeyError( - "Type [%d] not found in LogTocElement.types!" % ident) - - def __init__(self, data=None): - """TocElement creator. Data is the binary payload of the element.""" - - if (data): - naming = data[2:] - zt = bytearray((0, )) - self.group = naming[:naming.find(zt)].decode("ISO-8859-1") - self.name = naming[naming.find(zt) + 1:-1].decode("ISO-8859-1") - - self.ident = data[0] - - self.ctype = LogTocElement.get_cstring_from_id(data[1]) - self.pytype = LogTocElement.get_unpack_string_from_id(data[1]) - - self.access = data[1] & 0x10 - - -class Log(): - """Create log configuration""" - - # These codes can be decoded using os.stderror, but - # some of the text messages will look very strange - # in the UI, so they are redefined here - _err_codes = { - errno.ENOMEM: "No more memory available", - errno.ENOEXEC: "Command not found", - errno.ENOENT: "No such block id", - errno.E2BIG: "Block too large", - errno.EEXIST: "Block already exists" - } - - def __init__(self, crazyflie=None): - self.log_blocks = [] - # Called with newly created blocks - self.block_added_cb = Caller() - - self.cf = crazyflie - self.toc = None - self.cf.add_port_callback(CRTPPort.LOGGING, self._new_packet_cb) - - self.toc_updated = Caller() - self.state = IDLE - self.fake_toc_crc = 0xDEADBEEF - - self._refresh_callback = None - self._toc_cache = None - - def add_config(self, logconf): - """Add a log configuration to the logging framework. - - When doing this the contents of the log configuration will be validated - and listeners for new log configurations will be notified. When - validating the configuration the variables are checked against the TOC - to see that they actually exist. If they don't then the configuration - cannot be used. Since a valid TOC is required, a Crazyflie has to be - connected when calling this method, otherwise it will fail.""" - - if not self.cf.link: - logger.error("Cannot add configs without being connected to a " - "Crazyflie!") - return - - # If the log configuration contains variables that we added without - # type (i.e we want the stored as type for fetching as well) then - # resolve this now and add them to the block again. - for name in logconf.default_fetch_as: - var = self.toc.get_element_by_complete_name(name) - if not var: - logger.warning( - "%s not in TOC, this block cannot be used!", name) - logconf.valid = False - raise KeyError("Variable {} not in TOC".format(name)) - # Now that we know what type this variable has, add it to the log - # config again with the correct type - logconf.add_variable(name, var.ctype) - - # Now check that all the added variables are in the TOC and that - # the total size constraint of a data packet with logging data is - # not - size = 0 - for var in logconf.variables: - size += LogTocElement.get_size_from_id(var.fetch_as) - # Check that we are able to find the variable in the TOC so - # we can return error already now and not when the config is sent - if var.is_toc_variable(): - if (self.toc.get_element_by_complete_name(var.name) is None): - logger.warning( - "Log: %s not in TOC, this block cannot be used!", - var.name) - logconf.valid = False - raise KeyError("Variable {} not in TOC".format(var.name)) - - if (size <= MAX_LOG_DATA_PACKET_SIZE and - (logconf.period > 0 and logconf.period < 0xFF)): - logconf.valid = True - logconf.cf = self.cf - self.log_blocks.append(logconf) - self.block_added_cb.call(logconf) - else: - logconf.valid = False - raise AttributeError( - "The log configuration is too large or has an invalid " - "parameter") - - def refresh_toc(self, refresh_done_callback, toc_cache): - """Start refreshing the table of loggale variables""" - - self._toc_cache = toc_cache - self._refresh_callback = refresh_done_callback - self.toc = None - - pk = CRTPPacket() - pk.set_header(CRTPPort.LOGGING, CHAN_SETTINGS) - pk.data = (CMD_RESET_LOGGING,) - self.cf.send_packet(pk, expected_reply=(CMD_RESET_LOGGING,)) - - def _find_block(self, id): - for block in self.log_blocks: - if block.id == id: - return block - return None - - def _new_packet_cb(self, packet): - """Callback for newly arrived packets with TOC information""" - chan = packet.channel - cmd = packet.data[0] - payload = packet.data[1:] - - if (chan == CHAN_SETTINGS): - id = payload[0] - error_status = payload[1] - block = self._find_block(id) - if (cmd == CMD_CREATE_BLOCK): - if (block is not None): - if error_status == 0 or error_status == errno.EEXIST: - if not block.added: - logger.debug("Have successfully added id=%d", id) - - pk = CRTPPacket() - pk.set_header(5, CHAN_SETTINGS) - pk.data = (CMD_START_LOGGING, id, block.period) - self.cf.send_packet(pk, expected_reply=( - CMD_START_LOGGING, id)) - block.added = True - else: - msg = self._err_codes[error_status] - logger.warning("Error %d when adding id=%d (%s)", - error_status, id, msg) - block.err_no = error_status - block.added_cb.call(False) - block.error_cb.call(block, msg) - - else: - logger.warning("No LogEntry to assign block to !!!") - if (cmd == CMD_START_LOGGING): - if (error_status == 0x00): - logger.info("Have successfully started logging for id=%d", - id) - if block: - block.started = True - - else: - msg = self._err_codes[error_status] - logger.warning("Error %d when starting id=%d (%s)", - error_status, id, msg) - if block: - block.err_no = error_status - block.started_cb.call(self, False) - # This is a temporary fix, we are adding a new issue - # for this. For some reason we get an error back after - # the block has been started and added. This will show - # an error in the UI, but everything is still working. - # block.error_cb.call(block, msg) - - if (cmd == CMD_STOP_LOGGING): - if (error_status == 0x00): - logger.info("Have successfully stopped logging for id=%d", - id) - if block: - block.started = False - - if (cmd == CMD_DELETE_BLOCK): - # Accept deletion of a block that isn't added. This could - # happen due to timing (i.e add/start/delete in fast sequence) - if error_status == 0x00 or error_status == errno.ENOENT: - logger.info("Have successfully deleted id=%d", id) - if block: - block.started = False - block.added = False - - if (cmd == CMD_RESET_LOGGING): - # Guard against multiple responses due to re-sending - if not self.toc: - logger.debug("Logging reset, continue with TOC download") - self.log_blocks = [] - - self.toc = Toc() - toc_fetcher = TocFetcher(self.cf, LogTocElement, - CRTPPort.LOGGING, - self.toc, self._refresh_callback, - self._toc_cache) - toc_fetcher.start() - - if (chan == CHAN_LOGDATA): - chan = packet.channel - id = packet.data[0] - block = self._find_block(id) - timestamps = struct.unpack("<BBB", packet.data[1:4]) - timestamp = ( - timestamps[0] | timestamps[1] << 8 | timestamps[2] << 16) - logdata = packet.data[4:] - if (block is not None): - block.unpack_log_data(logdata, timestamp) - else: - logger.warning("Error no LogEntry to handle id=%d", id) diff --git a/src/cflib/cflib/crazyflie/mem.py b/src/cflib/cflib/crazyflie/mem.py deleted file mode 100644 index e6cdd906ac06527a3c1c084dee269fe1a2f635a4..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/mem.py +++ /dev/null @@ -1,844 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2014 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Enables flash access to the Crazyflie. - -""" - -import struct -import errno -import sys -from threading import Lock -from cflib.crtp.crtpstack import CRTPPacket, CRTPPort -from cflib.utils.callbacks import Caller -from binascii import crc32 -import binascii -import logging -from functools import reduce - -__author__ = 'Bitcraze AB' -__all__ = ['Memory', 'MemoryElement'] - -# Channels used for the logging port -CHAN_INFO = 0 -CHAN_READ = 1 -CHAN_WRITE = 2 - -# Commands used when accessing the Settings port -CMD_INFO_VER = 0 -CMD_INFO_NBR = 1 -CMD_INFO_DETAILS = 2 - -# The max size of a CRTP packet payload -MAX_LOG_DATA_PACKET_SIZE = 30 - -if sys.version_info < (3,): - EEPROM_TOKEN = "0xBC" -else: - EEPROM_TOKEN = b"0xBC" - -logger = logging.getLogger(__name__) - - -class MemoryElement(object): - """A memory """ - - TYPE_I2C = 0 - TYPE_1W = 1 - TYPE_DRIVER_LED = 0x10 - - def __init__(self, id, type, size, mem_handler): - """Initialize the element with default values""" - self.id = id - self.type = type - self.size = size - self.mem_handler = mem_handler - - @staticmethod - def type_to_string(t): - """Get string representation of memory type""" - if t == MemoryElement.TYPE_I2C: - return "I2C" - if t == MemoryElement.TYPE_1W: - return "1-wire" - if t == MemoryElement.TYPE_DRIVER_LED: - return "LED driver" - return "Unknown" - - def new_data(self, mem, addr, data): - logger.info("New data, but not OW mem") - - def __str__(self): - """Generate debug string for memory""" - return ("Memory: id={}, type={}, size={}".format( - self.id, MemoryElement.type_to_string(self.type), self.size)) - - -class LED: - """Used to set color/intensity of one LED in the LED-ring""" - - def __init__(self): - """Initialize to off""" - self.r = 0 - self.g = 0 - self.b = 0 - self.intensity = 100 - - def set(self, r, g, b, intensity=None): - """Set the R/G/B and optionally intensity in one call""" - self.r = r - self.g = g - self.b = b - if intensity: - self.intensity = intensity - - -class LEDDriverMemory(MemoryElement): - """Memory interface for using the LED-ring mapped memory for setting RGB - values for all the LEDs in the ring""" - - def __init__(self, id, type, size, mem_handler): - """Initialize with 12 LEDs""" - super(LEDDriverMemory, self).__init__(id=id, type=type, size=size, - mem_handler=mem_handler) - self._update_finished_cb = None - self._write_finished_cb = None - - self.leds = [] - for i in range(12): - self.leds.append(LED()) - - def new_data(self, mem, addr, data): - """Callback for when new memory data has been fetched""" - if mem.id == self.id: - logger.info("Got new data from the LED driver, but we don't care.") - - def write_data(self, write_finished_cb): - """Write the saved LED-ring data to the Crazyflie""" - self._write_finished_cb = write_finished_cb - data = bytearray() - for led in self.leds: - # In order to fit all the LEDs in one radio packet RGB565 is used - # to compress the colors. The calculations below converts 3 bytes - # RGB into 2 bytes RGB565. Then shifts the value of each color to - # LSB, applies the intensity and shifts them back for correct - # alignment on 2 bytes. - R5 = ((int)((((int(led.r) & 0xFF) * 249 + 1014) >> 11) & 0x1F) * - led.intensity / 100) - G6 = ((int)((((int(led.g) & 0xFF) * 253 + 505) >> 10) & 0x3F) * - led.intensity / 100) - B5 = ((int)((((int(led.b) & 0xFF) * 249 + 1014) >> 11) & 0x1F) * - led.intensity / 100) - tmp = (int(R5) << 11) | (int(G6) << 5) | (int(B5) << 0) - data += bytearray((tmp >> 8, tmp & 0xFF)) - self.mem_handler.write(self, 0x00, data, flush_queue=True) - - def update(self, update_finished_cb): - """Request an update of the memory content""" - if not self._update_finished_cb: - self._update_finished_cb = update_finished_cb - self.valid = False - logger.info("Updating content of memory {}".format(self.id)) - # Start reading the header - self.mem_handler.read(self, 0, 16) - - def write_done(self, mem, addr): - if self._write_finished_cb and mem.id == self.id: - logger.info("Write to LED driver done") - self._write_finished_cb(self, addr) - self._write_finished_cb = None - - def disconnect(self): - self._update_finished_cb = None - self._write_finished_cb = None - - -class I2CElement(MemoryElement): - - def __init__(self, id, type, size, mem_handler): - super(I2CElement, self).__init__(id=id, type=type, size=size, - mem_handler=mem_handler) - self._update_finished_cb = None - self._write_finished_cb = None - self.elements = {} - self.valid = False - - def new_data(self, mem, addr, data): - """Callback for when new memory data has been fetched""" - if mem.id == self.id: - if addr == 0: - done = False - # Check for header - if data[0:4] == EEPROM_TOKEN: - logger.info("Got new data: {}".format(data)) - [self.elements["version"], - self.elements["radio_channel"], - self.elements["radio_speed"], - self.elements["pitch_trim"], - self.elements["roll_trim"]] = struct.unpack("<BBBff", - data[4:15]) - if self.elements["version"] == 0: - done = True - elif self.elements["version"] == 1: - self.datav0 = data - self.mem_handler.read(self, 16, 5) - else: - valid = False - if self._update_finished_cb: - self._update_finished_cb(self) - self._update_finished_cb = None - - if addr == 16: - [radio_address_upper, radio_address_lower] = struct.unpack( - "<BI", self.datav0[15:16] + data[0:4]) - self.elements["radio_address"] = int( - radio_address_upper) << 32 | radio_address_lower - - logger.info(self.elements) - data = self.datav0 + data - done = True - - if done: - if self._checksum256(data[:len(data) - 1]) == \ - data[len(data) - 1]: - self.valid = True - if self._update_finished_cb: - self._update_finished_cb(self) - self._update_finished_cb = None - - def _checksum256(self, st): - return reduce(lambda x, y: x + y, list(st)) % 256 - - def write_data(self, write_finished_cb): - image = bytearray() - if self.elements["version"] == 0: - data = ( - 0x00, self.elements["radio_channel"], - self.elements["radio_speed"], - self.elements["pitch_trim"], self.elements["roll_trim"]) - image += struct.pack("<BBBff", *data) - elif self.elements["version"] == 1: - data = ( - 0x01, self.elements["radio_channel"], - self.elements["radio_speed"], - self.elements["pitch_trim"], self.elements["roll_trim"], - self.elements["radio_address"] >> 32, - self.elements["radio_address"] & 0xFFFFFFFF) - image += struct.pack("<BBBffBI", *data) - # Adding some magic: - image = EEPROM_TOKEN + image - image += struct.pack("B", self._checksum256(image)) - - self._write_finished_cb = write_finished_cb - - self.mem_handler.write(self, 0x00, - struct.unpack("B" * len(image), image)) - - def update(self, update_finished_cb): - """Request an update of the memory content""" - if not self._update_finished_cb: - self._update_finished_cb = update_finished_cb - self.valid = False - logger.info("Updating content of memory {}".format(self.id)) - # Start reading the header - self.mem_handler.read(self, 0, 16) - - def write_done(self, mem, addr): - if self._write_finished_cb and mem.id == self.id: - self._write_finished_cb(self, addr) - self._write_finished_cb = None - - def disconnect(self): - self._update_finished_cb = None - self._write_finished_cb = None - - -class OWElement(MemoryElement): - """Memory class with extra functionality for 1-wire memories""" - - element_mapping = { - 1: "Board name", - 2: "Board revision", - 3: "Custom" - } - - def __init__(self, id, type, size, addr, mem_handler): - """Initialize the memory with good defaults""" - super(OWElement, self).__init__(id=id, type=type, size=size, - mem_handler=mem_handler) - self.addr = addr - - self.valid = False - - self.vid = None - self.pid = None - self.name = None - self.pins = None - self.elements = {} - - self._update_finished_cb = None - self._write_finished_cb = None - - self._rev_element_mapping = {} - for key in list(OWElement.element_mapping.keys()): - self._rev_element_mapping[OWElement.element_mapping[key]] = key - - def new_data(self, mem, addr, data): - """Callback for when new memory data has been fetched""" - if mem.id == self.id: - if addr == 0: - if self._parse_and_check_header(data[0:8]): - if self._parse_and_check_elements(data[9:11]): - self.valid = True - self._update_finished_cb(self) - self._update_finished_cb = None - else: - # We need to fetch the elements, find out the length - (elem_ver, elem_len) = struct.unpack("BB", data[8:10]) - self.mem_handler.read(self, 8, elem_len + 3) - else: - # Call the update if the CRC check of the header fails, - # we're done here - if self._update_finished_cb: - self._update_finished_cb(self) - self._update_finished_cb = None - elif addr == 0x08: - if self._parse_and_check_elements(data): - self.valid = True - if self._update_finished_cb: - self._update_finished_cb(self) - self._update_finished_cb = None - - def _parse_and_check_elements(self, data): - """ - Parse and check the CRC and length of the elements part of the memory - """ - (elem_ver, elem_len, crc) = (data[0], data[1], data[-1]) - test_crc = crc32(data[:-1]) & 0x0ff - elem_data = data[2:-1] - if test_crc == crc: - while len(elem_data) > 0: - (eid, elen) = struct.unpack("BB", elem_data[:2]) - self.elements[self.element_mapping[eid]] = \ - elem_data[2:2 + elen].decode("ISO-8859-1") - elem_data = elem_data[2 + elen:] - return True - return False - - def write_done(self, mem, addr): - if self._write_finished_cb: - self._write_finished_cb(self, addr) - self._write_finished_cb = None - - def write_data(self, write_finished_cb): - # First generate the header part - header_data = struct.pack("<BIBB", 0xEB, self.pins, self.vid, self.pid) - header_crc = crc32(header_data) & 0x0ff - header_data += struct.pack("B", header_crc) - - # Now generate the elements part - elem = bytearray() - logger.info(list(self.elements.keys())) - for element in reversed(list(self.elements.keys())): - elem_string = self.elements[element] - # logger.info(">>>> {}".format(elem_string)) - key_encoding = self._rev_element_mapping[element] - elem += struct.pack("BB", key_encoding, len(elem_string)) - elem += bytearray(elem_string.encode("ISO-8859-1")) - - elem_data = struct.pack("BB", 0x00, len(elem)) - elem_data += elem - elem_crc = crc32(elem_data) & 0x0ff - elem_data += struct.pack("B", elem_crc) - - data = header_data + elem_data - - self.mem_handler.write(self, 0x00, - struct.unpack("B" * len(data), data)) - - self._write_finished_cb = write_finished_cb - - def update(self, update_finished_cb): - """Request an update of the memory content""" - if not self._update_finished_cb: - self._update_finished_cb = update_finished_cb - self.valid = False - logger.info("Updating content of memory {}".format(self.id)) - # Start reading the header - self.mem_handler.read(self, 0, 11) - - def _parse_and_check_header(self, data): - """Parse and check the CRC of the header part of the memory""" - # logger.info("Should parse header: {}".format(data)) - (start, self.pins, self.vid, self.pid, crc) = struct.unpack("<BIBBB", - data) - test_crc = crc32(data[:-1]) & 0x0ff - if start == 0xEB and crc == test_crc: - return True - return False - - def __str__(self): - """Generate debug string for memory""" - return ("OW {} ({:02X}:{:02X}): {}".format( - self.addr, self.vid, self.pid, self.elements)) - - def disconnect(self): - self._update_finished_cb = None - self._write_finished_cb = None - - -class _ReadRequest: - """ - Class used to handle memory reads that will split up the read in multiple - packets in necessary - """ - MAX_DATA_LENGTH = 20 - - def __init__(self, mem, addr, length, cf): - """Initialize the object with good defaults""" - self.mem = mem - self.addr = addr - self._bytes_left = length - self.data = bytearray() - self.cf = cf - - self._current_addr = addr - - def start(self): - """Start the fetching of the data""" - self._request_new_chunk() - - def resend(self): - logger.info("Sending write again...") - self._request_new_chunk() - - def _request_new_chunk(self): - """ - Called to request a new chunk of data to be read from the Crazyflie - """ - # Figure out the length of the next request - new_len = self._bytes_left - if new_len > _ReadRequest.MAX_DATA_LENGTH: - new_len = _ReadRequest.MAX_DATA_LENGTH - - logger.info("Requesting new chunk of {}bytes at 0x{:X}".format( - new_len, self._current_addr)) - - # Request the data for the next address - pk = CRTPPacket() - pk.set_header(CRTPPort.MEM, CHAN_READ) - pk.data = struct.pack("<BIB", self.mem.id, self._current_addr, new_len) - reply = struct.unpack("<BBBBB", pk.data[:-1]) - self.cf.send_packet(pk, expected_reply=reply, timeout=1) - - def add_data(self, addr, data): - """Callback when data is received from the Crazyflie""" - data_len = len(data) - if not addr == self._current_addr: - logger.warning( - "Address did not match when adding data to read request!") - return - - # Add the data and calculate the next address to fetch - self.data += data - self._bytes_left -= data_len - self._current_addr += data_len - - if self._bytes_left > 0: - self._request_new_chunk() - return False - else: - return True - - -class _WriteRequest: - """ - Class used to handle memory reads that will split up the read in multiple - packets in necessary - """ - MAX_DATA_LENGTH = 25 - - def __init__(self, mem, addr, data, cf): - """Initialize the object with good defaults""" - self.mem = mem - self.addr = addr - self._bytes_left = len(data) - self._data = data - self.data = bytearray() - self.cf = cf - - self._current_addr = addr - - self._sent_packet = None - self._sent_reply = None - - self._addr_add = 0 - - def start(self): - """Start the fetching of the data""" - self._write_new_chunk() - - def resend(self): - logger.info("Sending write again...") - self.cf.send_packet( - self._sent_packet, expected_reply=self._sent_reply, timeout=1) - - def _write_new_chunk(self): - """ - Called to request a new chunk of data to be read from the Crazyflie - """ - # Figure out the length of the next request - new_len = len(self._data) - if new_len > _WriteRequest.MAX_DATA_LENGTH: - new_len = _WriteRequest.MAX_DATA_LENGTH - - logger.info("Writing new chunk of {}bytes at 0x{:X}".format( - new_len, self._current_addr)) - - data = self._data[:new_len] - self._data = self._data[new_len:] - - pk = CRTPPacket() - pk.set_header(CRTPPort.MEM, CHAN_WRITE) - pk.data = struct.pack("<BI", self.mem.id, self._current_addr) - # Create a tuple used for matching the reply using id and address - reply = struct.unpack("<BBBBB", pk.data) - self._sent_reply = reply - # Add the data - pk.data += struct.pack("B" * len(data), *data) - self._sent_packet = pk - self.cf.send_packet(pk, expected_reply=reply, timeout=1) - - self._addr_add = len(data) - - def write_done(self, addr): - """Callback when data is received from the Crazyflie""" - if not addr == self._current_addr: - logger.warning( - "Address did not match when adding data to read request!") - return - - if len(self._data) > 0: - self._current_addr += self._addr_add - self._write_new_chunk() - return False - else: - logger.info("This write request is done") - return True - - -class Memory(): - """Access memories on the Crazyflie""" - - # These codes can be decoded using os.stderror, but - # some of the text messages will look very strange - # in the UI, so they are redefined here - _err_codes = { - errno.ENOMEM: "No more memory available", - errno.ENOEXEC: "Command not found", - errno.ENOENT: "No such block id", - errno.E2BIG: "Block too large", - errno.EEXIST: "Block already exists" - } - - def __init__(self, crazyflie=None): - """Instantiate class and connect callbacks""" - self.mems = [] - # Called when new memories have been added - self.mem_added_cb = Caller() - # Called when new data has been read - self.mem_read_cb = Caller() - - self.mem_write_cb = Caller() - - self.cf = crazyflie - self.cf.add_port_callback(CRTPPort.MEM, self._new_packet_cb) - - self._refresh_callback = None - self._fetch_id = 0 - self.nbr_of_mems = 0 - self._ow_mem_fetch_index = 0 - self._elem_data = () - self._read_requests = {} - self._read_requests_lock = Lock() - self._write_requests = {} - self._write_requests_lock = Lock() - self._ow_mems_left_to_update = [] - - self._getting_count = False - - def _mem_update_done(self, mem): - """ - Callback from each individual memory (only 1-wire) when reading of - header/elements are done - """ - if mem.id in self._ow_mems_left_to_update: - self._ow_mems_left_to_update.remove(mem.id) - - logger.info(mem) - - if len(self._ow_mems_left_to_update) == 0: - if self._refresh_callback: - self._refresh_callback() - self._refresh_callback = None - - def get_mem(self, id): - """Fetch the memory with the supplied id""" - for m in self.mems: - if m.id == id: - return m - - return None - - def get_mems(self, type): - """Fetch all the memories of the supplied type""" - ret = () - for m in self.mems: - if m.type == type: - ret += (m,) - - return ret - - def ow_search(self, vid=0xBC, pid=None, name=None): - """Search for specific memory id/name and return it""" - for m in self.get_mems(MemoryElement.TYPE_1W): - if pid and m.pid == pid or name and m.name == name: - return m - - return None - - def write(self, memory, addr, data, flush_queue=False): - """Write the specified data to the given memory at the given address""" - wreq = _WriteRequest(memory, addr, data, self.cf) - if memory.id not in self._write_requests: - self._write_requests[memory.id] = [] - - # Workaround until we secure the uplink and change messages for - # mems to non-blocking - self._write_requests_lock.acquire() - if flush_queue: - self._write_requests[memory.id] = self._write_requests[ - memory.id][:1] - self._write_requests[memory.id].insert(len(self._write_requests), wreq) - if len(self._write_requests[memory.id]) == 1: - wreq.start() - self._write_requests_lock.release() - - return True - - def read(self, memory, addr, length): - """ - Read the specified amount of bytes from the given memory at the given - address - """ - if memory.id in self._read_requests: - logger.warning("There is already a read operation ongoing for " - "memory id {}".format(memory.id)) - return False - - rreq = _ReadRequest(memory, addr, length, self.cf) - self._read_requests[memory.id] = rreq - - rreq.start() - - return True - - def refresh(self, refresh_done_callback): - """Start fetching all the detected memories""" - self._refresh_callback = refresh_done_callback - self._fetch_id = 0 - for m in self.mems: - try: - self.mem_read_cb.remove_callback(m.new_data) - m.disconnect() - except Exception as e: - logger.info( - "Error when removing memory after update: {}".format(e)) - self.mems = [] - - self.nbr_of_mems = 0 - self._getting_count = False - - logger.info("Requesting number of memories") - pk = CRTPPacket() - pk.set_header(CRTPPort.MEM, CHAN_INFO) - pk.data = (CMD_INFO_NBR,) - self.cf.send_packet(pk, expected_reply=(CMD_INFO_NBR,)) - - def _new_packet_cb(self, packet): - """Callback for newly arrived packets for the memory port""" - chan = packet.channel - cmd = packet.data[0] - payload = packet.data[1:] - - if chan == CHAN_INFO: - if cmd == CMD_INFO_NBR: - self.nbr_of_mems = payload[0] - logger.info("{} memories found".format(self.nbr_of_mems)) - - # Start requesting information about the memories, - # if there are any... - if self.nbr_of_mems > 0: - if not self._getting_count: - self._getting_count = True - logger.info("Requesting first id") - pk = CRTPPacket() - pk.set_header(CRTPPort.MEM, CHAN_INFO) - pk.data = (CMD_INFO_DETAILS, 0) - self.cf.send_packet(pk, expected_reply=( - CMD_INFO_DETAILS, 0)) - else: - self._refresh_callback() - - if cmd == CMD_INFO_DETAILS: - - # Did we get a good reply, otherwise try again: - if len(payload) < 5: - # Workaround for 1-wire bug when memory is detected - # but updating the info crashes the communication with - # the 1-wire. Fail by saying we only found 1 memory - # (the I2C). - logger.error( - "-------->Got good count, but no info on mem!") - self.nbr_of_mems = 1 - if self._refresh_callback: - self._refresh_callback() - self._refresh_callback = None - return - - # Create information about a new memory - # Id - 1 byte - mem_id = payload[0] - # Type - 1 byte - mem_type = payload[1] - # Size 4 bytes (as addr) - mem_size = struct.unpack("I", payload[2:6])[0] - # Addr (only valid for 1-wire?) - mem_addr_raw = struct.unpack("B" * 8, payload[6:14]) - mem_addr = "" - for m in mem_addr_raw: - mem_addr += "{:02X}".format(m) - - if (not self.get_mem(mem_id)): - if mem_type == MemoryElement.TYPE_1W: - mem = OWElement(id=mem_id, type=mem_type, - size=mem_size, - addr=mem_addr, mem_handler=self) - self.mem_read_cb.add_callback(mem.new_data) - self.mem_write_cb.add_callback(mem.write_done) - self._ow_mems_left_to_update.append(mem.id) - elif mem_type == MemoryElement.TYPE_I2C: - mem = I2CElement(id=mem_id, type=mem_type, - size=mem_size, - mem_handler=self) - self.mem_read_cb.add_callback(mem.new_data) - self.mem_write_cb.add_callback(mem.write_done) - elif mem_type == MemoryElement.TYPE_DRIVER_LED: - mem = LEDDriverMemory(id=mem_id, type=mem_type, - size=mem_size, mem_handler=self) - logger.info(mem) - self.mem_read_cb.add_callback(mem.new_data) - self.mem_write_cb.add_callback(mem.write_done) - else: - mem = MemoryElement(id=mem_id, type=mem_type, - size=mem_size, mem_handler=self) - logger.info(mem) - self.mems.append(mem) - self.mem_added_cb.call(mem) - # logger.info(mem) - - self._fetch_id = mem_id + 1 - - if self.nbr_of_mems - 1 >= self._fetch_id: - logger.info( - "Requesting information about memory {}".format( - self._fetch_id)) - pk = CRTPPacket() - pk.set_header(CRTPPort.MEM, CHAN_INFO) - pk.data = (CMD_INFO_DETAILS, self._fetch_id) - self.cf.send_packet(pk, expected_reply=( - CMD_INFO_DETAILS, self._fetch_id)) - else: - logger.info( - "Done getting all the memories, start reading the OWs") - ows = self.get_mems(MemoryElement.TYPE_1W) - # If there are any OW mems start reading them, otherwise - # we are done - for ow_mem in self.get_mems(MemoryElement.TYPE_1W): - ow_mem.update(self._mem_update_done) - if len(self.get_mems(MemoryElement.TYPE_1W)) == 0: - if self._refresh_callback: - self._refresh_callback() - self._refresh_callback = None - - if chan == CHAN_WRITE: - id = cmd - (addr, status) = struct.unpack("<IB", payload[0:5]) - logger.info( - "WRITE: Mem={}, addr=0x{:X}, status=0x{}".format( - id, addr, status)) - # Find the read request - if id in self._write_requests: - self._write_requests_lock.acquire() - wreq = self._write_requests[id][0] - if status == 0: - if wreq.write_done(addr): - # self._write_requests.pop(id, None) - # Remove the first item - self._write_requests[id].pop(0) - self.mem_write_cb.call(wreq.mem, wreq.addr) - - # Get a new one to start (if there are any) - if len(self._write_requests[id]) > 0: - self._write_requests[id][0].start() - - else: - logger.info("Status {}: write resending...".format(status)) - wreq.resend() - self._write_requests_lock.release() - - if chan == CHAN_READ: - id = cmd - (addr, status) = struct.unpack("<IB", payload[0:5]) - data = struct.unpack("B" * len(payload[5:]), payload[5:]) - logger.info("READ: Mem={}, addr=0x{:X}, status=0x{}, " - "data={}".format(id, addr, status, data)) - # Find the read request - if id in self._read_requests: - logger.info( - "READING: We are still interested in request for " - "mem {}".format(id)) - rreq = self._read_requests[id] - if status == 0: - if rreq.add_data(addr, payload[5:]): - self._read_requests.pop(id, None) - self.mem_read_cb.call(rreq.mem, rreq.addr, rreq.data) - else: - logger.info("Status {}: resending...".format(status)) - rreq.resend() diff --git a/src/cflib/cflib/crazyflie/param.py b/src/cflib/cflib/crazyflie/param.py deleted file mode 100644 index ff828abefa247763c719d39ea294b2c9f67c7921..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/param.py +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Enables reading/writing of parameter values to/from the Crazyflie. - -When a Crazyflie is connected it's possible to download a TableOfContent of all -the parameters that can be written/read. - -""" -import logging -import struct -import sys -from threading import Lock -from threading import Thread - -from cflib.crtp.crtpstack import CRTPPacket -from cflib.crtp.crtpstack import CRTPPort -from cflib.utils.callbacks import Caller - -from .toc import Toc -from .toc import TocFetcher -if sys.version_info < (3,): - from Queue import Queue -else: - from queue import Queue - - -__author__ = 'Bitcraze AB' -__all__ = ['Param', 'ParamTocElement'] - -logger = logging.getLogger(__name__) - -# Possible states -IDLE = 0 -WAIT_TOC = 1 -WAIT_READ = 2 -WAIT_WRITE = 3 - -TOC_CHANNEL = 0 -READ_CHANNEL = 1 -WRITE_CHANNEL = 2 - -# TOC access command -TOC_RESET = 0 -TOC_GETNEXT = 1 -TOC_GETCRC32 = 2 - - -# One element entry in the TOC -class ParamTocElement: - """An element in the Log TOC.""" - - RW_ACCESS = 0 - RO_ACCESS = 1 - - types = {0x08: ("uint8_t", '<B'), - 0x09: ("uint16_t", '<H'), - 0x0A: ("uint32_t", '<L'), - 0x0B: ("uint64_t", '<Q'), - 0x00: ("int8_t", '<b'), - 0x01: ("int16_t", '<h'), - 0x02: ("int32_t", '<i'), - 0x03: ("int64_t", '<q'), - 0x05: ("FP16", ''), - 0x06: ("float", '<f'), - 0x07: ("double", '<d')} - - def __init__(self, data=None): - """TocElement creator. Data is the binary payload of the element.""" - if (data): - strs = struct.unpack("s" * len(data[2:]), data[2:]) - if sys.version_info < (3,): - strs = ("{}" * len(strs)).format(*strs).split("\0") - else: - s = "" - for ch in strs: - s += ch.decode('ISO-8859-1') - strs = s.split("\x00") - self.group = strs[0] - self.name = strs[1] - - if type(data[0]) == str: - self.ident = ord(data[0]) - else: - self.ident = data[0] - - metadata = data[1] - if type(metadata) == str: - metadata = ord(metadata) - - self.ctype = self.types[metadata & 0x0F][0] - self.pytype = self.types[metadata & 0x0F][1] - if ((metadata & 0x40) != 0): - self.access = ParamTocElement.RO_ACCESS - else: - self.access = ParamTocElement.RW_ACCESS - - def get_readable_access(self): - if (self.access == ParamTocElement.RO_ACCESS): - return "RO" - return "RW" - - -class Param(): - """ - Used to read and write parameter values in the Crazyflie. - """ - - toc = Toc() - - def __init__(self, crazyflie): - self.cf = crazyflie - self.param_update_callbacks = {} - self.group_update_callbacks = {} - self.all_update_callback = Caller() - self.param_updater = None - - self.param_updater = _ParamUpdater(self.cf, self._param_updated) - self.param_updater.start() - - self.cf.disconnected.add_callback(self._disconnected) - - self.all_updated = Caller() - self.is_updated = False - - self.values = {} - - def request_update_of_all_params(self): - """Request an update of all the parameters in the TOC""" - for group in self.toc.toc: - for name in self.toc.toc[group]: - complete_name = "%s.%s" % (group, name) - self.request_param_update(complete_name) - - def _check_if_all_updated(self): - """Check if all parameters from the TOC has at least been fetched - once""" - for g in self.toc.toc: - if g not in self.values: - return False - for n in self.toc.toc[g]: - if n not in self.values[g]: - return False - - return True - - def _param_updated(self, pk): - """Callback with data for an updated parameter""" - var_id = pk.data[0] - element = self.toc.get_element_by_id(var_id) - if element: - s = struct.unpack(element.pytype, pk.data[1:])[0] - s = s.__str__() - complete_name = "%s.%s" % (element.group, element.name) - - # Save the value for synchronous access - if element.group not in self.values: - self.values[element.group] = {} - self.values[element.group][element.name] = s - - logger.debug("Updated parameter [%s]" % complete_name) - if complete_name in self.param_update_callbacks: - self.param_update_callbacks[complete_name].call( - complete_name, s) - if element.group in self.group_update_callbacks: - self.group_update_callbacks[element.group].call( - complete_name, s) - self.all_update_callback.call(complete_name, s) - - # Once all the parameters are updated call the - # callback for "everything updated" (after all the param - # updated callbacks) - if self._check_if_all_updated() and not self.is_updated: - self.is_updated = True - self.all_updated.call() - else: - logger.debug("Variable id [%d] not found in TOC", var_id) - - def remove_update_callback(self, group, name=None, cb=None): - """Remove the supplied callback for a group or a group.name""" - if not cb: - return - - if not name: - if group in self.group_update_callbacks: - self.group_update_callbacks[group].remove_callback(cb) - else: - paramname = "{}.{}".format(group, name) - if paramname in self.param_update_callbacks: - self.param_update_callbacks[paramname].remove_callback(cb) - - def add_update_callback(self, group=None, name=None, cb=None): - """ - Add a callback for a specific parameter name. This callback will be - executed when a new value is read from the Crazyflie. - """ - if not group and not name: - self.all_update_callback.add_callback(cb) - elif not name: - if group not in self.group_update_callbacks: - self.group_update_callbacks[group] = Caller() - self.group_update_callbacks[group].add_callback(cb) - else: - paramname = "{}.{}".format(group, name) - if paramname not in self.param_update_callbacks: - self.param_update_callbacks[paramname] = Caller() - self.param_update_callbacks[paramname].add_callback(cb) - - def refresh_toc(self, refresh_done_callback, toc_cache): - """ - Initiate a refresh of the parameter TOC. - """ - toc_fetcher = TocFetcher(self.cf, ParamTocElement, - CRTPPort.PARAM, self.toc, - refresh_done_callback, toc_cache) - toc_fetcher.start() - - def _disconnected(self, uri): - """Disconnected callback from Crazyflie API""" - self.param_updater.close() - self.is_updated = False - # Clear all values from the previous Crazyflie - self.toc = Toc() - self.values = {} - - def request_param_update(self, complete_name): - """ - Request an update of the value for the supplied parameter. - """ - self.param_updater.request_param_update( - self.toc.get_element_id(complete_name)) - - def set_value(self, complete_name, value): - """ - Set the value for the supplied parameter. - """ - element = self.toc.get_element_by_complete_name(complete_name) - - if not element: - logger.warning("Cannot set value for [%s], it's not in the TOC!", - complete_name) - raise KeyError("{} not in param TOC".format(complete_name)) - elif element.access == ParamTocElement.RO_ACCESS: - logger.debug("[%s] is read only, no trying to set value", - complete_name) - raise AttributeError("{} is read-only!".format(complete_name)) - else: - varid = element.ident - pk = CRTPPacket() - pk.set_header(CRTPPort.PARAM, WRITE_CHANNEL) - pk.data = struct.pack('<B', varid) - pk.data += struct.pack(element.pytype, eval(value)) - self.param_updater.request_param_setvalue(pk) - - -class _ParamUpdater(Thread): - """This thread will update params through a queue to make sure that we - get back values""" - - def __init__(self, cf, updated_callback): - """Initialize the thread""" - Thread.__init__(self) - self.setDaemon(True) - self.wait_lock = Lock() - self.cf = cf - self.updated_callback = updated_callback - self.request_queue = Queue() - self.cf.add_port_callback(CRTPPort.PARAM, self._new_packet_cb) - self._should_close = False - self._req_param = -1 - - def close(self): - # First empty the queue from all packets - while not self.request_queue.empty(): - self.request_queue.get() - # Then force an unlock of the mutex if we are waiting for a packet - # we didn't get back due to a disconnect for example. - try: - self.wait_lock.release() - except: - pass - - def request_param_setvalue(self, pk): - """Place a param set value request on the queue. When this is sent to - the Crazyflie it will answer with the update param value. """ - self.request_queue.put(pk) - - def _new_packet_cb(self, pk): - """Callback for newly arrived packets""" - if pk.channel == READ_CHANNEL or pk.channel == WRITE_CHANNEL: - var_id = pk.data[0] - if (pk.channel != TOC_CHANNEL and self._req_param == var_id and - pk is not None): - self.updated_callback(pk) - self._req_param = -1 - try: - self.wait_lock.release() - except: - pass - - def request_param_update(self, var_id): - """Place a param update request on the queue""" - pk = CRTPPacket() - pk.set_header(CRTPPort.PARAM, READ_CHANNEL) - pk.data = struct.pack('<B', var_id) - logger.debug("Requesting request to update param [%d]", var_id) - self.request_queue.put(pk) - - def run(self): - while not self._should_close: - pk = self.request_queue.get() # Wait for request update - self.wait_lock.acquire() - if self.cf.link: - self._req_param = pk.data[0] - self.cf.send_packet(pk, expected_reply=(tuple(pk.data[0:2]))) - else: - self.wait_lock.release() diff --git a/src/cflib/cflib/crazyflie/platformservice.py b/src/cflib/cflib/crazyflie/platformservice.py deleted file mode 100644 index 385f9ae892e145e189c8d14ae9a2eff90b01a23c..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/platformservice.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Used for sending control setpoints to the Crazyflie -""" -import struct - -from cflib.crtp.crtpstack import CRTPPacket -from cflib.crtp.crtpstack import CRTPPort - -__author__ = 'Bitcraze AB' -__all__ = ['PlatformService'] - - -class PlatformService(): - """ - Used for sending control setpoints to the Crazyflie - """ - - def __init__(self, crazyflie=None): - """ - Initialize the platform object. - """ - self._cf = crazyflie - - def set_continous_wave(self, enabled): - """ - Enable/disable the client side X-mode. When enabled this recalculates - the setpoints before sending them to the Crazyflie. - """ - pk = CRTPPacket() - pk.set_header(CRTPPort.PLATFORM, 0) - pk.data = (0, enabled) - self._cf.send_packet(pk) diff --git a/src/cflib/cflib/crazyflie/toc.py b/src/cflib/cflib/crazyflie/toc.py deleted file mode 100644 index 984e93ebaaf5d528ee4f1f102ad8c68b17fa434f..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/toc.py +++ /dev/null @@ -1,205 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -A generic TableOfContents module that is used to fetch, store and minipulate -a TOC for logging or parameters. -""" - -from cflib.crtp.crtpstack import CRTPPacket -import struct - -import logging - -__author__ = 'Bitcraze AB' -__all__ = ['TocElement', 'Toc', 'TocFetcher'] - -logger = logging.getLogger(__name__) - -TOC_CHANNEL = 0 - -# Commands used when accessing the Table of Contents -CMD_TOC_ELEMENT = 0 -CMD_TOC_INFO = 1 - -# Possible states when receiving TOC -IDLE = "IDLE" -GET_TOC_INFO = "GET_TOC_INFO" -GET_TOC_ELEMENT = "GET_TOC_ELEMENT" - - -class TocElement: - """An element in the TOC.""" - RW_ACCESS = 0 - RO_ACCESS = 1 - - ident = 0 - group = "" - name = "" - ctype = "" - pytype = "" - access = RO_ACCESS - - -class Toc: - """Container for TocElements.""" - - def __init__(self): - self.toc = {} - - def clear(self): - """Clear the TOC""" - self.toc = {} - - def add_element(self, element): - """Add a new TocElement to the TOC container.""" - try: - self.toc[element.group][element.name] = element - except KeyError: - self.toc[element.group] = {} - self.toc[element.group][element.name] = element - - def get_element_by_complete_name(self, complete_name): - """Get a TocElement element identified by complete name from the - container.""" - try: - return self.get_element_by_id(self.get_element_id(complete_name)) - except ValueError: - # Item not found - return None - - def get_element_id(self, complete_name): - """Get the TocElement element id-number of the element with the - supplied name.""" - [group, name] = complete_name.split(".") - element = self.get_element(group, name) - if element: - return element.ident - else: - logger.warning("Unable to find variable [%s]", complete_name) - return None - - def get_element(self, group, name): - """Get a TocElement element identified by name and group from the - container.""" - try: - return self.toc[group][name] - except KeyError: - return None - - def get_element_by_id(self, ident): - """Get a TocElement element identified by index number from the - container.""" - for group in list(self.toc.keys()): - for name in list(self.toc[group].keys()): - if self.toc[group][name].ident == ident: - return self.toc[group][name] - return None - - -class TocFetcher: - """Fetches TOC entries from the Crazyflie""" - - def __init__(self, crazyflie, element_class, port, toc_holder, - finished_callback, toc_cache): - self.cf = crazyflie - self.port = port - self._crc = 0 - self.requested_index = None - self.nbr_of_items = None - self.state = None - self.toc = toc_holder - self._toc_cache = toc_cache - self.finished_callback = finished_callback - self.element_class = element_class - - def start(self): - """Initiate fetching of the TOC.""" - logger.debug("[%d]: Start fetching...", self.port) - # Register callback in this class for the port - self.cf.add_port_callback(self.port, self._new_packet_cb) - - # Request the TOC CRC - self.state = GET_TOC_INFO - pk = CRTPPacket() - pk.set_header(self.port, TOC_CHANNEL) - pk.data = (CMD_TOC_INFO,) - self.cf.send_packet(pk, expected_reply=(CMD_TOC_INFO,)) - - def _toc_fetch_finished(self): - """Callback for when the TOC fetching is finished""" - self.cf.remove_port_callback(self.port, self._new_packet_cb) - logger.debug("[%d]: Done!", self.port) - self.finished_callback() - - def _new_packet_cb(self, packet): - """Handle a newly arrived packet""" - chan = packet.channel - if (chan != 0): - return - payload = packet.data[1:] - - if (self.state == GET_TOC_INFO): - [self.nbr_of_items, self._crc] = struct.unpack("<BI", payload[:5]) - logger.debug("[%d]: Got TOC CRC, %d items and crc=0x%08X", - self.port, self.nbr_of_items, self._crc) - - cache_data = self._toc_cache.fetch(self._crc) - if (cache_data): - self.toc.toc = cache_data - logger.info("TOC for port [%s] found in cache" % self.port) - self._toc_fetch_finished() - else: - self.state = GET_TOC_ELEMENT - self.requested_index = 0 - self._request_toc_element(self.requested_index) - - elif (self.state == GET_TOC_ELEMENT): - # Always add new element, but only request new if it's not the - # last one. - if self.requested_index != payload[0]: - return - self.toc.add_element(self.element_class(payload)) - logger.debug("Added element [%s]", - self.element_class(payload).ident) - if (self.requested_index < (self.nbr_of_items - 1)): - logger.debug("[%d]: More variables, requesting index %d", - self.port, self.requested_index + 1) - self.requested_index = self.requested_index + 1 - self._request_toc_element(self.requested_index) - else: # No more variables in TOC - self._toc_cache.insert(self._crc, self.toc.toc) - self._toc_fetch_finished() - - def _request_toc_element(self, index): - """Request information about a specific item in the TOC""" - logger.debug("Requesting index %d on port %d", index, self.port) - pk = CRTPPacket() - pk.set_header(self.port, TOC_CHANNEL) - pk.data = (CMD_TOC_ELEMENT, index) - self.cf.send_packet(pk, expected_reply=(CMD_TOC_ELEMENT, index)) diff --git a/src/cflib/cflib/crazyflie/toccache.py b/src/cflib/cflib/crazyflie/toccache.py deleted file mode 100644 index 22add3c8ea979e3e9352badd2ca5f77b5026cda7..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crazyflie/toccache.py +++ /dev/null @@ -1,127 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Access the TOC cache for reading/writing. It supports both user -cache and dist cache. -""" - -import os -import json -from glob import glob - -import logging - -from .log import LogTocElement # pylint: disable=W0611 -from .param import ParamTocElement # pylint: disable=W0611 - -__author__ = 'Bitcraze AB' -__all__ = ['TocCache'] - -logger = logging.getLogger(__name__) - - -class TocCache(): - """ - Access to TOC cache. To turn of the cache functionality - don't supply any directories. - """ - - def __init__(self, ro_cache=None, rw_cache=None): - self._cache_files = [] - if (ro_cache): - self._cache_files += glob(ro_cache + "/*.json") - if (rw_cache): - self._cache_files += glob(rw_cache + "/*.json") - if not os.path.exists(rw_cache): - os.makedirs(rw_cache) - - self._rw_cache = rw_cache - - def fetch(self, crc): - """ Try to get a hit in the cache, return None otherwise """ - cache_data = None - pattern = "%08X.json" % crc - hit = None - - for name in self._cache_files: - if (name.endswith(pattern)): - hit = name - - if (hit): - try: - cache = open(hit) - cache_data = json.load(cache, - object_hook=self._decoder) - cache.close() - except Exception as exp: - logger.warning("Error while parsing cache file [%s]:%s", - hit, str(exp)) - - return cache_data - - def insert(self, crc, toc): - """ Save a new cache to file """ - if self._rw_cache: - try: - filename = "%s/%08X.json" % (self._rw_cache, crc) - cache = open(filename, 'w') - cache.write(json.dumps(toc, indent=2, - default=self._encoder)) - cache.close() - logger.info("Saved cache to [%s]", filename) - self._cache_files += [filename] - except Exception as exp: - logger.warning("Could not save cache to file [%s]: %s", - filename, str(exp)) - else: - logger.warning("Could not save cache, no writable directory") - - def _encoder(self, obj): - """ Encode a toc element leaf-node """ - return {'__class__': obj.__class__.__name__, - 'ident': obj.ident, - 'group': obj.group, - 'name': obj.name, - 'ctype': obj.ctype, - 'pytype': obj.pytype, - 'access': obj.access} - raise TypeError(repr(obj) + ' is not JSON serializable') - - def _decoder(self, obj): - """ Decode a toc element leaf-node """ - if '__class__' in obj: - elem = eval(obj['__class__'])() - elem.ident = obj['ident'] - elem.group = str(obj['group']) - elem.name = str(obj['name']) - elem.ctype = str(obj['ctype']) - elem.pytype = str(obj['pytype']) - elem.access = obj['access'] - return elem - return obj diff --git a/src/cflib/cflib/crtp/__init__.py b/src/cflib/cflib/crtp/__init__.py deleted file mode 100644 index 687faaa7b0a26c61ef4321dab6f1454cc27889b6..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/__init__.py +++ /dev/null @@ -1,93 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -"""Scans and creates communication interfaces.""" -import logging - -from .debugdriver import DebugDriver -from .exceptions import WrongUriType -from .radiodriver import RadioDriver -from .serialdriver import SerialDriver -from .udpdriver import UdpDriver -from .usbdriver import UsbDriver - -__author__ = 'Bitcraze AB' -__all__ = [] - -logger = logging.getLogger(__name__) - - -DRIVERS = [RadioDriver, SerialDriver, UdpDriver, DebugDriver, UsbDriver] -INSTANCES = [] - - -def init_drivers(enable_debug_driver=False): - """Initialize all the drivers.""" - for driver in DRIVERS: - try: - if driver != DebugDriver or enable_debug_driver: - INSTANCES.append(driver()) - except Exception: # pylint: disable=W0703 - continue - - -def scan_interfaces(address=None): - """ Scan all the interfaces for available Crazyflies """ - available = [] - found = [] - for instance in INSTANCES: - logger.debug("Scanning: %s", instance) - try: - found = instance.scan_interface(address) - available += found - except Exception: - raise - return available - - -def get_interfaces_status(): - """Get the status of all the interfaces""" - status = {} - for instance in INSTANCES: - try: - status[instance.get_name()] = instance.get_status() - except Exception: - raise - return status - - -def get_link_driver(uri, link_quality_callback=None, link_error_callback=None): - """Return the link driver for the given URI. Returns None if no driver - was found for the URI or the URI was not well formatted for the matching - driver.""" - for instance in INSTANCES: - try: - instance.connect(uri, link_quality_callback, link_error_callback) - return instance - except WrongUriType: - continue - - return None diff --git a/src/cflib/cflib/crtp/crtpdriver.py b/src/cflib/cflib/crtp/crtpdriver.py deleted file mode 100644 index 7d9f7d8cb966a40bba79a984af67e2f3736c3aa8..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/crtpdriver.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -CRTP Driver main class. -""" - -__author__ = 'Bitcraze AB' -__all__ = ['CRTPDriver'] - - -class CRTPDriver: - """ CTRP Driver main class - - This class in inherited by all the CRTP link drivers. - """ - - def __init__(self): - """Driver constructor. Throw an exception if the driver is unable to - open the URI - """ - self.needs_resending = True - - def connect(self, uri, link_quality_callback, link_error_callback): - """Connect the driver to a specified URI - - @param uri Uri of the link to open - @param link_quality_callback Callback to report link quality in percent - @param link_error_callback Callback to report errors (will result in - disconnection) - """ - - def send_packet(self, pk): - """Send a CRTP packet""" - - def receive_packet(self, wait=0): - """Receive a CRTP packet. - - @param wait The time to wait for a packet in second. -1 means forever - - @return One CRTP packet or None if no packet has been received. - """ - - def get_status(self): - """ - Return a status string from the interface. - """ - - def get_name(self): - """ - Return a human readable name of the interface. - """ - - def scan_interface(self, address=None): - """ - Scan interface for available Crazyflie quadcopters and return a list - with them. - """ - - def enum(self): - """Enumerate, and return a list, of the available link URI on this - system - """ - - def get_help(self): - """return the help message on how to form the URI for this driver - None means no help - """ - - def close(self): - """Close the link""" diff --git a/src/cflib/cflib/crtp/crtpstack.py b/src/cflib/cflib/crtp/crtpstack.py deleted file mode 100644 index 3d3898ab4daf0eedb8c7c471d55c682d99342acc..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/crtpstack.py +++ /dev/null @@ -1,149 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -CRTP packet and ports. -""" -import logging -import sys - -__author__ = 'Bitcraze AB' -__all__ = ['CRTPPort', 'CRTPPacket'] - -logger = logging.getLogger(__name__) - - -class CRTPPort: - """ - Lists the available ports for the CRTP. - """ - CONSOLE = 0x00 - PARAM = 0x02 - COMMANDER = 0x03 - MEM = 0x04 - LOGGING = 0x05 - DEBUGDRIVER = 0x0E - LINKCTRL = 0x0F - ALL = 0xFF - - -class CRTPPacket(object): - """ - A packet that can be sent via the CRTP. - """ - - def __init__(self, header=0, data=None): - """ - Create an empty packet with default values. - """ - self.size = 0 - self._data = bytearray() - # The two bits in position 3 and 4 needs to be set for legacy - # support of the bootloader - self.header = header | 0x3 << 2 - self._port = (header & 0xF0) >> 4 - self._channel = header & 0x03 - if data: - self._set_data(data) - - def _get_channel(self): - """Get the packet channel""" - return self._channel - - def _set_channel(self, channel): - """Set the packet channel""" - self._channel = channel - self._update_header() - - def _get_port(self): - """Get the packet port""" - return self._port - - def _set_port(self, port): - """Set the packet port""" - self._port = port - self._update_header() - - def get_header(self): - """Get the header""" - self._update_header() - return self.header - - def set_header(self, port, channel): - """ - Set the port and channel for this packet. - """ - self._port = port - self.channel = channel - self._update_header() - - def _update_header(self): - """Update the header with the port/channel values""" - # The two bits in position 3 and 4 needs to be set for legacy - # support of the bootloader - self.header = ((self._port & 0x0f) << 4 | 3 << 2 | - (self.channel & 0x03)) - - # Some python madness to access different format of the data - def _get_data(self): - """Get the packet data""" - return self._data - - def _set_data(self, data): - """Set the packet data""" - if type(data) == bytearray: - self._data = data - elif type(data) == str: - if sys.version_info < (3,): - self._data = bytearray(data) - else: - self._data = bytearray(data.encode('ISO-8859-1')) - elif type(data) == list or type(data) == tuple: - self._data = bytearray(data) - elif sys.version_info >= (3,) and type(data) == bytes: - self._data = bytearray(data) - else: - raise Exception("Data must be bytearray, string, list or tuple," - " not {}".format(type(data))) - - def _get_data_l(self): - """Get the data in the packet as a list""" - return list(self._get_data_t()) - - def _get_data_t(self): - """Get the data in the packet as a tuple""" - return tuple(self._data) - - def __str__(self): - """Get a string representation of the packet""" - return "{}:{} {}".format(self._port, self.channel, self.datat) - - data = property(_get_data, _set_data) - datal = property(_get_data_l, _set_data) - datat = property(_get_data_t, _set_data) - datas = property(_get_data, _set_data) - port = property(_get_port, _set_port) - channel = property(_get_channel, _set_channel) diff --git a/src/cflib/cflib/crtp/debugdriver.py b/src/cflib/cflib/crtp/debugdriver.py deleted file mode 100644 index 98b554eff60c630aac5fc6ecb7655845306202e0..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/debugdriver.py +++ /dev/null @@ -1,897 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Fake link driver used to debug the UI without using the Crazyflie. - -The operation of this driver can be controlled in two ways, either by -connecting to different URIs or by sending messages to the DebugDriver port -though CRTP once connected. - -For normal connections a console thread is also started that will send -generated console output via CRTP. -""" -import errno -import logging -import random -import re -import string -import struct -import sys -import time -from datetime import datetime -from threading import Thread - -from cflib.crazyflie.log import LogTocElement -from cflib.crazyflie.param import ParamTocElement - -from .crtpdriver import CRTPDriver -from .crtpstack import CRTPPacket -from .crtpstack import CRTPPort -from .exceptions import WrongUriType -if sys.version_info < (3,): - import Queue as queue -else: - import queue - -__author__ = 'Bitcraze AB' -__all__ = ['DebugDriver'] - -logger = logging.getLogger(__name__) - -# This setup is used to debug raw memory logging -memlogging = {0x01: {"min": 0, "max": 255, "mod": 1, "vartype": 1}, - 0x02: {"min": 0, "max": 65000, "mod": 100, "vartype": 2}, - 0x03: {"min": 0, "max": 100000, "mod": 1000, "vartype": 3}, - 0x04: {"min": -100, "max": 100, "mod": 1, "vartype": 4}, - 0x05: {"min": -10000, "max": 10000, "mod": 2000, "vartype": 5}, - 0x06: {"min": -50000, "max": 50000, "mod": 1000, "vartype": 6}, - 0x07: {"min": 0, "max": 255, "mod": 1, "vartype": 1}} - - -class FakeMemory: - TYPE_I2C = 0 - TYPE_1W = 1 - - def __init__(self, type, size, addr, data=None): - self.type = type - self.size = size - self.addr = addr - self.data = [0] * size - if data: - for i in range(len(data)): - self.data[i] = data[i] - - def erase(self): - self.data = [0] * self.size - - -class DebugDriver(CRTPDriver): - """ Debug driver used for debugging UI/communication without using a - Crazyflie""" - - def __init__(self): - self.fakeLoggingThreads = [] - self._fake_mems = [] - self.needs_resending = False - # Fill up the fake logging TOC with values and data - self.fakeLogToc = [] - self.fakeLogToc.append({"varid": 0, "vartype": 5, "vargroup": "imu", - "varname": "gyro_x", "min": -10000, - "max": 10000, "mod": 1000}) - self.fakeLogToc.append({"varid": 1, "vartype": 5, "vargroup": "imu", - "varname": "gyro_y", "min": -10000, - "max": 10000, "mod": 150}) - self.fakeLogToc.append({"varid": 2, "vartype": 5, "vargroup": "imu", - "varname": "gyro_z", "min": -10000, - "max": 10000, "mod": 200}) - self.fakeLogToc.append({"varid": 3, "vartype": 5, "vargroup": "imu", - "varname": "acc_x", "min": -1000, - "max": 1000, "mod": 15}) - self.fakeLogToc.append({"varid": 4, "vartype": 5, "vargroup": "imu", - "varname": "acc_y", "min": -1000, - "max": 1000, "mod": 10}) - self.fakeLogToc.append({"varid": 5, "vartype": 5, "vargroup": "imu", - "varname": "acc_z", "min": -1000, - "max": 1000, "mod": 20}) - self.fakeLogToc.append({"varid": 6, "vartype": 7, - "vargroup": "stabilizer", "varname": "roll", - "min": -90, "max": 90, "mod": 2}) - self.fakeLogToc.append({"varid": 7, "vartype": 7, - "vargroup": "stabilizer", "varname": "pitch", - "min": -90, "max": 90, "mod": 1.5}) - self.fakeLogToc.append({"varid": 8, "vartype": 7, - "vargroup": "stabilizer", "varname": "yaw", - "min": -90, "max": 90, "mod": 2.5}) - self.fakeLogToc.append({"varid": 9, "vartype": 7, "vargroup": "pm", - "varname": "vbat", "min": 3.0, - "max": 4.2, "mod": 0.1}) - self.fakeLogToc.append({"varid": 10, "vartype": 6, "vargroup": "motor", - "varname": "m1", "min": 0, "max": 65000, - "mod": 1000}) - self.fakeLogToc.append({"varid": 11, "vartype": 6, "vargroup": "motor", - "varname": "m2", "min": 0, "max": 65000, - "mod": 1000}) - self.fakeLogToc.append({"varid": 12, "vartype": 6, "vargroup": "motor", - "varname": "m3", "min": 0, "max": 65000, - "mod": 1000}) - self.fakeLogToc.append({"varid": 13, "vartype": 6, "vargroup": "motor", - "varname": "m4", "min": 0, "max": 65000, - "mod": 1000}) - self.fakeLogToc.append({"varid": 14, "vartype": 2, - "vargroup": "stabilizer", "varname": "thrust", - "min": 0, "max": 65000, "mod": 1000}) - self.fakeLogToc.append({"varid": 15, "vartype": 7, - "vargroup": "baro", "varname": "asl", - "min": 540, "max": 545, "mod": 0.5}) - self.fakeLogToc.append({"varid": 16, "vartype": 7, - "vargroup": "baro", "varname": "aslRaw", - "min": 540, "max": 545, "mod": 1.0}) - self.fakeLogToc.append({"varid": 17, "vartype": 7, - "vargroup": "baro", "varname": "aslLong", - "min": 540, "max": 545, "mod": 0.5}) - self.fakeLogToc.append({"varid": 18, "vartype": 7, - "vargroup": "baro", "varname": "temp", - "min": 26, "max": 38, "mod": 1.0}) - self.fakeLogToc.append({"varid": 19, "vartype": 7, - "vargroup": "altHold", "varname": "target", - "min": 542, "max": 543, "mod": 0.1}) - self.fakeLogToc.append({"varid": 20, "vartype": 6, - "vargroup": "gps", "varname": "lat", - "min": 556112190, "max": 556112790, - "mod": 10}) - self.fakeLogToc.append({"varid": 21, "vartype": 6, - "vargroup": "gps", "varname": "lon", - "min": 129945110, "max": 129945710, - "mod": 10}) - self.fakeLogToc.append({"varid": 22, "vartype": 6, - "vargroup": "gps", "varname": "hMSL", - "min": 0, "max": 100000, - "mod": 1000}) - self.fakeLogToc.append({"varid": 23, "vartype": 6, - "vargroup": "gps", "varname": "heading", - "min": -10000000, "max": 10000000, - "mod": 100000}) - self.fakeLogToc.append({"varid": 24, "vartype": 6, - "vargroup": "gps", "varname": "gSpeed", - "min": 0, "max": 1000, - "mod": 100}) - self.fakeLogToc.append({"varid": 25, "vartype": 3, - "vargroup": "gps", "varname": "hAcc", - "min": 0, "max": 5000, - "mod": 100}) - self.fakeLogToc.append({"varid": 26, "vartype": 1, - "vargroup": "gps", "varname": "fixType", - "min": 0, "max": 5, - "mod": 1}) - - # Fill up the fake logging TOC with values and data - self.fakeParamToc = [] - self.fakeParamToc.append({"varid": 0, "vartype": 0x08, - "vargroup": "blah", "varname": "p", - "writable": True, "value": 100}) - self.fakeParamToc.append({"varid": 1, "vartype": 0x0A, - "vargroup": "info", "varname": "cid", - "writable": False, "value": 1234}) - self.fakeParamToc.append({"varid": 2, "vartype": 0x06, - "vargroup": "rpid", "varname": "prp", - "writable": True, "value": 1.5}) - self.fakeParamToc.append({"varid": 3, "vartype": 0x06, - "vargroup": "rpid", "varname": "pyaw", - "writable": True, "value": 2.5}) - self.fakeParamToc.append({"varid": 4, "vartype": 0x06, - "vargroup": "rpid", "varname": "irp", - "writable": True, "value": 3.5}) - self.fakeParamToc.append({"varid": 5, "vartype": 0x06, - "vargroup": "rpid", "varname": "iyaw", - "writable": True, "value": 4.5}) - self.fakeParamToc.append({"varid": 6, "vartype": 0x06, - "vargroup": "pid_attitude", - "varname": "pitch_kd", "writable": True, - "value": 5.5}) - self.fakeParamToc.append({"varid": 7, "vartype": 0x06, - "vargroup": "rpid", "varname": "dyaw", - "writable": True, "value": 6.5}) - self.fakeParamToc.append({"varid": 8, "vartype": 0x06, - "vargroup": "apid", "varname": "prp", - "writable": True, "value": 7.5}) - self.fakeParamToc.append({"varid": 9, "vartype": 0x06, - "vargroup": "apid", "varname": "pyaw", - "writable": True, "value": 8.5}) - self.fakeParamToc.append({"varid": 10, "vartype": 0x06, - "vargroup": "apid", "varname": "irp", - "writable": True, "value": 9.5}) - self.fakeParamToc.append({"varid": 11, "vartype": 0x06, - "vargroup": "apid", "varname": "iyaw", - "writable": True, "value": 10.5}) - self.fakeParamToc.append({"varid": 12, "vartype": 0x06, - "vargroup": "apid", "varname": "drp", - "writable": True, "value": 11.5}) - self.fakeParamToc.append({"varid": 13, "vartype": 0x06, - "vargroup": "apid", "varname": "dyaw", - "writable": True, "value": 12.5}) - self.fakeParamToc.append({"varid": 14, "vartype": 0x08, - "vargroup": "flightctrl", - "varname": "xmode", "writable": True, - "value": 1}) - self.fakeParamToc.append({"varid": 15, "vartype": 0x08, - "vargroup": "flightctrl", - "varname": "ratepid", "writable": True, - "value": 1}) - self.fakeParamToc.append({"varid": 16, "vartype": 0x08, - "vargroup": "imu_sensors", - "varname": "HMC5883L", "writable": False, - "value": 1}) - self.fakeParamToc.append({"varid": 17, "vartype": 0x08, - "vargroup": "imu_sensors", - "varname": "MS5611", "writable": False, - "value": 1}) - self.fakeParamToc.append({"varid": 18, "vartype": 0x0A, - "vargroup": "firmware", - "varname": "revision0", "writable": False, - "value": 0xdeb}) - self.fakeParamToc.append({"varid": 19, "vartype": 0x09, - "vargroup": "firmware", - "varname": "revision1", "writable": False, - "value": 0x99}) - self.fakeParamToc.append({"varid": 20, "vartype": 0x08, - "vargroup": "firmware", - "varname": "modified", "writable": False, - "value": 1}) - self.fakeParamToc.append({"varid": 21, "vartype": 0x08, - "vargroup": "imu_tests", - "varname": "MPU6050", "writable": False, - "value": 1}) - self.fakeParamToc.append({"varid": 22, "vartype": 0x08, - "vargroup": "imu_tests", - "varname": "HMC5883L", "writable": False, - "value": 1}) - self.fakeParamToc.append({"varid": 23, "vartype": 0x08, - "vargroup": "imu_tests", - "varname": "MS5611", "writable": False, - "value": 1}) - - self.fakeflash = {} - self._random_answer_delay = True - self.queue = queue.Queue() - self._packet_handler = _PacketHandlingThread(self.queue, - self.fakeLogToc, - self.fakeParamToc, - self._fake_mems) - self._packet_handler.start() - - def scan_interface(self, address): - return [["debug://0/0", "Normal connection"], - ["debug://0/1", "Fail to connect"], - ["debug://0/2", "Incomplete log TOC download"], - ["debug://0/3", "Insert random delays on replies"], - ["debug://0/4", - "Insert random delays on replies and random TOC CRCs"], - ["debug://0/5", "Normal but random TOC CRCs"], - ["debug://0/6", "Normal but empty I2C and OW mems"]] - - def get_status(self): - return "Ok" - - def get_name(self): - return "debug" - - def connect(self, uri, linkQualityCallback, linkErrorCallback): - - if not re.search("^debug://", uri): - raise WrongUriType("Not a debug URI") - - self._packet_handler.linkErrorCallback = linkErrorCallback - self._packet_handler.linkQualityCallback = linkQualityCallback - - # Debug-options for this driver that - # is set by using different connection URIs - self._packet_handler.inhibitAnswers = False - self._packet_handler.doIncompleteLogTOC = False - self._packet_handler.bootloader = False - self._packet_handler._random_answer_delay = False - self._packet_handler._random_toc_crcs = False - - if (re.search("^debug://.*/1\Z", uri)): - self._packet_handler.inhibitAnswers = True - if (re.search("^debug://.*/110\Z", uri)): - self._packet_handler.bootloader = True - if (re.search("^debug://.*/2\Z", uri)): - self._packet_handler.doIncompleteLogTOC = True - if (re.search("^debug://.*/3\Z", uri)): - self._packet_handler._random_answer_delay = True - if (re.search("^debug://.*/4\Z", uri)): - self._packet_handler._random_answer_delay = True - self._packet_handler._random_toc_crcs = True - if (re.search("^debug://.*/5\Z", uri)): - self._packet_handler._random_toc_crcs = True - - if len(self._fake_mems) == 0: - # Add empty EEPROM - self._fake_mems.append(FakeMemory(type=0, size=100, addr=0)) - # Add EEPROM with settings - self._fake_mems.append( - FakeMemory(type=0, size=100, addr=0, - data=[48, 120, 66, 67, 1, 8, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 231, 8, 231, 231, 231, 218])) - # Add 1-wire memory with settings for LED-ring - self._fake_mems.append( - FakeMemory(type=1, size=112, addr=0x1234567890ABCDEF, - data=[0xeb, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x44, 0x00, 0x0e, - 0x01, 0x09, 0x62, 0x63, 0x4c, 0x65, 0x64, - 0x52, 0x69, 0x6e, - 0x67, 0x02, 0x01, 0x62, 0x55])) - # Add 1-wire memory with settings for LED-ring but bad CRC - self._fake_mems.append( - FakeMemory(type=1, size=112, addr=0x1234567890ABCDEF, - data=[0xeb, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, - 0x44, 0x00, 0x0e, - 0x01, 0x09, 0x62, 0x63, 0x4c, 0x65, 0x64, - 0x52, 0x69, 0x6e, - 0x67, 0x02, 0x01, 0x62, 0x56])) - # Add empty 1-wire memory - self._fake_mems.append( - FakeMemory(type=1, size=112, addr=0x1234567890ABCDEE, - data=[0x00 for a in range(112)])) - - if (re.search("^debug://.*/6\Z", uri)): - logger.info("------------->Erasing memories on connect") - for m in self._fake_mems: - m.erase() - - self.fakeConsoleThread = None - - if (not self._packet_handler.inhibitAnswers and - not self._packet_handler.bootloader): - self.fakeConsoleThread = FakeConsoleThread(self.queue) - self.fakeConsoleThread.start() - - if (self._packet_handler.linkQualityCallback is not None): - self._packet_handler.linkQualityCallback(0) - - def receive_packet(self, time=0): - if time == 0: - try: - return self.queue.get(False) - except queue.Empty: - return None - elif time < 0: - try: - return self.queue.get(True) - except queue.Empty: - return None - else: - try: - return self.queue.get(True, time) - except queue.Empty: - return None - - def send_packet(self, pk): - self._packet_handler.handle_packet(pk) - - def close(self): - logger.info("Closing debugdriver") - for f in self._packet_handler.fakeLoggingThreads: - f.stop() - if self.fakeConsoleThread: - self.fakeConsoleThread.stop() - - -class _PacketHandlingThread(Thread): - """Thread for handling packets asynchronously""" - - def __init__(self, out_queue, fake_log_toc, fake_param_toc, fake_mems): - Thread.__init__(self) - self.setDaemon(True) - self.queue = out_queue - self.fakeLogToc = fake_log_toc - self.fakeParamToc = fake_param_toc - self._fake_mems = fake_mems - self._in_queue = queue.Queue() - - self.inhibitAnswers = False - self.doIncompleteLogTOC = False - self.bootloader = False - self._random_answer_delay = False - self._random_toc_crcs = False - - self.linkErrorCallback = None - self.linkQualityCallback = None - random.seed(None) - self.fakeLoggingThreads = [] - - self._added_blocks = [] - - self.nowAnswerCounter = 4 - - def handle_packet(self, pk): - self._in_queue.put(pk) - - def run(self): - while (True): - pk = self._in_queue.get(True) - if (self.inhibitAnswers): - self.nowAnswerCounter = self.nowAnswerCounter - 1 - logger.debug( - "Not answering with any data, will send link errori" - " in %d retries", self.nowAnswerCounter) - if (self.nowAnswerCounter == 0): - self.linkErrorCallback("Nothing is answering, and it" - " shouldn't") - else: - if (pk.port == 0xFF): - self._handle_bootloader(pk) - elif (pk.port == CRTPPort.DEBUGDRIVER): - self._handle_debugmessage(pk) - elif (pk.port == CRTPPort.COMMANDER): - pass - elif (pk.port == CRTPPort.LOGGING): - self._handle_logging(pk) - elif (pk.port == CRTPPort.PARAM): - self.handleParam(pk) - elif (pk.port == CRTPPort.MEM): - self._handle_mem_access(pk) - else: - logger.warning( - "Not handling incoming packets on port [%d]", - pk.port) - - def _handle_mem_access(self, pk): - chan = pk.channel - cmd = pk.data[0] - payload = pk.data[1:] - - if chan == 0: # Info channel - p_out = CRTPPacket() - p_out.set_header(CRTPPort.MEM, 0) - if cmd == 1: # Request number of memories - p_out.data = (1, len(self._fake_mems)) - if cmd == 2: - id = payload[0] - logger.info("Getting mem {}".format(id)) - m = self._fake_mems[id] - p_out.data = struct.pack( - '<BBBIQ', 2, id, m.type, m.size, m.addr) - self._send_packet(p_out) - - if chan == 1: # Read channel - id = cmd - addr = struct.unpack("I", payload[0:4])[0] - length = payload[4] - status = 0 - logger.info("MEM: Read {}bytes at 0x{:X} from memory {}".format( - length, addr, id)) - m = self._fake_mems[id] - p_out = CRTPPacket() - p_out.set_header(CRTPPort.MEM, 1) - p_out.data = struct.pack("<BIB", id, addr, status) - p_out.data += struct.pack("B" * length, - *m.data[addr:addr + length]) - self._send_packet(p_out) - - if chan == 2: # Write channel - id = cmd - addr = struct.unpack("I", payload[0:4])[0] - data = payload[4:] - logger.info("MEM: Write {}bytes at 0x{:X} to memory {}".format( - len(data), addr, id)) - m = self._fake_mems[id] - - for i in range(len(data)): - m.data[addr + i] = data[i] - - status = 0 - - p_out = CRTPPacket() - p_out.set_header(CRTPPort.MEM, 2) - p_out.data = struct.pack("<BIB", id, addr, status) - self._send_packet(p_out) - - def _handle_bootloader(self, pk): - cmd = pk.data[1] - if (cmd == 0x10): # Request info about copter - p = CRTPPacket() - p.set_header(0xFF, 0xFF) - pageSize = 1024 - buffPages = 10 - flashPages = 100 - flashStart = 1 - p.data = struct.pack('<BBHHHH', 0xFF, 0x10, pageSize, buffPages, - flashPages, flashStart) - p.data += struct.pack('B' * 12, 0xA0A1A2A3A4A5) - self._send_packet(p) - logging.info("Bootloader: Sending info back info") - elif (cmd == 0x14): # Upload buffer - [page, addr] = struct.unpack('<HH', p.data[0:4]) - elif (cmd == 0x18): # Flash page - p = CRTPPacket() - p.set_header(0xFF, 0xFF) - p.data = struct.pack('<BBH', 0xFF, 0x18, 1) - self._send_packet(p) - elif (cmd == 0xFF): # Reset to firmware - logger.info("Bootloader: Got reset command") - else: - logger.warning("Bootloader: Unknown command 0x%02X", cmd) - - def _handle_debugmessage(self, pk): - if (pk.channel == 0): - cmd = struct.unpack("B", pk.data[0])[0] - if (cmd == 0): # Fake link quality - newLinkQuality = struct.unpack("B", pk.data[1])[0] - self.linkQualityCallback(newLinkQuality) - elif (cmd == 1): - self.linkErrorCallback("DebugDriver was forced to disconnect!") - else: - logger.warning("Debug port: Not handling cmd=%d on channel 0", - cmd) - else: - logger.warning("Debug port: Not handling channel=%d", - pk.channel) - - def _handle_toc_access(self, pk): - chan = pk.channel - cmd = pk.data[0] - logger.info("TOC access on port %d", pk.port) - if (chan == 0): # TOC Access - cmd = pk.data[0] - if (cmd == 0): # Reqest variable info - p = CRTPPacket() - p.set_header(pk.port, 0) - varIndex = 0 - if (len(pk.data) > 1): - varIndex = pk.data[1] - logger.debug("TOC[%d]: Requesting ID=%d", pk.port, - varIndex) - else: - logger.debug("TOC[%d]: Requesting first index..surprise," - " it 0 !", pk.port) - - if (pk.port == CRTPPort.LOGGING): - l = self.fakeLogToc[varIndex] - if (pk.port == CRTPPort.PARAM): - l = self.fakeParamToc[varIndex] - - vartype = l["vartype"] - if (pk.port == CRTPPort.PARAM and l["writable"] is True): - vartype = vartype | (0x10) - - p.data = struct.pack("<BBB", cmd, l["varid"], vartype) - for ch in l["vargroup"]: - p.data.append(ord(ch)) - p.data.append(0) - for ch in l["varname"]: - p.data.append(ord(ch)) - p.data.append(0) - if (self.doIncompleteLogTOC is False): - self._send_packet(p) - elif (varIndex < 5): - self._send_packet(p) - else: - logger.info("TOC: Doing incomplete TOC, stopping after" - " varIndex => 5") - - if (cmd == 1): # TOC CRC32 request - fakecrc = 0 - if (pk.port == CRTPPort.LOGGING): - tocLen = len(self.fakeLogToc) - fakecrc = 0xAAAAAAAA - if (pk.port == CRTPPort.PARAM): - tocLen = len(self.fakeParamToc) - fakecrc = 0xBBBBBBBB - - if self._random_toc_crcs: - fakecrc = int(''.join( - random.choice("ABCDEF" + string.digits) for x in - range(8)), 16) - logger.debug("Generated random TOC CRC: 0x%x", fakecrc) - logger.info("TOC[%d]: Requesting TOC CRC, sending back fake" - " stuff: %d", pk.port, len(self.fakeLogToc)) - p = CRTPPacket() - p.set_header(pk.port, 0) - p.data = struct.pack('<BBIBB', 1, tocLen, fakecrc, 16, 24) - self._send_packet(p) - - def handleParam(self, pk): - chan = pk.channel - cmd = pk.data[0] - logger.debug("PARAM: Port=%d, Chan=%d, cmd=%d", pk.port, - chan, cmd) - if (chan == 0): # TOC Access - self._handle_toc_access(pk) - elif (chan == 2): # Settings access - varId = pk.data[0] - formatStr = ParamTocElement.types[ - self.fakeParamToc[varId]["vartype"]][1] - newvalue = struct.unpack(formatStr, pk.data[1:])[0] - self.fakeParamToc[varId]["value"] = newvalue - logger.info("PARAM: New value [%s] for param [%d]", newvalue, - varId) - # Send back the new value - p = CRTPPacket() - p.set_header(pk.port, 2) - p.data += struct.pack("<B", varId) - p.data += struct.pack(formatStr, self.fakeParamToc[varId]["value"]) - self._send_packet(p) - elif (chan == 1): - p = CRTPPacket() - p.set_header(pk.port, 1) - varId = cmd - p.data.append(varId) - formatStr = ParamTocElement.types[ - self.fakeParamToc[varId]["vartype"]][1] - p.data += struct.pack(formatStr, self.fakeParamToc[varId]["value"]) - logger.info("PARAM: Getting value for %d", varId) - self._send_packet(p) - - def _handle_logging(self, pk): - chan = pk.channel - cmd = pk.data[0] - logger.debug("LOG: Chan=%d, cmd=%d", chan, cmd) - if (chan == 0): # TOC Access - self._handle_toc_access(pk) - elif (chan == 1): # Settings access - if (cmd == 0): - blockId = pk.data[1] - if blockId not in self._added_blocks: - self._added_blocks.append(blockId) - logger.info("LOG:Adding block id=%d", blockId) - listofvars = pk.data[3:] - fakeThread = _FakeLoggingDataThread(self.queue, blockId, - listofvars, - self.fakeLogToc) - self.fakeLoggingThreads.append(fakeThread) - fakeThread.start() - # Anser that everything is ok - p = CRTPPacket() - p.set_header(5, 1) - p.data = struct.pack('<BBB', 0, blockId, 0x00) - self._send_packet(p) - else: - p = CRTPPacket() - p.set_header(5, 1) - p.data = struct.pack('<BBB', 0, blockId, errno.EEXIST) - self._send_packet(p) - if (cmd == 1): - logger.warning("LOG: Appending block not implemented!") - if (cmd == 2): - blockId = pk.data[1] - logger.info("LOG: Should delete block %d", blockId) - success = False - for fb in self.fakeLoggingThreads: - if (fb.blockId == blockId): - fb._disable_logging() - fb.stop() - - p = CRTPPacket() - p.set_header(5, 1) - p.data = struct.pack('<BBB', cmd, blockId, 0x00) - self._send_packet(p) - logger.info("LOG: Deleted block=%d", blockId) - success = True - if (success is False): - logger.warning("LOG: Could not delete block=%d, not found", - blockId) - # TODO: Send back error code - - if (cmd == 3): - blockId = pk.data[1] - period = pk.data[2] * 10 # Sent as multiple of 10 ms - logger.info("LOG:Starting block %d", blockId) - success = False - for fb in self.fakeLoggingThreads: - if (fb.blockId == blockId): - fb._enable_logging() - fb.period = period - p = CRTPPacket() - p.set_header(5, 1) - p.data = struct.pack('<BBB', cmd, blockId, 0x00) - self._send_packet(p) - logger.info("LOG:Started block=%d", blockId) - success = True - if (success is False): - logger.info("LOG:Could not start block=%d, not found", - blockId) - # TODO: Send back error code - if (cmd == 4): - blockId = pk.data[1] - logger.info("LOG:Pausing block %d", blockId) - success = False - for fb in self.fakeLoggingThreads: - if (fb.blockId == blockId): - fb._disable_logging() - p = CRTPPacket() - p.set_header(5, 1) - p.data = struct.pack('<BBB', cmd, blockId, 0x00) - self._send_packet(p) - logger.info("LOG:Pause block=%d", blockId) - success = True - if (success is False): - logger.warning("LOG:Could not pause block=%d, not found", - blockId) - # TODO: Send back error code - if (cmd == 5): - logger.info("LOG: Reset logging, but doing nothing") - p = CRTPPacket() - p.set_header(5, 1) - p.data = struct.pack('<BBB', cmd, 0x00, 0x00) - self._send_packet(p) - - elif (chan > 1): - logger.warning("LOG: Uplink packets with channels > 1 not" - " supported!") - - def _send_packet(self, pk): - # Do not delay log data - if (self._random_answer_delay and pk.port != 0x05 and - pk.channel != 0x02): - # Calculate a delay between 0ms and 250ms - delay = random.randint(0, 250) / 1000.0 - logger.debug("Delaying answer %.2fms", delay * 1000) - time.sleep(delay) - self.queue.put(pk) - - -class _FakeLoggingDataThread(Thread): - """Thread that will send back fake logging data via CRTP""" - - def __init__(self, outQueue, blockId, listofvars, fakeLogToc): - Thread.__init__(self) - self.starttime = datetime.now() - self.outQueue = outQueue - self.setDaemon(True) - self.mod = 0 - self.blockId = blockId - self.period = 0 - self.listofvars = listofvars - self.shouldLog = False - self.fakeLogToc = fakeLogToc - self.fakeLoggingData = [] - self.setName("Fakelog block=%d" % blockId) - self.shouldQuit = False - - logging.info("FakeDataLoggingThread created for blockid=%d", blockId) - i = 0 - while (i < len(listofvars)): - varType = listofvars[i] - var_stored_as = (varType >> 8) - var_fetch_as = (varType & 0xFF) - if (var_stored_as > 0): - addr = struct.unpack("<I", listofvars[i + 1:i + 5]) - logger.debug("FakeLoggingThread: We should log a memory addr" - " 0x%04X", addr) - self.fakeLoggingData.append([memlogging[var_fetch_as], - memlogging[var_fetch_as]["min"], - 1]) - i = i + 5 - else: - varId = listofvars[i] - logger.debug("FakeLoggingThread: We should log variable from" - " TOC: id=%d, type=0x%02X", varId, varType) - for t in self.fakeLogToc: - if (varId == t["varid"]): - # Each touple will have var data and current fake value - self.fakeLoggingData.append([t, t["min"], 1]) - i = i + 2 - - def _enable_logging(self): - self.shouldLog = True - logging.info("_FakeLoggingDataThread: Enable thread [%s] at period %d", - self.getName(), self.period) - - def _disable_logging(self): - self.shouldLog = False - logging.info("_FakeLoggingDataThread: Disable thread [%s]", - self.getName()) - - def stop(self): - self.shouldQuit = True - - def run(self): - while (self.shouldQuit is False): - if (self.shouldLog is True): - - p = CRTPPacket() - p.set_header(5, 2) - p.data = struct.pack('<B', self.blockId) - timestamp = int( - (datetime.now() - self.starttime).total_seconds() * 1000) - p.data += struct.pack('BBB', timestamp & 0xff, - (timestamp >> 8) & 0x0ff, - (timestamp >> 16) & 0x0ff) # Timestamp - - for d in self.fakeLoggingData: - # Set new value - d[1] = d[1] + d[0]["mod"] * d[2] - # Obej the limitations - if (d[1] > d[0]["max"]): - d[1] = d[0]["max"] # Limit value - d[2] = -1 # Switch direction - if (d[1] < d[0]["min"]): - d[1] = d[0]["min"] # Limit value - d[2] = 1 # Switch direction - # Pack value - formatStr = LogTocElement.types[d[0]["vartype"]][1] - p.data += struct.pack(formatStr, d[1]) - self.outQueue.put(p) - time.sleep(self.period / 1000.0) # Period in ms here - - -class FakeConsoleThread(Thread): - """Thread that will send back fake console data via CRTP""" - - def __init__(self, outQueue): - Thread.__init__(self) - self.outQueue = outQueue - self.setDaemon(True) - self._should_run = True - - def stop(self): - self._shoud_run = False - - def run(self): - # Temporary hack to test GPS from firmware by sending NMEA string - # on console - long_val = 0 - lat_val = 0 - alt_val = 0 - - while (self._should_run): - long_val += 1 - lat_val += 1 - alt_val += 1.0 - - long_string = "5536.677%d" % (long_val % 99) - lat_string = "01259.645%d" % (lat_val % 99) - alt_string = "%.1f" % (alt_val % 100.0) - - # Copy of what is sent from the module, but note that only - # the GPGGA message is being simulated, the others are fixed... - self._send_text("Time is now %s\n" % datetime.now()) - self._send_text("$GPVTG,,T,,M,0.386,N,0.716,K,A*2E\n") - self._send_text("$GPGGA,135544.0") - self._send_text("0,%s,N,%s,E,1,04,2.62,3.6,M,%s,M,,*58\n" % ( - long_string, lat_string, alt_string)) - self._send_text( - "$GPGSA,A,3,31,20,23,07,,,,,,,,,3.02,2.62,1.52*05\n") - self._send_text("$GPGSV,2,1,07,07,09,181,15,13,63,219,26,16,02," - "097,,17,05,233,20*7E\n") - self._send_text( - "$GPGSV,2,2,07,20,42,119,35,23,77,097,27,31,12,032,19*47\n") - self._send_text( - "$GPGLL,5536.67734,N,01259.64578,E,135544.00,A,A*68\n") - - time.sleep(2) - - def _send_text(self, message): - p = CRTPPacket() - p.set_header(0, 0) - - us = "%is" % len(message) - # This might be done prettier ;-) - p.data = message - - self.outQueue.put(p) diff --git a/src/cflib/cflib/crtp/exceptions.py b/src/cflib/cflib/crtp/exceptions.py deleted file mode 100644 index 876f9ee000e4e96560291cd0956899c7611ef9f5..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/exceptions.py +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Exception used when the URI is not for the current driver -(ie. radio:// for the serial driver ...) -It basically means that an other driver could do the job -It does NOT means that the URI is good or bad -""" - -__author__ = 'Bitcraze AB' -__all__ = ['WrongUriType', 'CommunicationException'] - - -class WrongUriType(Exception): - """ Wrong type of URI for this interface """ - pass - - -class CommunicationException(Exception): - """ Communication problem when communicating with a Crazyflie """ - pass diff --git a/src/cflib/cflib/crtp/radiodriver.py b/src/cflib/cflib/crtp/radiodriver.py deleted file mode 100644 index 019d67cbcf2416ad60ef17e1c002c23219ec7d2a..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/radiodriver.py +++ /dev/null @@ -1,482 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Crazyradio CRTP link driver. - -This driver is used to communicate with the Crazyflie using the Crazyradio -USB dongle. -""" -import array -import binascii -import collections -import logging -import re -import struct -import sys -import threading - -from cflib.crtp.crtpdriver import CRTPDriver -from cflib.drivers.crazyradio import Crazyradio -from usb import USBError - -from .crtpstack import CRTPPacket -from .exceptions import WrongUriType - -if sys.version_info < (3,): - import Queue as queue -else: - import queue - - -__author__ = 'Bitcraze AB' -__all__ = ['RadioDriver'] - -logger = logging.getLogger(__name__) - - -class RadioDriver(CRTPDriver): - """ Crazyradio link driver """ - - def __init__(self): - """ Create the link driver """ - CRTPDriver.__init__(self) - self.cradio = None - self.uri = "" - self.link_error_callback = None - self.link_quality_callback = None - self.in_queue = None - self.out_queue = None - self._thread = None - self.needs_resending = True - - def connect(self, uri, link_quality_callback, link_error_callback): - """ - Connect the link driver to a specified URI of the format: - radio://<dongle nbr>/<radio channel>/[250K,1M,2M] - - The callback for linkQuality can be called at any moment from the - driver to report back the link quality in percentage. The - callback from linkError will be called when a error occurs with - an error message. - """ - - # check if the URI is a radio URI - if not re.search("^radio://", uri): - raise WrongUriType("Not a radio URI") - - # Open the USB dongle - if not re.search("^radio://([0-9]+)((/([0-9]+))" - "((/(250K|1M|2M))?(/([A-F0-9]+))?)?)?$", uri): - raise WrongUriType('Wrong radio URI format!') - - uri_data = re.search("^radio://([0-9]+)((/([0-9]+))" - "((/(250K|1M|2M))?(/([A-F0-9]+))?)?)?$", uri) - - self.uri = uri - - channel = 2 - if uri_data.group(4): - channel = int(uri_data.group(4)) - - datarate = Crazyradio.DR_2MPS - if uri_data.group(7) == "250K": - datarate = Crazyradio.DR_250KPS - if uri_data.group(7) == "1M": - datarate = Crazyradio.DR_1MPS - if uri_data.group(7) == "2M": - datarate = Crazyradio.DR_2MPS - - if self.cradio is None: - self.cradio = Crazyradio(devid=int(uri_data.group(1))) - else: - raise Exception("Link already open!") - - if self.cradio.version >= 0.4: - self.cradio.set_arc(10) - else: - logger.warning("Radio version <0.4 will be obsoleted soon!") - - self.cradio.set_channel(channel) - - self.cradio.set_data_rate(datarate) - - if uri_data.group(9): - addr = str(uri_data.group(9)) - new_addr = struct.unpack("<BBBBB", binascii.unhexlify(addr)) - self.cradio.set_address(new_addr) - - # Prepare the inter-thread communication queue - self.in_queue = queue.Queue() - # Limited size out queue to avoid "ReadBack" effect - self.out_queue = queue.Queue(1) - - # Launch the comm thread - self._thread = _RadioDriverThread(self.cradio, self.in_queue, - self.out_queue, - link_quality_callback, - link_error_callback, - self) - self._thread.start() - - self.link_error_callback = link_error_callback - - def receive_packet(self, time=0): - """ - Receive a packet though the link. This call is blocking but will - timeout and return None if a timeout is supplied. - """ - if time == 0: - try: - return self.in_queue.get(False) - except queue.Empty: - return None - elif time < 0: - try: - return self.in_queue.get(True) - except queue.Empty: - return None - else: - try: - return self.in_queue.get(True, time) - except queue.Empty: - return None - - def send_packet(self, pk): - """ Send the packet pk though the link """ - # if self.out_queue.full(): - # self.out_queue.get() - if (self.cradio is None): - return - - try: - self.out_queue.put(pk, True, 2) - except queue.Full: - if self.link_error_callback: - self.link_error_callback("RadioDriver: Could not send packet" - " to copter") - - def pause(self): - self._thread.stop() - self._thread = None - - def restart(self): - if self._thread: - return - - self._thread = _RadioDriverThread(self.cradio, self.in_queue, - self.out_queue, - self.link_quality_callback, - self.link_error_callback, - self) - self._thread.start() - - def close(self): - """ Close the link. """ - # Stop the comm thread - self._thread.stop() - - # Close the USB dongle - try: - if self.cradio: - self.cradio.close() - except: - # If we pull out the dongle we will not make this call - pass - self.cradio = None - - while not self.out_queue.empty(): - self.out_queue.get() - - # Clear callbacks - self.link_error_callback = None - self.link_quality_callback = None - - def _scan_radio_channels(self, start=0, stop=125): - """ Scan for Crazyflies between the supplied channels. """ - return list(self.cradio.scan_channels(start, stop, (0xff,))) - - def scan_selected(self, links): - to_scan = () - for l in links: - one_to_scan = {} - uri_data = re.search("^radio://([0-9]+)((/([0-9]+))" - "(/(250K|1M|2M))?)?$", - l) - - one_to_scan["channel"] = int(uri_data.group(4)) - - datarate = Crazyradio.DR_2MPS - if uri_data.group(6) == "250K": - datarate = Crazyradio.DR_250KPS - if uri_data.group(6) == "1M": - datarate = Crazyradio.DR_1MPS - if uri_data.group(6) == "2M": - datarate = Crazyradio.DR_2MPS - - one_to_scan["datarate"] = datarate - - to_scan += (one_to_scan,) - - found = self.cradio.scan_selected(to_scan, (0xFF, 0xFF, 0xFF)) - - ret = () - for f in found: - dr_string = "" - if f["datarate"] == Crazyradio.DR_2MPS: - dr_string = "2M" - if f["datarate"] == Crazyradio.DR_250KPS: - dr_string = "250K" - if f["datarate"] == Crazyradio.DR_1MPS: - dr_string = "1M" - - ret += ("radio://0/{}/{}".format(f["channel"], dr_string),) - - return ret - - def scan_interface(self, address): - """ Scan interface for Crazyflies """ - if self.cradio is None: - try: - self.cradio = Crazyradio() - except Exception: - return [] - else: - raise Exception("Cannot scann for links while the link is open!") - - # FIXME: implements serial number in the Crazyradio driver! - serial = "N/A" - - logger.info("v%s dongle with serial %s found", self.cradio.version, - serial) - found = [] - - if address is not None: - addr = "{:X}".format(address) - new_addr = struct.unpack("<BBBBB", binascii.unhexlify(addr)) - self.cradio.set_address(new_addr) - - self.cradio.set_arc(1) - - self.cradio.set_data_rate(self.cradio.DR_250KPS) - - if address is None or address == 0xE7E7E7E7E7: - found += [["radio://0/{}/250K".format(c), ""] - for c in self._scan_radio_channels()] - self.cradio.set_data_rate(self.cradio.DR_1MPS) - found += [["radio://0/{}/1M".format(c), ""] - for c in self._scan_radio_channels()] - self.cradio.set_data_rate(self.cradio.DR_2MPS) - found += [["radio://0/{}/2M".format(c), ""] - for c in self._scan_radio_channels()] - else: - found += [["radio://0/{}/250K/{:X}".format(c, address), ""] - for c in self._scan_radio_channels()] - self.cradio.set_data_rate(self.cradio.DR_1MPS) - found += [["radio://0/{}/1M/{:X}".format(c, address), ""] - for c in self._scan_radio_channels()] - self.cradio.set_data_rate(self.cradio.DR_2MPS) - found += [["radio://0/{}/2M/{:X}".format(c, address), ""] - for c in self._scan_radio_channels()] - - self.cradio.close() - self.cradio = None - - return found - - def get_status(self): - if self.cradio is None: - try: - self.cradio = Crazyradio() - except USBError as e: - return "Cannot open Crazyradio. Permission problem?" \ - " ({})".format(str(e)) - except Exception as e: - return str(e) - - ver = self.cradio.version - self.cradio.close() - self.cradio = None - - return "Crazyradio version {}".format(ver) - - def get_name(self): - return "radio" - - -# Transmit/receive radio thread -class _RadioDriverThread(threading.Thread): - """ - Radio link receiver thread used to read data from the - Crazyradio USB driver. """ - - RETRYCOUNT_BEFORE_DISCONNECT = 10 - - def __init__(self, cradio, inQueue, outQueue, link_quality_callback, - link_error_callback, link): - """ Create the object """ - threading.Thread.__init__(self) - self.cradio = cradio - self.in_queue = inQueue - self.out_queue = outQueue - self.sp = False - self.link_error_callback = link_error_callback - self.link_quality_callback = link_quality_callback - self.retryBeforeDisconnect = self.RETRYCOUNT_BEFORE_DISCONNECT - self.retries = collections.deque() - self.retry_sum = 0 - - self.curr_up = 0 - self.curr_down = 1 - - self.has_safelink = False - self._link = link - - def stop(self): - """ Stop the thread """ - self.sp = True - try: - self.join() - except Exception: - pass - - def _send_packet_safe(self, cr, packet): - """ - Adds 1bit counter to CRTP header to guarantee that no ack (downlink) - payload are lost and no uplink packet are duplicated. - The caller should resend packet if not acked (ie. same as with a - direct call to crazyradio.send_packet) - """ - packet = bytearray(packet) - packet[0] &= 0xF3 - packet[0] |= self.curr_up << 3 | self.curr_down << 2 - resp = cr.send_packet(packet) - if resp and resp.ack and len(resp.data) and \ - (resp.data[0] & 0x04) == (self.curr_down << 2): - self.curr_down = 1 - self.curr_down - if resp and resp.ack: - self.curr_up = 1 - self.curr_up - - return resp - - def run(self): - """ Run the receiver thread """ - dataOut = array.array('B', [0xFF]) - waitTime = 0 - emptyCtr = 0 - - # Try up to 10 times to enable the safelink mode - for _ in range(10): - resp = self.cradio.send_packet((0xff, 0x05, 0x01)) - if resp and resp.data and tuple(resp.data) == (0xff, 0x05, 0x01): - self.has_safelink = True - self.curr_up = 0 - self.curr_down = 0 - break - logging.info("Has safelink: {}".format(self.has_safelink)) - self._link.needs_resending = not self.has_safelink - - while (True): - if (self.sp): - break - - try: - if self.has_safelink: - ackStatus = self._send_packet_safe(self.cradio, dataOut) - else: - ackStatus = self.cradio.send_packet(dataOut) - except Exception as e: - import traceback - - self.link_error_callback( - "Error communicating with crazy radio ,it has probably " - "been unplugged!\nException:%s\n\n%s" % ( - e, traceback.format_exc())) - - # Analise the in data packet ... - if ackStatus is None: - if (self.link_error_callback is not None): - self.link_error_callback("Dongle communication error" - " (ackStatus==None)") - continue - - if (self.link_quality_callback is not None): - # track the mean of a sliding window of the last N packets - retry = 10 - ackStatus.retry - self.retries.append(retry) - self.retry_sum += retry - if len(self.retries) > 100: - self.retry_sum -= self.retries.popleft() - link_quality = float(self.retry_sum) / len(self.retries) * 10 - self.link_quality_callback(link_quality) - - # If no copter, retry - if ackStatus.ack is False: - self.retryBeforeDisconnect = self.retryBeforeDisconnect - 1 - if (self.retryBeforeDisconnect == 0 and - self.link_error_callback is not None): - self.link_error_callback("Too many packets lost") - continue - self.retryBeforeDisconnect = self.RETRYCOUNT_BEFORE_DISCONNECT - - data = ackStatus.data - - # If there is a copter in range, the packet is analysed and the - # next packet to send is prepared - if (len(data) > 0): - inPacket = CRTPPacket(data[0], list(data[1:])) - # print "<- " + inPacket.__str__() - self.in_queue.put(inPacket) - waitTime = 0 - emptyCtr = 0 - else: - emptyCtr += 1 - if (emptyCtr > 10): - emptyCtr = 10 - # Relaxation time if the last 10 packet where empty - waitTime = 0.01 - else: - waitTime = 0 - - # get the next packet to send of relaxation (wait 10ms) - outPacket = None - try: - outPacket = self.out_queue.get(True, waitTime) - except queue.Empty: - outPacket = None - - dataOut = array.array('B') - - if outPacket: - # print "-> " + outPacket.__str__() - dataOut.append(outPacket.header) - for X in outPacket.data: - if type(X) == int: - dataOut.append(X) - else: - dataOut.append(ord(X)) - else: - dataOut.append(0xFF) diff --git a/src/cflib/cflib/crtp/serialdriver.py b/src/cflib/cflib/crtp/serialdriver.py deleted file mode 100644 index 29d5262774a7b12202adedc203e98e062ef7ec59..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/serialdriver.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -An early serial link driver. This could still be used (after some fixing) to -run high-speed CRTP with the Crazyflie. The UART can be run at 2Mbit. -""" -import re - -from .crtpdriver import CRTPDriver -from .exceptions import WrongUriType - -__author__ = 'Bitcraze AB' -__all__ = ['SerialDriver'] - - -class SerialDriver(CRTPDriver): - - def __init__(self): - None - - def connect(self, uri, linkQualityCallback, linkErrorCallback): - # check if the URI is a serial URI - if not re.search("^serial://", uri): - raise WrongUriType("Not a serial URI") - - # Check if it is a valid serial URI - uriRe = re.search("^serial://([a-z A-Z 0-9]+)/?([0-9]+)?$", uri) - if not uriRe: - raise Exception("Invalid serial URI") - - def get_name(self): - return "serial" - - def scan_interface(self, address): - return [] diff --git a/src/cflib/cflib/crtp/udpdriver.py b/src/cflib/cflib/crtp/udpdriver.py deleted file mode 100644 index 8f1fb82d0eba9d67c5c5b1e2d65aa7cbdc84e058..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/udpdriver.py +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" CRTP UDP Driver. Work either with the UDP server or with an UDP device -See udpserver.py for the protocol""" -import re -import struct -import sys -from socket import socket - -from .crtpdriver import CRTPDriver -from .crtpstack import CRTPPacket -from .exceptions import WrongUriType -if sys.version_info < (3,): - import Queue as queue -else: - import queue - -__author__ = 'Bitcraze AB' -__all__ = ['UdpDriver'] - - -class UdpDriver(CRTPDriver): - - def __init__(self): - None - - def connect(self, uri, linkQualityCallback, linkErrorCallback): - # check if the URI is a radio URI - if not re.search("^udp://", uri): - raise WrongUriType("Not an UDP URI") - - self.queue = queue.Queue() - self.socket = socket(socket.AF_INET, socket.SOCK_DGRAM) - self.addr = ("localhost", 7777) - self.socket.connect(self.addr) - - # Add this to the server clients list - self.socket.sendto("\xFF\x01\x01\x01", self.addr) - - def receive_packet(self, time=0): - data, addr = self.socket.recvfrom(1024) - - if data: - data = struct.unpack('b' * (len(data) - 1), data[0:len(data) - 1]) - pk = CRTPPacket() - pk.port = data[0] - pk.data = data[1:] - return pk - - try: - if time == 0: - return self.rxqueue.get(False) - elif time < 0: - while True: - return self.rxqueue.get(True, 10) - else: - return self.rxqueue.get(True, time) - except queue.Empty: - return None - - def send_packet(self, pk): - raw = (pk.port,) + struct.unpack("B" * len(pk.data), pk.data) - - cksum = 0 - for i in raw: - cksum += i - - cksum %= 256 - - data = ''.join(chr(v) for v in (raw + (cksum,))) - - # print tuple(data) - self.socket.sendto(data, self.addr) - - def close(self): - # Remove this from the server clients list - self.socket.sendto("\xFF\x01\x02\x02", self.addr) - - def get_name(self): - return "udp" - - def scan_interface(self, address): - return [] diff --git a/src/cflib/cflib/crtp/usbdriver.py b/src/cflib/cflib/crtp/usbdriver.py deleted file mode 100644 index 083d9fad8e5f97c42c6ab1971f353cbdd4b0292c..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/crtp/usbdriver.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Crazyflie USB driver. - -This driver is used to communicate with the Crazyflie using the USB connection. -""" - -import logging - -from cflib.crtp.crtpdriver import CRTPDriver -from .crtpstack import CRTPPacket -from .exceptions import WrongUriType -import threading -import sys -import re -import time - -from cflib.drivers.cfusb import CfUsb -from usb import USBError -if sys.version_info < (3,): - import Queue as queue -else: - import queue - -__author__ = 'Bitcraze AB' -__all__ = ['UsbDriver'] - -logger = logging.getLogger(__name__) - - -class UsbDriver(CRTPDriver): - """ Crazyradio link driver """ - - def __init__(self): - """ Create the link driver """ - CRTPDriver.__init__(self) - self.cfusb = None - self.uri = "" - self.link_error_callback = None - self.link_quality_callback = None - self.in_queue = None - self.out_queue = None - self._thread = None - self.needs_resending = False - - def connect(self, uri, link_quality_callback, link_error_callback): - """ - Connect the link driver to a specified URI of the format: - radio://<dongle nbr>/<radio channel>/[250K,1M,2M] - - The callback for linkQuality can be called at any moment from the - driver to report back the link quality in percentage. The - callback from linkError will be called when a error occues with - an error message. - """ - - # check if the URI is a radio URI - if not re.search("^usb://", uri): - raise WrongUriType("Not a radio URI") - - # Open the USB dongle - if not re.search("^usb://([0-9]+)$", - uri): - raise WrongUriType('Wrong radio URI format!') - - uri_data = re.search("^usb://([0-9]+)$", - uri) - - self.uri = uri - - if self.cfusb is None: - self.cfusb = CfUsb(devid=int(uri_data.group(1))) - if self.cfusb.dev: - self.cfusb.set_crtp_to_usb(True) - else: - self.cfusb = None - raise Exception("Could not open {}".format(self.uri)) - - else: - raise Exception("Link already open!") - - # Prepare the inter-thread communication queue - self.in_queue = queue.Queue() - # Limited size out queue to avoid "ReadBack" effect - self.out_queue = queue.Queue(50) - - # Launch the comm thread - self._thread = _UsbReceiveThread(self.cfusb, self.in_queue, - link_quality_callback, - link_error_callback) - self._thread.start() - - self.link_error_callback = link_error_callback - - def receive_packet(self, time=0): - """ - Receive a packet though the link. This call is blocking but will - timeout and return None if a timeout is supplied. - """ - if time == 0: - try: - return self.in_queue.get(False) - except queue.Empty: - return None - elif time < 0: - try: - return self.in_queue.get(True) - except queue.Empty: - return None - else: - try: - return self.in_queue.get(True, time) - except queue.Empty: - return None - - def send_packet(self, pk): - """ Send the packet pk though the link """ - # if self.out_queue.full(): - # self.out_queue.get() - if (self.cfusb is None): - return - - try: - dataOut = (pk.header,) - dataOut += pk.datat - self.cfusb.send_packet(dataOut) - except queue.Full: - if self.link_error_callback: - self.link_error_callback( - "UsbDriver: Could not send packet to Crazyflie") - - def pause(self): - self._thread.stop() - self._thread = None - - def restart(self): - if self._thread: - return - - self._thread = _UsbReceiveThread(self.cfusb, self.in_queue, - self.link_quality_callback, - self.link_error_callback) - self._thread.start() - - def close(self): - """ Close the link. """ - # Stop the comm thread - self._thread.stop() - - # Close the USB dongle - try: - if self.cfusb: - self.cfusb.set_crtp_to_usb(False) - self.cfusb.close() - except Exception as e: - # If we pull out the dongle we will not make this call - logger.info("Could not close {}".format(e)) - pass - self.cfusb = None - - def scan_interface(self, address): - """ Scan interface for Crazyflies """ - if self.cfusb is None: - try: - self.cfusb = CfUsb() - except Exception as e: - logger.warn( - "Exception while scanning for Crazyflie USB: {}".format( - str(e))) - return [] - else: - raise Exception("Cannot scan for links while the link is open!") - - # FIXME: implements serial number in the Crazyradio driver! - # serial = "N/A" - - found = self.cfusb.scan() - - self.cfusb.close() - self.cfusb = None - - return found - - def get_status(self): - return "No information available" - - def get_name(self): - return "UsbCdc" - - -# Transmit/receive radio thread -class _UsbReceiveThread(threading.Thread): - """ - Radio link receiver thread used to read data from the - Crazyradio USB driver. """ - - # RETRYCOUNT_BEFORE_DISCONNECT = 10 - - def __init__(self, cfusb, inQueue, link_quality_callback, - link_error_callback): - """ Create the object """ - threading.Thread.__init__(self) - self.cfusb = cfusb - self.in_queue = inQueue - self.sp = False - self.link_error_callback = link_error_callback - self.link_quality_callback = link_quality_callback - - def stop(self): - """ Stop the thread """ - self.sp = True - try: - self.join() - except Exception: - pass - - def run(self): - """ Run the receiver thread """ - - while (True): - if (self.sp): - break - try: - # Blocking until USB data available - data = self.cfusb.receive_packet() - if len(data) > 0: - pk = CRTPPacket(data[0], list(data[1:])) - self.in_queue.put(pk) - except Exception as e: - import traceback - - self.link_error_callback( - "Error communicating with the Crazyflie" - " ,it has probably been unplugged!\n" - "Exception:%s\n\n%s" % (e, - traceback.format_exc())) diff --git a/src/cflib/cflib/drivers/__init__.py b/src/cflib/cflib/drivers/__init__.py deleted file mode 100644 index f074d797a973bcba4f86ae9ed1bcd86112840a72..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/drivers/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Drivers for the link interfaces that can be used by CRTP. -""" diff --git a/src/cflib/cflib/drivers/cfusb.py b/src/cflib/cflib/drivers/cfusb.py deleted file mode 100644 index 8e620abf1900ae56e809db4ce89c728831fdb6e5..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/drivers/cfusb.py +++ /dev/null @@ -1,196 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -USB driver for the Crazyflie. -""" - -import os -import usb -import logging -import sys -import time -import array -import binascii - -__author__ = 'Bitcraze AB' -__all__ = ['CfUsb'] - -logger = logging.getLogger(__name__) - -USB_VID = 0x0483 -USB_PID = 0x5740 - -try: - import usb.core - - pyusb_backend = None - if os.name == "nt": - import usb.backend.libusb0 as libusb0 - - pyusb_backend = libusb0.get_backend() - pyusb1 = True -except: - pyusb1 = False - - -def _find_devices(): - """ - Returns a list of CrazyRadio devices currently connected to the computer - """ - ret = [] - - logger.info("Looking for devices....") - - if pyusb1: - for d in usb.core.find(idVendor=USB_VID, idProduct=USB_PID, find_all=1, - backend=pyusb_backend): - ret.append(d) - else: - busses = usb.busses() - for bus in busses: - for device in bus.devices: - if device.idVendor == USB_VID: - if device.idProduct == USB_PID: - ret += [device, ] - - return ret - - -class CfUsb: - """ Used for communication with the Crazyradio USB dongle """ - - def __init__(self, device=None, devid=0): - """ Create object and scan for USB dongle if no device is supplied """ - self.dev = None - self.handle = None - self._last_write = 0 - self._last_read = 0 - - if device is None: - devices = _find_devices() - try: - self.dev = devices[devid] - except Exception: - self.dev = None - - if self.dev: - if (pyusb1 is True): - self.dev.set_configuration(1) - self.handle = self.dev - self.version = float( - "{0:x}.{1:x}".format(self.dev.bcdDevice >> 8, - self.dev.bcdDevice & 0x0FF)) - else: - self.handle = self.dev.open() - self.handle.setConfiguration(1) - self.handle.claimInterface(0) - self.version = float(self.dev.deviceVersion) - - def get_serial(self): - return usb.util.get_string(self.dev, 255, self.dev.iSerialNumber) - - def close(self): - if (pyusb1 is False): - if self.handle: - self.handle.releaseInterface() - self.handle.reset() - else: - if self.dev: - self.dev.reset() - - self.handle = None - self.dev = None - - def scan(self): - # TODO: Currently only supports one device - if self.dev: - return [("usb://0", "")] - return [] - - def set_crtp_to_usb(self, crtp_to_usb): - if crtp_to_usb: - _send_vendor_setup(self.handle, 0x01, 0x01, 1, ()) - else: - _send_vendor_setup(self.handle, 0x01, 0x01, 0, ()) - - # Data transfers - def send_packet(self, dataOut): - """ Send a packet and receive the ack from the radio dongle - The ack contains information about the packet transmition - and a data payload if the ack packet contained any """ - try: - if (pyusb1 is False): - count = self.handle.bulkWrite(1, dataOut, 20) - else: - count = self.handle.write(endpoint=1, data=dataOut, timeout=20) - except usb.USBError as e: - pass - - def receive_packet(self): - dataIn = () - try: - if (pyusb1 is False): - dataIn = self.handle.bulkRead(0x81, 64, 20) - else: - dataIn = self.handle.read(0x81, 64, timeout=20) - except usb.USBError as e: - try: - if e.backend_error_code == -7 or e.backend_error_code == -116: - # Normal, the read was empty - pass - else: - raise IOError("Crazyflie disconnected") - except AttributeError as e: - # pyusb < 1.0 doesn't implement getting the underlying error - # number and it seems as if it's not possible to detect - # if the cable is disconnected. So this detection is not - # supported, but the "normal" case will work. - pass - - return dataIn - - -# Private utility functions -def _send_vendor_setup(handle, request, value, index, data): - if pyusb1: - handle.ctrl_transfer(usb.TYPE_VENDOR, request, wValue=value, - wIndex=index, timeout=1000, data_or_wLength=data) - else: - handle.controlMsg(usb.TYPE_VENDOR, request, data, value=value, - index=index, timeout=1000) - - -def _get_vendor_setup(handle, request, value, index, length): - if pyusb1: - return handle.ctrl_transfer(usb.TYPE_VENDOR | 0x80, request, - wValue=value, wIndex=index, timeout=1000, - data_or_wLength=length) - else: - return handle.controlMsg(usb.TYPE_VENDOR | 0x80, request, length, - value=value, index=index, timeout=1000) diff --git a/src/cflib/cflib/drivers/crazyradio.py b/src/cflib/cflib/drivers/crazyradio.py deleted file mode 100644 index 9641fa23a0014bcc1c3b143e604a137684225402..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/drivers/crazyradio.py +++ /dev/null @@ -1,292 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -USB driver for the Crazyradio USB dongle. -""" - -import os -import usb -import logging - -__author__ = 'Bitcraze AB' -__all__ = ['Crazyradio'] - -logger = logging.getLogger(__name__) - -# USB parameters -CRADIO_VID = 0x1915 -CRADIO_PID = 0x7777 - -# Dongle configuration requests -# See http://wiki.bitcraze.se/projects:crazyradio:protocol for documentation -SET_RADIO_CHANNEL = 0x01 -SET_RADIO_ADDRESS = 0x02 -SET_DATA_RATE = 0x03 -SET_RADIO_POWER = 0x04 -SET_RADIO_ARD = 0x05 -SET_RADIO_ARC = 0x06 -ACK_ENABLE = 0x10 -SET_CONT_CARRIER = 0x20 -SCANN_CHANNELS = 0x21 -LAUNCH_BOOTLOADER = 0xFF - -try: - import usb.core - - pyusb_backend = None - if os.name == "nt": - import usb.backend.libusb0 as libusb0 - - pyusb_backend = libusb0.get_backend() - pyusb1 = True -except: - pyusb1 = False - - -def _find_devices(): - """ - Returns a list of CrazyRadio devices currently connected to the computer - """ - ret = [] - - if pyusb1: - for d in usb.core.find(idVendor=0x1915, idProduct=0x7777, find_all=1, - backend=pyusb_backend): - ret.append(d) - else: - busses = usb.busses() - for bus in busses: - for device in bus.devices: - if device.idVendor == CRADIO_VID: - if device.idProduct == CRADIO_PID: - ret += [device, ] - - return ret - - -class _radio_ack: - ack = False - powerDet = False - retry = 0 - data = () - - -class Crazyradio: - """ Used for communication with the Crazyradio USB dongle """ - # configuration constants - DR_250KPS = 0 - DR_1MPS = 1 - DR_2MPS = 2 - - P_M18DBM = 0 - P_M12DBM = 1 - P_M6DBM = 2 - P_0DBM = 3 - - def __init__(self, device=None, devid=0): - """ Create object and scan for USB dongle if no device is supplied """ - if device is None: - try: - device = _find_devices()[devid] - except Exception: - raise Exception("Cannot find a Crazyradio Dongle") - - self.dev = device - - if (pyusb1 is True): - self.dev.set_configuration(1) - self.handle = self.dev - self.version = float("{0:x}.{1:x}".format( - self.dev.bcdDevice >> 8, self.dev.bcdDevice & 0x0FF)) - else: - self.handle = self.dev.open() - self.handle.setConfiguration(1) - self.handle.claimInterface(0) - self.version = float(self.dev.deviceVersion) - - if self.version < 0.3: - raise "This driver requires Crazyradio firmware V0.3+" - - if self.version < 0.4: - logger.warning("You should update to Crazyradio firmware V0.4+") - - # Reset the dongle to power up settings - self.set_data_rate(self.DR_2MPS) - self.set_channel(2) - self.arc = -1 - if self.version >= 0.4: - self.set_cont_carrier(False) - self.set_address((0xE7,) * 5) - self.set_power(self.P_0DBM) - self.set_arc(3) - self.set_ard_bytes(32) - - def close(self): - if (pyusb1 is False): - if self.handle: - self.handle.releaseInterface() - self.handle.reset() - else: - if self.dev: - self.dev.reset() - - self.handle = None - self.dev = None - - # Dongle configuration - def set_channel(self, channel): - """ Set the radio channel to be used """ - _send_vendor_setup(self.handle, SET_RADIO_CHANNEL, channel, 0, ()) - - def set_address(self, address): - """ Set the radio address to be used""" - if len(address) != 5: - raise Exception("Crazyradio: the radio address shall be 5" - " bytes long") - - _send_vendor_setup(self.handle, SET_RADIO_ADDRESS, 0, 0, address) - - def set_data_rate(self, datarate): - """ Set the radio datarate to be used """ - _send_vendor_setup(self.handle, SET_DATA_RATE, datarate, 0, ()) - - def set_power(self, power): - """ Set the radio power to be used """ - _send_vendor_setup(self.handle, SET_RADIO_POWER, power, 0, ()) - - def set_arc(self, arc): - """ Set the ACK retry count for radio communication """ - _send_vendor_setup(self.handle, SET_RADIO_ARC, arc, 0, ()) - self.arc = arc - - def set_ard_time(self, us): - """ Set the ACK retry delay for radio communication """ - # Auto Retransmit Delay: - # 0000 - Wait 250uS - # 0001 - Wait 500uS - # 0010 - Wait 750uS - # ........ - # 1111 - Wait 4000uS - - # Round down, to value representing a multiple of 250uS - t = int((us / 250) - 1) - if (t < 0): - t = 0 - if (t > 0xF): - t = 0xF - _send_vendor_setup(self.handle, SET_RADIO_ARD, t, 0, ()) - - def set_ard_bytes(self, nbytes): - _send_vendor_setup(self.handle, SET_RADIO_ARD, 0x80 | nbytes, 0, ()) - - def set_cont_carrier(self, active): - if active: - _send_vendor_setup(self.handle, SET_CONT_CARRIER, 1, 0, ()) - else: - _send_vendor_setup(self.handle, SET_CONT_CARRIER, 0, 0, ()) - - def _has_fw_scan(self): - # return self.version >= 0.5 - # FIXME: Mitigation for Crazyradio firmware bug #9 - return False - - def scan_selected(self, selected, packet): - result = () - for s in selected: - self.set_channel(s["channel"]) - self.set_data_rate(s["datarate"]) - status = self.send_packet(packet) - if status and status.ack: - result = result + (s,) - - return result - - def scan_channels(self, start, stop, packet): - if self._has_fw_scan(): # Fast firmware-driven scan - _send_vendor_setup(self.handle, SCANN_CHANNELS, start, stop, - packet) - return tuple(_get_vendor_setup(self.handle, SCANN_CHANNELS, - 0, 0, 64)) - else: # Slow PC-driven scan - result = tuple() - for i in range(start, stop + 1): - self.set_channel(i) - status = self.send_packet(packet) - if status and status.ack: - result = result + (i,) - return result - - # Data transferts - def send_packet(self, dataOut): - """ Send a packet and receive the ack from the radio dongle - The ack contains information about the packet transmition - and a data payload if the ack packet contained any """ - ackIn = None - data = None - try: - if (pyusb1 is False): - self.handle.bulkWrite(1, dataOut, 1000) - data = self.handle.bulkRead(0x81, 64, 1000) - else: - self.handle.write(endpoint=1, data=dataOut, timeout=1000) - data = self.handle.read(0x81, 64, timeout=1000) - except usb.USBError: - pass - - if data is not None: - ackIn = _radio_ack() - if data[0] != 0: - ackIn.ack = (data[0] & 0x01) != 0 - ackIn.powerDet = (data[0] & 0x02) != 0 - ackIn.retry = data[0] >> 4 - ackIn.data = data[1:] - else: - ackIn.retry = self.arc - - return ackIn - - -# Private utility functions -def _send_vendor_setup(handle, request, value, index, data): - if pyusb1: - handle.ctrl_transfer(usb.TYPE_VENDOR, request, wValue=value, - wIndex=index, timeout=1000, data_or_wLength=data) - else: - handle.controlMsg(usb.TYPE_VENDOR, request, data, value=value, - index=index, timeout=1000) - - -def _get_vendor_setup(handle, request, value, index, length): - if pyusb1: - return handle.ctrl_transfer(usb.TYPE_VENDOR | 0x80, request, - wValue=value, wIndex=index, timeout=1000, - data_or_wLength=length) - else: - return handle.controlMsg(usb.TYPE_VENDOR | 0x80, request, length, - value=value, index=index, timeout=1000) diff --git a/src/cflib/cflib/utils/__init__.py b/src/cflib/cflib/utils/__init__.py deleted file mode 100644 index dd277b671ca49039c8196b9338e923d4dcd3b06e..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/utils/__init__.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -Various utilities that is needed by the cflib. -""" diff --git a/src/cflib/cflib/utils/callbacks.py b/src/cflib/cflib/utils/callbacks.py deleted file mode 100644 index 3102bc630f0d0a5403e5a92134eb193b36391391..0000000000000000000000000000000000000000 --- a/src/cflib/cflib/utils/callbacks.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2011-2013 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -""" -Callback objects used in the Crazyflie library -""" - -__author__ = 'Bitcraze AB' -__all__ = ['Caller'] - - -class Caller(): - """ An object were callbacks can be registered and called """ - - def __init__(self): - """ Create the object """ - self.callbacks = [] - - def add_callback(self, cb): - """ Register cb as a new callback. Will not register duplicates. """ - if ((cb in self.callbacks) is False): - self.callbacks.append(cb) - - def remove_callback(self, cb): - """ Un-register cb from the callbacks """ - self.callbacks.remove(cb) - - def call(self, *args): - """ Call the callbacks registered with the arguments args """ - for cb in self.callbacks: - cb(*args) diff --git a/src/cflib/test/__init__.py b/src/cflib/test/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/cflib/test/crtp/__init__.py b/src/cflib/test/crtp/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/cflib/test/crtp/test_crtpstack.py b/src/cflib/test/crtp/test_crtpstack.py deleted file mode 100644 index 1c45422e5bf1ceef8b546a915b59c12592c195a4..0000000000000000000000000000000000000000 --- a/src/cflib/test/crtp/test_crtpstack.py +++ /dev/null @@ -1,106 +0,0 @@ -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -import unittest - -from cflib.crtp.crtpstack import CRTPPacket - - -class CRTPPacketTest(unittest.TestCase): - - def setUp(self): - self.callback_count = 0 - self.sut = CRTPPacket() - - def test_that_port_and_channle_is_encoded_in_header(self): - # Fixture - self.sut.set_header(2, 1) - - # Test - actual = self.sut.get_header() - - # Assert - expected = 0x2d - self.assertEqual(expected, actual) - - def test_that_port_is_truncated_in_header(self): - # Fixture - port = 0xff - self.sut.set_header(port, 0) - - # Test - actual = self.sut.get_header() - - # Assert - expected = 0xfc - self.assertEqual(expected, actual) - - def test_that_channel_is_truncated_in_header(self): - # Fixture - channel = 0xff - self.sut.set_header(0, channel) - - # Test - actual = self.sut.get_header() - - # Assert - expected = 0x0f - self.assertEqual(expected, actual) - - def test_that_port_and_channel_is_encoded_in_header_when_set_separat(self): - # Fixture - self.sut.port = 2 - self.sut.channel = 1 - - # Test - actual = self.sut.get_header() - - # Assert - expected = 0x2d - self.assertEqual(expected, actual) - - def test_that_default_header_is_set_when_constructed(self): - # Fixture - - # Test - actual = self.sut.get_header() - - # Assert - expected = 0x0c - self.assertEqual(expected, actual) - - def test_that_header_is_set_when_constructed(self): - # Fixture - sut = CRTPPacket(header=0x21) - - # Test - actual = sut.get_header() - - # Assert - self.assertEqual(0x2d, actual) - self.assertEqual(2, sut.port) - self.assertEqual(1, sut.channel) diff --git a/src/cflib/test/utils/__init__.py b/src/cflib/test/utils/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/cflib/test/utils/test_callbacks.py b/src/cflib/test/utils/test_callbacks.py deleted file mode 100644 index bd00a0800bc85edfc48725df8543850470e10994..0000000000000000000000000000000000000000 --- a/src/cflib/test/utils/test_callbacks.py +++ /dev/null @@ -1,97 +0,0 @@ -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. -import unittest - -from cflib.utils.callbacks import Caller - - -class CallerTest(unittest.TestCase): - - def setUp(self): - self.callback_count = 0 - self.sut = Caller() - - def test_that_callback_is_added(self): - # Fixture - - # Test - self.sut.add_callback(self._callback) - - # Assert - self.sut.call() - self.assertEqual(1, self.callback_count) - - def test_that_callback_is_added_only_one_time(self): - # Fixture - - # Test - self.sut.add_callback(self._callback) - self.sut.add_callback(self._callback) - - # Assert - self.sut.call() - self.assertEqual(1, self.callback_count) - - def test_that_multiple_callbacks_are_added(self): - # Fixture - - # Test - self.sut.add_callback(self._callback) - self.sut.add_callback(self._callback2) - - # Assert - self.sut.call() - self.assertEqual(2, self.callback_count) - - def test_that_callback_is_removed(self): - # Fixture - self.sut.add_callback(self._callback) - - # Test - self.sut.remove_callback(self._callback) - - # Assert - self.sut.call() - self.assertEqual(0, self.callback_count) - - def test_that_callback_is_called_with_arguments(self): - # Fixture - self.sut.add_callback(self._callback_with_args) - - # Test - self.sut.call('The token') - - # Assert - self.assertEqual('The token', self.callback_token) - - def _callback(self): - self.callback_count += 1 - - def _callback2(self): - self.callback_count += 1 - - def _callback_with_args(self, token): - self.callback_token = token diff --git a/src/cfloader/__init__.py b/src/cfloader/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7113904b9cd24313e56b13cbbbdfd1f562aae028 --- /dev/null +++ b/src/cfloader/__init__.py @@ -0,0 +1,203 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# || ____ _ __ +# +------+ / __ )(_) /_______________ _____ ___ +# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ +# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ +# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ +# +# Copyright (C) 2011-2013 Bitcraze AB +# +# Crazyflie Nano Quadcopter Client +# +# 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. + +# Crazy Loader bootloader utility +# Can reset bootload and reset back the bootloader + +import sys +import os + +import cflib.crtp +from cflib.bootloader import Bootloader +from cflib.bootloader.boottypes import BootVersion, TargetTypes, Target + + +def main(): + # Initialise the CRTP link driver + link = None + try: + cflib.crtp.init_drivers() + link = cflib.crtp.get_link_driver("radio://") + except Exception as e: + print("Error: {}".format(str(e))) + if link: + link.close() + sys.exit(-1) + + # Set the default parameters + # Default to Arnaud's copter + cpu_id = "32:00:6e:06:58:37:35:32:60:58:01:43" + clink = None + action = "info" + boot = "cold" + + if len(sys.argv) < 2: + print() + print("==============================") + print(" CrazyLoader Flash Utility") + print("==============================") + print() + print(" Usage:", sys.argv[0], "[CRTP options] <action> [parameters]") + print() + print("The CRTP options are described above") + print() + print("Crazyload option:") + print(" info : Print the info of the bootloader " + "and quit.") + print(" Will let the target in bootloader " + "mode") + print(" reset : Reset the device in firmware mode") + print(" flash <file> [targets] : flash the <img> binary file from " + "the first") + print(" possible page in flash and reset " + "to firmware") + print(" mode.") + if link: + link.close() + sys.exit(0) + + # Analyse the command line parameters + sys.argv = sys.argv[1:] + argv = [] + + warm_uri = None + + i = 0 + while i < len(sys.argv): + if sys.argv[i] == "-i": + i += 1 + cpu_id = sys.argv[i] + elif sys.argv[i] == "--cold-boot" or sys.argv[i] == "-c": + boot = "cold" + elif sys.argv[i] == "--warm-boot" or sys.argv[i] == "-w": + boot = "reset" + i += 1 + clink = sys.argv[i] + else: + argv += [sys.argv[i]] + i += 1 + sys.argv = argv + + # Analyse the command + if len(sys.argv) < 1: + action = "info" + elif sys.argv[0] == "info": + action = "info" + elif sys.argv[0] == "reset": + action = "reset" + elif sys.argv[0] == "flash": + # print len(sys.argv) + if len(sys.argv) < 2: + print("The flash action require a file name.") + link.close() + sys.exit(-1) + action = "flash" + filename = sys.argv[1] + targetnames = {} + for t in sys.argv[2:]: + [target, type] = t.split("-") + if target in targetnames: + targetnames[target] += (type,) + else: + targetnames[target] = (type,) + else: + print("Action", sys.argv[0], "unknown!") + link.close() + sys.exit(-1) + + # Currently there's two different targets available + targets = () + + try: + # Initialise the bootloader lib + bl = Bootloader(clink) + + ######################################### + # Get the connection with the bootloader + ######################################### + # The connection is done by reseting to the bootloader (default) + if boot == "reset": + print("Reset to bootloader mode ..."), + sys.stdout.flush() + if bl.start_bootloader(warm_boot=True): + print(" done!") + else: + print("Failed to warmboot") + bl.close() + sys.exit(-1) + else: # The connection is done by a cold boot ... + print("Restart the Crazyflie you want to bootload in the next"), + print(" 10 seconds ..."), + + sys.stdout.flush() + if bl.start_bootloader(warm_boot=False): + print(" done!") + else: + print("Cannot connect the bootloader!") + bl.close() + sys.exit(-1) + + print("Connected to bootloader on {} (version=0x{:X})".format( + BootVersion.to_ver_string(bl.protocol_version), + bl.protocol_version)) + + if bl.protocol_version == BootVersion.CF2_PROTO_VER: + targets += (bl.get_target(TargetTypes.NRF51),) + targets += (bl.get_target(TargetTypes.STM32),) + + ###################################### + # Doing something (hopefully) useful + ###################################### + + # Print information about the targets + for target in targets: + print(target) + if action == "info": + None # Already done ... + elif action == "reset": + print + print("Reset in firmware mode ...") + bl.reset_to_firmware() + elif action == "flash": + bl.flash(filename, targetnames) + print("Reset in firmware mode ...") + bl.reset_to_firmware() + else: + None + except Exception as e: + import traceback + + traceback.print_exc(file=sys.stdout) + print(e) + + finally: + ######################### + # Closing the connection + ######################### + if bl: + bl.close() diff --git a/src/cfloader/__main__.py b/src/cfloader/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..b48e88c16bd98d4344f120ef78ca50605a006e1d --- /dev/null +++ b/src/cfloader/__main__.py @@ -0,0 +1,4 @@ +from cfloader import main + +if __name__ == "__main__": + main() diff --git a/src/cfzmq.py b/src/cfzmq/__init__.py similarity index 98% rename from src/cfzmq.py rename to src/cfzmq/__init__.py index aa7bd0f19d0a22d3f7520acdd96b8702b67a6f34..c336f6604a02dc6022ae5a9f2282cb07ca1098c1 100644 --- a/src/cfzmq.py +++ b/src/cfzmq/__init__.py @@ -40,6 +40,8 @@ import cflib.crtp from cflib.crazyflie import Crazyflie from cflib.crazyflie.log import LogConfig +import cfclient + if os.name == 'posix': print('Disabling standard output for libraries!') stdout = os.dup(1) @@ -318,8 +320,8 @@ class ZMQServer(): def __init__(self, base_url): """Start threads and bind ports""" cflib.crtp.init_drivers(enable_debug_driver=True) - self._cf = Crazyflie(ro_cache=sys.path[0] + "/cflib/cache", - rw_cache=sys.path[1] + "/cache") + self._cf = Crazyflie(ro_cache=None, + rw_cache=cfclient.config_path + "/cache") signal.signal(signal.SIGINT, signal.SIG_DFL) @@ -368,3 +370,6 @@ def main(): ZMQServer(args.url) # CRTL-C to exit + +if __name__ == "__main__": + main() diff --git a/src/cfzmq/__main__.py b/src/cfzmq/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..6ba7f561c929f5e5fc86d6ae3af0429152bdf91a --- /dev/null +++ b/src/cfzmq/__main__.py @@ -0,0 +1,4 @@ +from cfzmq import main + +if __name__ == "__main__": + main() diff --git a/src/leapsdk/__init__.py b/src/leapsdk/__init__.py deleted file mode 100644 index c8c34c858acc3dda7dcfff26944fa8f1ee86ba22..0000000000000000000000000000000000000000 --- a/src/leapsdk/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# -*- coding: utf-8 -*- -# -# || ____ _ __ -# +------+ / __ )(_) /_______________ _____ ___ -# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ -# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ -# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ -# -# Copyright (C) 2014 Bitcraze AB -# -# Crazyflie Nano Quadcopter Client -# -# 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. - -""" -In order to use the Leap Motion as an input device reader the libraries from -the Leap Motion SDK has to be placed in this directory. This can be done by -downloading the SDK from the http://developer.leapmotion.com website, unpacking -it and copying the following files: - - * LeapSDK\lib\Leap.py -> leapsdk\Leap.py - * LeapSDK\lib\<your_arch>\LeapPython.so -> leapsdk\LeapPython.so - * LeapSDK\lib\<your_arch>\libLeap.so -> leapsdk\libLeap.so -""" diff --git a/tools/build/bdist b/tools/build/bdist new file mode 100755 index 0000000000000000000000000000000000000000..ed4f310909c5a426c0fedd507470fa8cfabd5eaa --- /dev/null +++ b/tools/build/bdist @@ -0,0 +1,17 @@ +#!/usr/bin/env python +import os +import subprocess +import os.path as _path + +__author__ = 'arnaud' + +try: + script_dir = os.path.dirname(os.path.realpath(__file__)) + root = _path.normpath(_path.join(script_dir, '../..')) + + subprocess.check_call(['python3', 'setup.py', 'bdist_wheel'], cwd=root) + + print("Wheel built") +except subprocess.CalledProcessError as e: + print("Error: Cannot build the wheel") + raise e diff --git a/tools/build/build b/tools/build/build index 393ccc3d2efd160003dc907b15b5baaed8c35810..752479831d2cbd12c640439d46cd0086af1b9395 100755 --- a/tools/build/build +++ b/tools/build/build @@ -5,4 +5,4 @@ import subprocess script_dir = os.path.dirname(os.path.realpath(__file__)) subprocess.check_call([script_dir + "/pep8"]) -subprocess.check_call([script_dir + "/test"]) +subprocess.check_call([script_dir + "/bdist"]) diff --git a/tools/build/test b/tools/build/test deleted file mode 100755 index facc77f7f6faf6c368ab95be20340b5ccf227f09..0000000000000000000000000000000000000000 --- a/tools/build/test +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env python -import os -import subprocess -import os.path as _path - -try: - script_dir = os.path.dirname(os.path.realpath(__file__)) - cflib = _path.normpath(_path.join(script_dir, '../../src/cflib')) - - subprocess.check_call(['python', '-m', 'unittest', 'discover', cflib]) - - print("Unit tests pass") -except subprocess.CalledProcessError as e: - print("Error: Unit tests fail") - raise e