first
|
@ -0,0 +1,246 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(QGroundControl LANGUAGES C CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel;Coverage")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
add_compile_options(-Wall -Wextra)
|
||||
endif()
|
||||
|
||||
# CMake build type
|
||||
# Debug Release RelWithDebInfo MinSizeRel Coverage
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
# default to release with debug symbols
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
|
||||
endif()
|
||||
|
||||
# Add folder where are supportive functions
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
|
||||
# Configure Qt5 to get necessary variables
|
||||
include(Qt5QGCConfiguration)
|
||||
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "Qt version: ${QT_VERSION}")
|
||||
message(STATUS "Qt spec: ${QT_MKSPEC}")
|
||||
|
||||
set(COMPANY "Mavlink")
|
||||
set(COPYRIGHT "Copyright (c) 2018 QGroundControl. All rights reserved.")
|
||||
set(IDENTIFIER "io.mavlink.qgroundcontrol")
|
||||
|
||||
add_definitions(
|
||||
-DQGC_APPLICATION_NAME="QGroundControl"
|
||||
-DQGC_ORG_NAME="QGroundControl.org"
|
||||
-DQGC_ORG_DOMAIN="org.qgroundcontrol"
|
||||
)
|
||||
|
||||
include(Git)
|
||||
message(STATUS "QGroundControl version: ${APP_VERSION_STR}")
|
||||
|
||||
#=============================================================================
|
||||
# ccache
|
||||
#
|
||||
option(CCACHE "Use ccache if available" ON)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if (CCACHE AND CCACHE_PROGRAM AND NOT DEFINED ENV{CCACHE_DISABLE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# Compile QML
|
||||
#
|
||||
option(COMPILE_QML "Pre-compile QML files using the Qt Quick compiler." FALSE)
|
||||
add_feature_info(COMPILE_QML COMPILE_QML "Pre-compile QML files using the Qt Quick compiler.")
|
||||
if(COMPILE_QML)
|
||||
find_package(Qt5QuickCompiler)
|
||||
|
||||
set_package_properties(Qt5QuickCompiler PROPERTIES
|
||||
DESCRIPTION "Pre-compile QML files using the Qt Quick compiler."
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# Debug QML
|
||||
#
|
||||
option(DEBUG_QML "Build QGroundControl with QML debugging/profiling support." FALSE)
|
||||
add_feature_info(DEBUG_QML DEBUG_QML "Build QGroundControl with QML debugging/profiling support.")
|
||||
if(DEBUG_QML)
|
||||
message(STATUS "To enable the QML debugger/profiler, run with: '-qmljsdebugger=port:1234'")
|
||||
add_definitions(-DQMLJSDEBUGGER)
|
||||
add_definitions(-DQT_DECLARATIVE_DEBUG)
|
||||
add_definitions(-DQT_QML_DEBUG)
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# GStreamer
|
||||
#
|
||||
find_package(PkgConfig)
|
||||
|
||||
set(GST_DEPENDENCIES
|
||||
gstreamer-1.0>=1.14
|
||||
gstreamer-video-1.0>=1.14
|
||||
gstreamer-gl-1.0>=1.14
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
pkg_check_modules(GST
|
||||
${GST_DEPENDENCIES}
|
||||
)
|
||||
else()
|
||||
pkg_check_modules(GST
|
||||
${GST_DEPENDENCIES}
|
||||
egl
|
||||
)
|
||||
endif()
|
||||
|
||||
if (GST_FOUND)
|
||||
add_definitions(
|
||||
-DQGC_GST_STREAMING
|
||||
)
|
||||
|
||||
option(QGC_GST_MICROHARD_ENABLED "Enable microhard" OFF)
|
||||
option(QGC_GST_TAISYNC_ENABLED "Enable taisyng" OFF)
|
||||
else()
|
||||
if (QGC_GST_MICROHARD_ENABLED OR QGC_GST_TAISYNC_ENABLED)
|
||||
message(FATAL_ERROR "You tried to enable Microhard or Taisync but gstreamer is not found. Make sure to set PKG_CONFIG_EXECUTABLE and/or PKG_CONFIG_PATH properly.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# Qt5
|
||||
#
|
||||
find_package(Qt5 ${QT_VERSION}
|
||||
COMPONENTS
|
||||
Bluetooth
|
||||
Charts
|
||||
Concurrent
|
||||
Core
|
||||
Location
|
||||
Multimedia
|
||||
Network
|
||||
Positioning
|
||||
Quick
|
||||
QuickControls2
|
||||
QuickWidgets
|
||||
OpenGL
|
||||
Sql
|
||||
Svg
|
||||
Test
|
||||
TextToSpeech
|
||||
Widgets
|
||||
Xml
|
||||
REQUIRED
|
||||
HINTS
|
||||
${QT_LIBRARY_HINTS}
|
||||
)
|
||||
|
||||
if(NOT QT_MKSPEC MATCHES "winrt")
|
||||
find_package(Qt5 ${QT_VERSION}
|
||||
COMPONENTS
|
||||
SerialPort
|
||||
REQUIRED
|
||||
HINTS
|
||||
${QT_LIBRARY_HINTS}
|
||||
)
|
||||
endif()
|
||||
|
||||
# Sets the default flags for compilation and linking.
|
||||
include(CompileOptions)
|
||||
|
||||
include_directories(
|
||||
libs/eigen
|
||||
|
||||
libs/libevents
|
||||
|
||||
libs/mavlink/include/mavlink/v2.0
|
||||
libs/mavlink/include/mavlink/v2.0/all
|
||||
libs/mavlink/include/mavlink/v2.0/common
|
||||
|
||||
libs/shapelib
|
||||
)
|
||||
|
||||
add_subdirectory(libs)
|
||||
add_subdirectory(src)
|
||||
|
||||
set(QGC_RESOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qgcimages.qrc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qgcresources.qrc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/qgroundcontrol.qrc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/resources/InstrumentValueIcons/InstrumentValueIcons.qrc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FirmwarePlugin/APM/APMResources.qrc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/FirmwarePlugin/PX4/PX4Resources.qrc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/VideoReceiverApp/qml.qrc
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
# append application icon resource for Windows
|
||||
set(QGC_RESOURCES
|
||||
${QGC_RESOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/windows/QGroundControl.rc)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
list(APPEND QGC_RESOURCES
|
||||
UnitTest.qrc
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
add_library(QGroundControl SHARED ${QGC_RESOURCES})
|
||||
else()
|
||||
add_executable(QGroundControl ${QGC_RESOURCES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(QGroundControl PRIVATE qgc)
|
||||
|
||||
# Files/directories to install
|
||||
install(
|
||||
TARGETS QGroundControl
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
install(
|
||||
DIRECTORY ${CMAKE_SOURCE_DIR}/resources/
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/qgroundcontrol
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_SOURCE_DIR}/deploy/org.mavlink.qgroundcontrol.desktop
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_SOURCE_DIR}/resources/icons/qgroundcontrol.png
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps/
|
||||
RENAME org.mavlink.qgroundcontrol.png
|
||||
)
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/deploy/org.mavlink.qgroundcontrol.metainfo.xml.in
|
||||
${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml
|
||||
@ONLY
|
||||
)
|
||||
install(
|
||||
FILES ${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml
|
||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo/
|
||||
)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
target_link_libraries(QGroundControl PRIVATE Qt5::Test)
|
||||
endif()
|
||||
|
||||
if(NOT QT_MKSPEC MATCHES "winrt")
|
||||
target_link_libraries(QGroundControl
|
||||
PUBLIC
|
||||
Qt5::SerialPort
|
||||
)
|
||||
endif()
|
||||
|
||||
include(QGCDeploy)
|
|
@ -0,0 +1,46 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at lm@qgroundcontrol.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
Thank you for considering to contribute to QGroundControl.
|
||||
|
||||
Contributions must be made under QGroundControl's dual-license system, under GPLv3 and Apache 2.0. This by definition rules out the re-use of any copyleft (e.g. GPL) licensed code. All contributions must be original or from a compatible license (BSD 2/3 clause, MIT, Apache 2.0).
|
||||
|
||||
* https://opensource.org/licenses/gpl-3.0.html
|
||||
* https://opensource.org/licenses/Apache-2.0
|
||||
|
||||
Users of the codebase are free to use it under either license. The dual approach is necessary to be able to offer QGroundControl through the iOS and Android app stores and offers the open source community choice.
|
|
@ -0,0 +1,15 @@
|
|||
# QGroundControl License
|
||||
|
||||
QGroundControl (QGC) is dual-licensed as Apache 2.0 and GPLv3. All contributions have to be made under both licenses (see [CONTRIBUTING](CONTRIBUTING.md)).
|
||||
|
||||
## Apache 2.0 License
|
||||
|
||||
The [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) License is a permissive license which allows QGC to be built and used in any environment, including proprietary applications. It allows QGC to be built for mobile app stores. When building with Apache 2.0 a commercial Qt license is required.
|
||||
|
||||
## GPL v3 License
|
||||
|
||||
The [GPL v3 License](http://www.gnu.org/licenses/gpl-3.0.en.html) is a strong copyleft license. When building QGC under this license the open source version of Qt can be used. Our licensing grants the permission to use a later version of the license, however, contributions have to be made under 3.0.
|
||||
|
||||
## Contact
|
||||
|
||||
If you have questions regarding the licensing, please contact the maintainer Lorenz Meier, [lm@groundcontrol.org].
|
|
@ -0,0 +1,3 @@
|
|||
# Running QGC on CentOS 7
|
||||
|
||||
This information has moved to the [QGroundControl Developer Guide](https://dev.qgroundcontrol.com/en/getting_started/CentOS.html).
|
|
@ -0,0 +1,197 @@
|
|||
# QGroundControl Change Log
|
||||
|
||||
Note: This file only contains high level features or important fixes.
|
||||
|
||||
## 4.1 - Daily build
|
||||
|
||||
### 4.1.2 - Not yet released
|
||||
* Bug: Radio setup - Fix double send of `MAV_CMD_PREFLIGHT_CALIBRATION` causing "Unable to send command" error.
|
||||
|
||||
### 4.1.1 - Stable
|
||||
* Fix TCP link comms
|
||||
|
||||
### 4.1.0
|
||||
|
||||
* Support simple cameras which only support DIGICAM_CONTROL in the Photo/Video control on Fly View.
|
||||
* Load Parameters From File: Support loading parameters which don't currently existing on the vehicle.
|
||||
* Load Parameters From File: Add dialog which shows diff of file and vehicle params. Selective param upload from file.
|
||||
* Video Streaming: New camera control supports capturing individual images from the stream
|
||||
* Fly: Press and hold on arm button will change it to Force Arm. Click again to force arm.
|
||||
* VTOL: General setting for transition distance which affects Plan takeoff, landing pattern creation
|
||||
* VTOL: Much better VTOL support throughout QGC
|
||||
* Maps: Support zoom up to level 23 even if map provider doesn't provide tiles that high
|
||||
* Settings/Mavlink: Add ability to forward mavlink traffic out specified UDP port
|
||||
* Support mavlink terrain protocol which queries gcs for terrain height information. Allows planning missions with TERRAIN\_FRAME.
|
||||
* Fly: New instrument values display/editing support
|
||||
* Plan: Added new VTOL Landing Pattern support
|
||||
* Plan: Much better conversion of missions to KML for 3d visualization/verification of missions
|
||||
* Plan: New Terrain Profile display including terrain collision indications on profile and in patterns (Survey, CorridorScan, etc)
|
||||
* Fly: Rearchitect view and controls within for much better customization support in custom builds
|
||||
|
||||
## 4.0
|
||||
|
||||
## 4.0.9 - Not yet released
|
||||
|
||||
* Don't auto-connect to second Cube Orange/Yellow composite port
|
||||
* Plan: Fix bugs associated with mission commands which specify and altitude but no lat/lon
|
||||
* Fix bug which could prevent view switching from working after altitude mode warning dialog would pop up
|
||||
|
||||
## 4.0.8 - Stable
|
||||
|
||||
* iOS: Modify QGC file storage location to support new Files app
|
||||
* Mobile: Fix Log Replay status bar file selection
|
||||
|
||||
## 4.0.7 - Stable
|
||||
|
||||
* Fix video page sizing
|
||||
* Virtual Joystick: Fix right stick centering. Fix/add support for rover/sub reverse throttle support.
|
||||
* Fix display of multiple ADSB vehicles
|
||||
|
||||
### 4.0.6 - Stable
|
||||
|
||||
* Analyze/Log Download - Fix download on mobile versions of QGC
|
||||
* Fly: Fix problems where Continue Mission and Change Altitude were not available after a Mission Pause.
|
||||
* PX4 Flow: Fix video display problem
|
||||
|
||||
### 4.0.5 - Stable
|
||||
|
||||
* Solo: Fix mission upload failures
|
||||
* Plan: Fix crash when using Create Plan - Survey for fixed wing vehicle
|
||||
|
||||
### 4.0.4
|
||||
|
||||
* Mobile File Save: Fix problem with incorrect file extension being added
|
||||
* Radio Setup: Fix problem with Spektrum bind
|
||||
* Plan/Fly: Bring back waypoint number display in map items
|
||||
|
||||
### 4.0.3
|
||||
|
||||
* Plan: Add setting for takeoff item not required
|
||||
* Plan: Takeoff item must be added prior to allowing other item types to enable
|
||||
* Video: Add low latency mode as optional configuration setting (defaults to false)
|
||||
* ArduPilot: Fix generated list of available firmwares
|
||||
|
||||
### 4.0.2
|
||||
|
||||
* Fix Mavlink V2 protocol negotation based on capability bits
|
||||
* Fix waiting for AUTOPILOT_VERSION response to get capability bits
|
||||
* ArduPilot: Above two fixes make fence/rally support enabling more reliable
|
||||
|
||||
### 4.0.1
|
||||
|
||||
* Fix ArduPilot current mission item tracking in Fly view
|
||||
* Fix ADSB vehicle display
|
||||
* Fix map positioning bug in Plan view
|
||||
* Fix Windows 0xcc000007b startup error causes by incorrect VC runtimes being installed.
|
||||
|
||||
### 4.0.0
|
||||
|
||||
* Added ROI option during manual flight.
|
||||
* Windows: Move builds to 64 bit, Qt 5.12.5
|
||||
* Plan: ROI button will switch to Cancel ROI at appropriate times
|
||||
* Plan: When ROI is selected the flight path lines which are affected by the ROI will change color
|
||||
* ADSB: Added support for connecting to SBS server. Adds support for ADSB data from USB SDR Dongle running 'dump1090 --net' for example.
|
||||
* Toolbar: Scrollable left/right on small screens like phones
|
||||
* Plan View: New create plan UI for initial plan creation
|
||||
* New Corridor editing tools ui. Includes ability to trace polyline by clicking.
|
||||
* New Polygon editing tools ui. Includes ability to trace polygon by clicking.
|
||||
* More performant flight path display algorithm. Mobile builds no longer show limited path length.
|
||||
* ArduPilot: Add Motor Test vehicle setup page
|
||||
* Compass Instrument: Add indicators for Home, COG and Next Waypoint headings.
|
||||
* Log Replay: Support changing speed of playback
|
||||
* Basic object avoidance added to vehicles.
|
||||
* Added ability to set a joystick button to be single action or repeated action while the button is held down.
|
||||
* Rework joysticks. Fixed several issues and updated setup UI.
|
||||
* Adding support for UDP RTP h.265 video streams
|
||||
* For text to speech engine on Linux to English (all messages are in English)
|
||||
* Automated the ingestion of localization from Crowdin
|
||||
* Automated the generation of language resources into the application
|
||||
* Added all languages that come from Crowdin, even if empty.
|
||||
* Allow dynamic language changes
|
||||
* Check and respect camera storage status
|
||||
* QGC now requires Qt 5.11 or greater. The idea is to standardize on Qt 5.12 (LTS). Just waiting for a solution for Windows as Qt dropped support for 32-bit.
|
||||
* New, QtQuick MAVLink Inspector. The basics are already there but it still needs the ability to filter compID.
|
||||
* Fixed application storage location on iOS. It was trying to save things where it could not.
|
||||
* Basic support for secondary, thermal imaging with video streaming. If a camera provides both visual spectrum and thermal imaging, you have the option of displaying both at the same time.
|
||||
* Better handling of fonts for Korean and Chinese locales. QGC now has builtin fonts for Korean (where some unusable font was being used). I still need to know if Chinese will need its own font as well.
|
||||
* ArduPilot: Copter - Add suppor for Simple and Super Simple flight modes
|
||||
* ArduPilot: Flight Mode setup - Switch Options were not showing up for all firmware revs
|
||||
* ArduCopter: Add PID Tuning page to Tuning Setup
|
||||
* ArduPilot: Copter - Advanced Tuning support
|
||||
* ArduPilot: Rover - Frame setup support
|
||||
* ArduPilot: Copter - Update support to 3.5+
|
||||
* ArduPilot: Plane - Update support to 3.8+
|
||||
* ArduPilot: Rover - Update support to 3.4+
|
||||
* ArduPilot: Rework Airframe setup ui
|
||||
* Plan/Pattern: Support named presets to simplify commonly used settings setup. Currently only supported by Survey.
|
||||
* ArduCopter: Handle 3.7 parameter name change from CH#_OPT to RC#_OPTION.
|
||||
* Improved support for flashing/connecting to ChibiOS bootloaders boards.
|
||||
* Making the camera API available to all firmwares, not just PX4.
|
||||
* ArduPilot: Support configurable mavlink stream rates. Available from Settings/Mavlink page.
|
||||
* Major rewrite and bug fix pass through Structure Scan. Previous version had such bad problems that it can no longer be supported. Plans with Structure Scan will need to be recreated. New QGC will not load old Structure Scan plans.
|
||||
|
||||
## 3.5
|
||||
|
||||
### 3.5.5
|
||||
* Fix mavlink message memset which cause wrong commands to be sent on ArduPilot GotoLocation.
|
||||
* Disable Pause when fixed wing is on landing approach.
|
||||
|
||||
### 3.5.4
|
||||
* Update windows drivers
|
||||
* Add support for FMUK66 flashing/connection
|
||||
* Guard against null geometry coming from gstreamer which can cause crashes
|
||||
* Add .apj file selection support to custom firmware flash
|
||||
|
||||
### 3.5.3
|
||||
* Change minimum RTK Survey-In limit to 0.01 meters
|
||||
* Change Windows driver detection logic
|
||||
* Fix crash when clicking on GeoFence polygon vertex
|
||||
* PX4: Fix missing ```MC_YAW_FF``` parameter in PID Tuning
|
||||
* ArduPilot: Fix parameter file save generating bad characters from git hash
|
||||
|
||||
### 3.5.2
|
||||
* Fix Ubuntu AppImage startup failure
|
||||
|
||||
### 3.5.1
|
||||
* Update Windows usb drivers
|
||||
* Add ArduPilot CubeBlack Service Bulletin check
|
||||
* Fix visibility of PX4/ArduPilot logo in toolbar
|
||||
* Fix tile set count but in OfflineMaps which would cause image and elevation tile set to have incorrect counts and be incorrectly marked as download incomplete.
|
||||
|
||||
### 3.5.0
|
||||
* Plan GeoFence: Fix loading of fence from intermediate 3.4 code
|
||||
* Structure Scan: Fix loading of structure scan height
|
||||
* ArduPilot: Fix location of planned home position when not connected to vehicle. Issue #6840.
|
||||
* Fix loading of parameters from multiple components. Would report download complete too early, thus missing all default component params.
|
||||
* Fix file delete in mobile file dialogs
|
||||
* Add support for specifying fixed RTK based station location in Settings/General.
|
||||
* Added Airmap integration to QGC
|
||||
* Added ESTIMATOR_STATUS values to new estimatorStatus Vehicle FactGroup. These are now available to display in instrument panel.
|
||||
* Added Chinese and Turkish localization and partial German localization.
|
||||
* Make Distance to GCS available for display from instrument panel.
|
||||
* Make Heading to Home available for display from instrument panel.
|
||||
* Edit Position dialog available on polygon vertices.
|
||||
* Fixed Wing Landing Pattern: Add stop photo/video support. Defaults to on such that doing an RTL will stop camera.
|
||||
* Support loading polygons from SHP files
|
||||
* Bumped settings version (now 8). This will cause all settings to be reset to defaults.
|
||||
* Orbit visuals support changing rotation direction
|
||||
* Added support for the Taisync 2.4GHz ViUlinx digital HD wireless link.
|
||||
* Added UDP Port option for NMEA GPS Device.
|
||||
|
||||
## 3.4
|
||||
|
||||
### 3.4.4
|
||||
* Stable desktop versions now inform user at boot if newer version is available.
|
||||
* Multi-Vehicle Start Mission and Pause now work correctly. Issue #6864.
|
||||
|
||||
### 3.4.3
|
||||
* Fix bug where Resume Mission would not display correctly in some cases. Issue #6835.
|
||||
* Fix Planned Home Position altitude when no terrain data available. Issue #6846.
|
||||
|
||||
### 3.4.2
|
||||
* Fix bug where new mission items may end up with 0 altitude internally and sent to vehicle while UI shows correct altitude. Issue #6823.
|
||||
|
||||
### 3.4.1
|
||||
* Fix crash when Survery with terrain follow is moved quickly
|
||||
* Fix terrain follow climb/descent rate fields swapped in ui
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/****************************************************************************
|
||||
*
|
||||
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
*
|
||||
* QGroundControl is licensed according to the terms in the file
|
||||
* COPYING.md in the root of the source code directory.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
// This is an example class c++ file which is used to describe the QGroundControl
|
||||
// coding style. In general almost everything in here has some coding style meaning.
|
||||
// Not all style choices are explained.
|
||||
|
||||
#include "CodingStyle.h"
|
||||
#include "QGCApplication.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// Note how the Qt headers and the QGroundControl headers above are kept separate
|
||||
|
||||
Q_LOGGING_CATEGORY(CodingStyleLog, "CodingStyleLog")
|
||||
|
||||
const int CodingStyle::_privateStaticVariable = 0;
|
||||
|
||||
CodingStyle::CodingStyle(QObject* parent) :
|
||||
QObject(parent),
|
||||
_protectedVariable(1),
|
||||
_privateVariable1(2),
|
||||
_privateVariable2(3)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Document non-obvious private methods in the code file.
|
||||
void CodingStyle::_privateMethod(void)
|
||||
{
|
||||
// Always include braces even for single line if/for/...
|
||||
if (uas != _uasId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note the brace placement
|
||||
if (_lastSeenComponent == -1) {
|
||||
_lastSeenComponent = component;
|
||||
} else {
|
||||
Q_ASSERT(component == _lastSeenComponent); // Asserts are your friend
|
||||
}
|
||||
}
|
||||
|
||||
void CodingStyle::_privateMethod2(void)
|
||||
{
|
||||
Fact* fact = qobject_cast<Fact*>(sender());
|
||||
Q_ASSERT(fact);
|
||||
|
||||
QVariant typedValue;
|
||||
switch (fact->type()) {
|
||||
case FactMetaData::valueTypeInt8:
|
||||
case FactMetaData::valueTypeInt16:
|
||||
case FactMetaData::valueTypeInt32:
|
||||
typedValue.setValue(QVariant(value.toInt()));
|
||||
break;
|
||||
|
||||
case FactMetaData::valueTypeUint8:
|
||||
case FactMetaData::valueTypeUint16:
|
||||
case FactMetaData::valueTypeUint32:
|
||||
typedValue.setValue(value.toUInt());
|
||||
break;
|
||||
|
||||
case FactMetaData::valueTypeFloat: {
|
||||
int localScopedVar = 1;
|
||||
typedValue.setValue(value.toFloat());
|
||||
}
|
||||
break;
|
||||
|
||||
case FactMetaData::valueTypeDouble:
|
||||
typedValue.setValue(value.toDouble());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CodingStyle::_methodWithManyArguments(QWidget* parent,
|
||||
const QString& caption,
|
||||
const QString& dir,
|
||||
Options options1,
|
||||
Options /* options2 */,
|
||||
Options options3)
|
||||
{
|
||||
// options2 is an unused method argument.
|
||||
// Do not use Q_UNUSUED and do not just remove the argument name and leave the type.
|
||||
|
||||
// Implementataion here...
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/****************************************************************************
|
||||
*
|
||||
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
*
|
||||
* QGroundControl is licensed according to the terms in the file
|
||||
* COPYING.md in the root of the source code directory.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
// This is an example class header file which is used to describe the QGroundControl
|
||||
// coding style. In general almost everything in here has some coding style meaning.
|
||||
// Not all style choices are explained.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "Fact.h"
|
||||
#include "UASInterface.h"
|
||||
|
||||
// Note how the Qt headers, System, headers and the QGroundControl headers above are kept in separate groups
|
||||
|
||||
// If you are going to use a logging category for a class it should have the same name as the class
|
||||
// with a suffix of Log.
|
||||
Q_DECLARE_LOGGING_CATEGORY(CodingStyleLog)
|
||||
|
||||
/// Here is the class documentation. Class names are PascalCase. If you override any of the Qt base classes to provide
|
||||
/// generic base implementations for widespread use prefix the class name with QGC. For example:
|
||||
/// QGCMessageBox - is a QGC special vesion of Qt MessageBox
|
||||
/// QGCPalette - is a QGC special version of Qt Palette
|
||||
/// For normal single use classes do no prefix them name with QGC.
|
||||
|
||||
class CodingStyle : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CodingStyle(QObject* parent = NULL);
|
||||
~CodingStyle();
|
||||
|
||||
/// Document public methods which are non-obvious in the header file
|
||||
bool publicMethod1(void);
|
||||
|
||||
signals:
|
||||
/// Document signals which are non-obvious in the header file
|
||||
void qtSignal(void);
|
||||
|
||||
public slots:
|
||||
// Public slots should only be used if the slot is connected to from another class. Majority of slots
|
||||
// should be private.
|
||||
void publicSlot(void);
|
||||
|
||||
// Don't use protected methods or variables unless the class is specifically meant to be used as a base class.
|
||||
protected:
|
||||
int _protectedVariable; ///< variable names are camelCase
|
||||
|
||||
void _protectedMethod(void); ///< method names are camelCase
|
||||
|
||||
private slots:
|
||||
void _privateSlot(void);
|
||||
|
||||
private:
|
||||
// Private methods and variable names begin with an "_". Documentation for
|
||||
// non-obvious private methods goes in the code file, not the header.
|
||||
void _privateMethod(void);
|
||||
|
||||
void _methodWithManyArguments(QWidget* parent, const QString& caption, const QString& dir, Options options1, Options options2, Options options3);
|
||||
|
||||
/// Document non-obvious variables in the header file. Long descriptions go here.
|
||||
int _privateVariable1;
|
||||
|
||||
int _privateVariable2; ///< Short descriptions go here
|
||||
|
||||
static const int _privateStaticVariable;
|
||||
};
|
|
@ -0,0 +1,64 @@
|
|||
/****************************************************************************
|
||||
*
|
||||
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
*
|
||||
* QGroundControl is licensed according to the terms in the file
|
||||
* COPYING.md in the root of the source code directory.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
import QtQuick 2.5
|
||||
import QtQuick.Controls 1.3
|
||||
|
||||
import QGroundControl 1.0
|
||||
import QGroundControl.ScreenTools 1.0
|
||||
import QGroundControl.Controls 1.0
|
||||
import QGroundControl.Palette 1.0
|
||||
|
||||
/// This is an example Qml file which is used to describe the QGroundControl coding style.
|
||||
/// In general almost everything in here has some coding style meaning including order of
|
||||
/// code. Not all style choices are explained. If there is any confusison please ask
|
||||
/// and we'll answer and update style as needed.
|
||||
Item {
|
||||
// Property binding to item properties
|
||||
width: ScreenTools.defaultFontPixelHeight * 10 // No hardcoded sizing. All sizing must be relative to a ScreenTools font size
|
||||
height: ScreenTools.defaultFontPixelHeight * 20
|
||||
|
||||
// Property definitions available to consumers of this Qml Item come first
|
||||
property int myIntProperty: 10
|
||||
property real myRealProperty: 20.0
|
||||
|
||||
// Property definitions which are internal to the item are prepending with an underscore
|
||||
// to signal private and come second
|
||||
readonly property real _rectWidth: ScreenTools.defaultFontPixelWidth * 10 // Use readonly appropriately to increase binding performance
|
||||
readonly property real _rectHeight: ScreenTools.defaultFontPixelWidth * 10
|
||||
|
||||
function myFunction() {
|
||||
console.log("myFunction was called")
|
||||
}
|
||||
|
||||
QGCPalette {
|
||||
id: qgcPal // Note how id does not use an underscore
|
||||
colorGroupEnabled: enabled
|
||||
}
|
||||
|
||||
// You should always use the QGC provided variants of base control since they automatically support
|
||||
// our theming and font support.
|
||||
QGCButton {
|
||||
// Also not how there is no id: specified for this control. Only add id: if it is needed.
|
||||
text: "Button"
|
||||
|
||||
onClicked: myFunction()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: _rectWidth
|
||||
height: _rectHeight
|
||||
color: qgcPal.window // Use QGC palette colors for everything, no hardcoded colors
|
||||
}
|
||||
|
||||
// For scoped blocks which are long include a comment so you can tell what the brace is matching.
|
||||
// This is very handy when the top level brace scrolls off the screen. The endbrace comment in this
|
||||
// specific file is only included as style info. This example code is not long enough to really need it.
|
||||
} // Item - CodingStyle
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
|
||||
<plist version="0.9">
|
||||
<dict>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>macx.icns</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${QMAKE_FULL_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${QMAKE_FULL_VERSION}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Camera devices used for FPV</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,14 @@
|
|||
# This system is for posting bugs or feature requests ONLY. Support questions will be automatically closed with no response.
|
||||
# For questions about how to use or build QGC see: http://qgroundcontrol.com/#resources
|
||||
|
||||
----
|
||||
# To post a bug report
|
||||
When posting bug reports, include the following informaiton:
|
||||
- Ground station operating system.
|
||||
- QGroundControl version and build (daily, stable, self-built from source, etc.)
|
||||
- **If you are using a Stable build which is not the latest, first step is to install latest Stable build and try again prior to entering a bug report.**
|
||||
- Autopilot board: Pixhawk I, Pixhawk Mini, Pixhawk 2, etc.
|
||||
- Autopilot firmware (e.g. PX4, ArduPilot, custom) and version.
|
||||
- Exact steps to reproduce the problem. Starting from booting QGroundControl to when the problem occurs.
|
||||
|
||||
**All this template text should be deleted before creating your issue.**
|
|
@ -0,0 +1,135 @@
|
|||
|
||||
# Enforce the presence of the GIT repository
|
||||
#
|
||||
# We depend on our submodules, so we have to prevent attempts to
|
||||
# compile without it being present.
|
||||
ifeq ($(wildcard .git),)
|
||||
$(error YOU HAVE TO USE GIT TO DOWNLOAD THIS REPOSITORY. ABORTING.)
|
||||
endif
|
||||
|
||||
|
||||
# explicity set default build target
|
||||
all: linux
|
||||
|
||||
# Parsing
|
||||
# --------------------------------------------------------------------
|
||||
# assume 1st argument passed is the main target, the
|
||||
# rest are arguments to pass to the makefile generated
|
||||
# by cmake in the subdirectory
|
||||
FIRST_ARG := $(firstword $(MAKECMDGOALS))
|
||||
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
|
||||
j ?= 4
|
||||
|
||||
NINJA_BIN := ninja
|
||||
ifndef NO_NINJA_BUILD
|
||||
NINJA_BUILD := $(shell $(NINJA_BIN) --version 2>/dev/null)
|
||||
|
||||
ifndef NINJA_BUILD
|
||||
NINJA_BIN := ninja-build
|
||||
NINJA_BUILD := $(shell $(NINJA_BIN) --version 2>/dev/null)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef NINJA_BUILD
|
||||
PX4_CMAKE_GENERATOR := Ninja
|
||||
PX4_MAKE := $(NINJA_BIN)
|
||||
|
||||
ifdef VERBOSE
|
||||
PX4_MAKE_ARGS := -v
|
||||
else
|
||||
PX4_MAKE_ARGS :=
|
||||
endif
|
||||
else
|
||||
ifdef SYSTEMROOT
|
||||
# Windows
|
||||
PX4_CMAKE_GENERATOR := "MSYS\ Makefiles"
|
||||
else
|
||||
PX4_CMAKE_GENERATOR := "Unix\ Makefiles"
|
||||
endif
|
||||
PX4_MAKE = $(MAKE)
|
||||
PX4_MAKE_ARGS = -j$(j) --no-print-directory
|
||||
endif
|
||||
|
||||
CMAKE_BUILD_TYPE ?= RelWithDebInfo
|
||||
|
||||
SRC_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
# Functions
|
||||
# --------------------------------------------------------------------
|
||||
# describe how to build a cmake config
|
||||
define cmake-build
|
||||
+@$(eval BUILD_DIR = $(SRC_DIR)/build/$@$(BUILD_DIR_SUFFIX))
|
||||
+@if [ $(PX4_CMAKE_GENERATOR) = "Ninja" ] && [ -e $(BUILD_DIR)/Makefile ]; then rm -rf $(BUILD_DIR); fi
|
||||
+@if [ ! -e $(BUILD_DIR)/CMakeCache.txt ]; then mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake $(2) -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -G"$(PX4_CMAKE_GENERATOR)" -DQT_MKSPEC=$(1) || (rm -rf $(BUILD_DIR)); fi
|
||||
+@(cd $(BUILD_DIR) && $(PX4_MAKE) $(PX4_MAKE_ARGS) $(ARGS))
|
||||
endef
|
||||
|
||||
|
||||
# Qt mkspec
|
||||
# android_armv7 android_x86 gcc_64
|
||||
|
||||
gcc_64:
|
||||
$(call cmake-build,$@,$(SRC_DIR))
|
||||
|
||||
android_armv7:
|
||||
$(call cmake-build,$@,$(SRC_DIR))
|
||||
|
||||
android_x86:
|
||||
$(call cmake-build,$@,$(SRC_DIR))
|
||||
|
||||
clang_64:
|
||||
$(call cmake-build,$@,$(SRC_DIR))
|
||||
|
||||
xcode:
|
||||
@mkdir -p build/xcode; cd build/xcode; cmake -GXcode -DCMAKE_BUILD_TYPE=RelWithDebInfo $(SRC_DIR)
|
||||
|
||||
linux: gcc_64
|
||||
|
||||
android: android_armv7
|
||||
|
||||
mac: clang_64
|
||||
|
||||
|
||||
# Astyle
|
||||
# --------------------------------------------------------------------
|
||||
.PHONY: check_format format
|
||||
|
||||
check_format:
|
||||
$(call colorecho,"Checking formatting with astyle")
|
||||
@$(SRC_DIR)/Tools/astyle/check_code_style_all.sh
|
||||
@cd $(SRC_DIR) && git diff --check
|
||||
|
||||
format:
|
||||
$(call colorecho,"Formatting with astyle")
|
||||
@$(SRC_DIR)/Tools/astyle/check_code_style_all.sh --fix
|
||||
|
||||
# Testing
|
||||
# --------------------------------------------------------------------
|
||||
.PHONY: tests tests_coverage
|
||||
|
||||
tests:
|
||||
|
||||
tests_coverage:
|
||||
|
||||
# Cleanup
|
||||
# --------------------------------------------------------------------
|
||||
.PHONY: clean submodulesclean submodulesupdate distclean
|
||||
|
||||
clean:
|
||||
@rm -rf $(SRC_DIR)/build
|
||||
|
||||
submodulesclean:
|
||||
@git submodule foreach --quiet --recursive git clean -ff -x -d
|
||||
@git submodule update --quiet --init --recursive --force || true
|
||||
@git submodule sync --recursive
|
||||
@git submodule update --init --recursive --force
|
||||
|
||||
submodulesupdate:
|
||||
@git submodule update --quiet --init --recursive || true
|
||||
@git submodule sync --recursive
|
||||
@git submodule update --init --recursive
|
||||
|
||||
distclean:
|
||||
@git submodule deinit -f .
|
||||
@git clean -ff -x -d -e ".project" -e ".cproject" -e ".idea" -e ".settings" -e ".vscode"
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,325 @@
|
|||
################################################################################
|
||||
#
|
||||
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
#
|
||||
# QGroundControl is licensed according to the terms in the file
|
||||
# COPYING.md in the root of the source code directory.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
#
|
||||
# This file contains configuration settings which are common to both the QGC Application and
|
||||
# the Location Plugin. It should mainly contains initial CONFIG tag setup and compiler settings.
|
||||
#
|
||||
|
||||
# Setup our supported build types. We do this once here and then use the defined config scopes
|
||||
# to allow us to easily modify suported build types in one place instead of duplicated throughout
|
||||
# the project file.
|
||||
|
||||
CONFIG -= debug_and_release
|
||||
CONFIG += warn_on
|
||||
CONFIG += resources_big
|
||||
CONFIG += c++17
|
||||
|
||||
linux {
|
||||
linux-g++ | linux-g++-64 | linux-g++-32 | linux-clang {
|
||||
message("Linux build")
|
||||
CONFIG += LinuxBuild
|
||||
DEFINES += __STDC_LIMIT_MACROS
|
||||
DEFINES += QGC_GST_TAISYNC_ENABLED
|
||||
DEFINES += QGC_GST_MICROHARD_ENABLED
|
||||
linux-clang {
|
||||
message("Linux clang")
|
||||
QMAKE_CXXFLAGS += -Qunused-arguments -fcolor-diagnostics
|
||||
} else {
|
||||
#QMAKE_CXXFLAGS += -H # Handy for debugging why something is getting built when an include file is touched
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Werror \
|
||||
-Wno-deprecated-copy \ # These come from mavlink headers
|
||||
-Wno-unused-parameter \ # gst_plugins-good has these errors
|
||||
-Wno-implicit-fallthrough # gst_plugins-good has these errors
|
||||
}
|
||||
} else : linux-rasp-pi2-g++ {
|
||||
message("Linux R-Pi2 build")
|
||||
CONFIG += LinuxBuild
|
||||
DEFINES += __STDC_LIMIT_MACROS __rasp_pi2__
|
||||
DEFINES += QGC_GST_TAISYNC_ENABLED
|
||||
DEFINES += QGC_GST_MICROHARD_ENABLED
|
||||
} else : android-clang {
|
||||
CONFIG += AndroidBuild MobileBuild
|
||||
DEFINES += __android__
|
||||
DEFINES += __STDC_LIMIT_MACROS
|
||||
DEFINES += QGC_ENABLE_BLUETOOTH
|
||||
DEFINES += QGC_GST_TAISYNC_ENABLED
|
||||
DEFINES += QGC_GST_MICROHARD_ENABLED
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Werror \
|
||||
-Wno-unused-parameter \ # gst_plugins-good has these errors
|
||||
-Wno-implicit-fallthrough \ # gst_plugins-good has these errors
|
||||
-Wno-unused-command-line-argument \ # from somewhere in Qt generated build files
|
||||
-Wno-parentheses-equality # android gstreamer header files
|
||||
QMAKE_CFLAGS_WARN_ON += \
|
||||
-Wno-unused-command-line-argument # from somewhere in Qt generated build files
|
||||
target.path = $$DESTDIR
|
||||
equals(ANDROID_TARGET_ARCH, armeabi-v7a) {
|
||||
DEFINES += __androidArm32__
|
||||
message("Android Arm 32 bit build")
|
||||
} else:equals(ANDROID_TARGET_ARCH, arm64-v8a) {
|
||||
DEFINES += __androidArm64__
|
||||
message("Android Arm 64 bit build")
|
||||
} else:equals(ANDROID_TARGET_ARCH, x86) {
|
||||
CONFIG += Androidx86Build
|
||||
DEFINES += __androidx86__
|
||||
message("Android x86 build")
|
||||
} else {
|
||||
error("Unsupported Android architecture: $${ANDROID_TARGET_ARCH}")
|
||||
}
|
||||
} else {
|
||||
error("Unsuported Linux toolchain, only GCC 32- or 64-bit is supported")
|
||||
}
|
||||
} else : win32 {
|
||||
contains(QMAKE_TARGET.arch, x86_64) {
|
||||
message("Windows build")
|
||||
CONFIG += WindowsBuild
|
||||
DEFINES += __STDC_LIMIT_MACROS
|
||||
DEFINES += QGC_GST_TAISYNC_ENABLED
|
||||
DEFINES += QGC_GST_MICROHARD_ENABLED
|
||||
QMAKE_CFLAGS -= -Zc:strictStrings
|
||||
QMAKE_CFLAGS_RELEASE -= -Zc:strictStrings
|
||||
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
|
||||
QMAKE_CXXFLAGS -= -Zc:strictStrings
|
||||
QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings
|
||||
QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
|
||||
QMAKE_CXXFLAGS_WARN_ON += /WX /W3 \
|
||||
/wd4005 \ # silence warnings about macro redefinition, these come from the shapefile code with is external
|
||||
/wd4290 \ # ignore exception specifications
|
||||
/wd4267 \ # silence conversion from 'size_t' to 'int', possible loss of data, these come from gps drivers shared with px4
|
||||
/wd4100 # unreferenced formal parameter - gst-plugins-good
|
||||
} else {
|
||||
error("Unsupported Windows toolchain, only Visual Studio 2017 64 bit is supported")
|
||||
}
|
||||
} else : macx {
|
||||
macx-clang | macx-llvm {
|
||||
message("Mac build")
|
||||
CONFIG += MacBuild
|
||||
CONFIG += x86_64
|
||||
CONFIG -= x86
|
||||
DEFINES += QGC_GST_TAISYNC_ENABLED
|
||||
DEFINES += QGC_GST_MICROHARD_ENABLED
|
||||
QMAKE_CXXFLAGS += -fvisibility=hidden
|
||||
QMAKE_CXXFLAGS_WARN_ON += -Werror \
|
||||
-Wno-unused-parameter \ # gst-plugins-good
|
||||
-Wno-unused-but-set-variable \ # eigen & QGCTileCacheWorker.cpp
|
||||
-Wno-deprecated-declarations # eigen
|
||||
} else {
|
||||
error("Unsupported Mac toolchain, only 64-bit LLVM+clang is supported")
|
||||
}
|
||||
} else : ios {
|
||||
message("iOS build")
|
||||
CONFIG += iOSBuild MobileBuild app_bundle
|
||||
CONFIG -= bitcode
|
||||
DEFINES += __ios__
|
||||
DEFINES += QGC_NO_GOOGLE_MAPS
|
||||
DEFINES += NO_SERIAL_LINK
|
||||
DEFINES += QGC_DISABLE_UVC
|
||||
DEFINES += QGC_GST_TAISYNC_ENABLED
|
||||
DEFINES += NO_SERIAL_LINK
|
||||
QMAKE_IOS_DEPLOYMENT_TARGET = 11.0
|
||||
QMAKE_APPLE_TARGETED_DEVICE_FAMILY = 1,2 # Universal
|
||||
QMAKE_LFLAGS += -Wl,-no_pie
|
||||
} else {
|
||||
error("Unsupported build platform, only Linux, Windows, Android and Mac (Mac OS and iOS) are supported")
|
||||
}
|
||||
|
||||
# Enable ccache where we can
|
||||
linux|macx|ios {
|
||||
system(which ccache) {
|
||||
message("Found ccache, enabling")
|
||||
!ios {
|
||||
QMAKE_CXX = ccache $$QMAKE_CXX
|
||||
QMAKE_CC = ccache $$QMAKE_CC
|
||||
} else {
|
||||
QMAKE_CXX = $$PWD/tools/iosccachecc.sh
|
||||
QMAKE_CC = $$PWD/tools/iosccachecxx.sh
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contains(DEFINES, NO_SERIAL_LINK) {
|
||||
message("Serial port support disabled")
|
||||
}
|
||||
|
||||
!MacBuild:!AndroidBuild {
|
||||
# See QGCPostLinkCommon.pri for details on why MacBuild doesn't use DESTDIR
|
||||
DESTDIR = staging
|
||||
}
|
||||
|
||||
MobileBuild {
|
||||
DEFINES += __mobile__
|
||||
}
|
||||
|
||||
StableBuild {
|
||||
message("Stable Build")
|
||||
} else {
|
||||
message("Daily Build")
|
||||
DEFINES += DAILY_BUILD
|
||||
}
|
||||
|
||||
# Set the QGC version from git
|
||||
APP_VERSION_STR = vUnknown
|
||||
VERSION = 0.0.0 # Marker to indicate out-of-tree build
|
||||
MAC_VERSION = 0.0.0
|
||||
MAC_BUILD = 0
|
||||
exists ($$PWD/.git) {
|
||||
GIT_DESCRIBE = $$system(git --git-dir $$PWD/.git --work-tree $$PWD describe --always --tags)
|
||||
GIT_BRANCH = $$system(git --git-dir $$PWD/.git --work-tree $$PWD rev-parse --abbrev-ref HEAD)
|
||||
GIT_HASH = $$system(git --git-dir $$PWD/.git --work-tree $$PWD rev-parse --short HEAD)
|
||||
GIT_TIME = $$system(git --git-dir $$PWD/.git --work-tree $$PWD show --oneline --format=\"%ci\" -s HEAD)
|
||||
|
||||
message(GIT_DESCRIBE $${GIT_DESCRIBE})
|
||||
|
||||
# Pull the version info from the last annotated version tag. Format: v#.#.#
|
||||
contains(GIT_DESCRIBE, ^v[0-9]+.[0-9]+.[0-9]+.*) {
|
||||
APP_VERSION_STR = $${GIT_DESCRIBE}
|
||||
VERSION = $$replace(GIT_DESCRIBE, "v", "")
|
||||
VERSION = $$replace(VERSION, "-", ".")
|
||||
VERSION = $$section(VERSION, ".", 0, 3)
|
||||
}
|
||||
|
||||
DailyBuild {
|
||||
APP_VERSION_STR = "Daily $${GIT_BRANCH}:$${GIT_HASH} $${GIT_TIME}"
|
||||
}
|
||||
|
||||
message(QGroundControl APP_VERSION_STR VERSION $${APP_VERSION_STR} $${VERSION})
|
||||
|
||||
MacBuild {
|
||||
MAC_VERSION = $$section(VERSION, ".", 0, 2)
|
||||
MAC_BUILD = $$section(VERSION, ".", 3, 3)
|
||||
message(QGroundControl MAC_VERSION MAC_BUILD $${MAC_VERSION} $${MAC_BUILD})
|
||||
}
|
||||
}
|
||||
DEFINES += APP_VERSION_STR=\"\\\"$$APP_VERSION_STR\\\"\"
|
||||
|
||||
AndroidBuild {
|
||||
QGC_ANDROID_PACKAGE = org.mavlink.qgroundcontrol
|
||||
|
||||
message(VERSION $${VERSION})
|
||||
MAJOR_VERSION = $$section(VERSION, ".", 0, 0)
|
||||
MINOR_VERSION = $$section(VERSION, ".", 1, 1)
|
||||
PATCH_VERSION = $$section(VERSION, ".", 2, 2)
|
||||
DEV_VERSION = $$section(VERSION, ".", 3, 3)
|
||||
|
||||
greaterThan(MAJOR_VERSION, 9) {
|
||||
error(Major version larger than 1 digit: $${MAJOR_VERSION})
|
||||
}
|
||||
greaterThan(MINOR_VERSION, 9) {
|
||||
error(Minor version larger than 1 digit: $${MINOR_VERSION})
|
||||
}
|
||||
greaterThan(PATCH_VERSION, 99) {
|
||||
error(Patch version larger than 2 digits: $${PATCH_VERSION})
|
||||
}
|
||||
greaterThan(DEV_VERSION, 999) {
|
||||
error(Dev version larger than 3 digits: $${DEV_VERSION})
|
||||
}
|
||||
|
||||
lessThan(PATCH_VERSION, 10) {
|
||||
PATCH_VERSION = $$join(PATCH_VERSION, "", "0")
|
||||
}
|
||||
equals(DEV_VERSION, "") {
|
||||
DEV_VERSION = "0"
|
||||
}
|
||||
lessThan(DEV_VERSION, 10) {
|
||||
DEV_VERSION = $$join(DEV_VERSION, "", "0")
|
||||
}
|
||||
lessThan(DEV_VERSION, 100) {
|
||||
DEV_VERSION = $$join(DEV_VERSION, "", "0")
|
||||
}
|
||||
|
||||
# Bitness for android version number is 66/34 instead of 64/32 in because of a required version number bump screw-up ages ago
|
||||
equals(ANDROID_TARGET_ARCH, arm64-v8a) {
|
||||
ANDROID_TRUE_BITNESS = 64
|
||||
ANDROID_VERSION_BITNESS = 66
|
||||
} else {
|
||||
ANDROID_TRUE_BITNESS = 32
|
||||
ANDROID_VERSION_BITNESS = 34
|
||||
}
|
||||
|
||||
# Version code format: BBMIPPDDD (B=Bitness, I=Minor)
|
||||
ANDROID_VERSION_CODE = "BBMIPPDDD"
|
||||
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "BB", $$ANDROID_VERSION_BITNESS)
|
||||
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "M", $$MAJOR_VERSION)
|
||||
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "I", $$MINOR_VERSION)
|
||||
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "PP", $$PATCH_VERSION)
|
||||
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "DDD", $$DEV_VERSION)
|
||||
|
||||
message(Android version info: $${ANDROID_VERSION_CODE} bitness:$${ANDROID_VERSION_BITNESS} major:$${MAJOR_VERSION} minor:$${MINOR_VERSION} patch:$${PATCH_VERSION} dev:$${DEV_VERSION})
|
||||
|
||||
ANDROID_VERSION_NAME = APP_VERSION_STR
|
||||
}
|
||||
|
||||
DEFINES += EIGEN_MPL2_ONLY
|
||||
|
||||
# Installer configuration
|
||||
|
||||
installer {
|
||||
CONFIG -= debug
|
||||
CONFIG -= debug_and_release
|
||||
CONFIG += release
|
||||
message(Build Installer)
|
||||
}
|
||||
|
||||
# Setup our supported build flavors
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
message(Debug flavor)
|
||||
CONFIG += DebugBuild
|
||||
} else:CONFIG(release, debug|release) {
|
||||
message(Release flavor)
|
||||
CONFIG += ReleaseBuild
|
||||
} else {
|
||||
error(Unsupported build flavor)
|
||||
}
|
||||
|
||||
# Setup our build directories
|
||||
|
||||
SOURCE_DIR = $$IN_PWD
|
||||
|
||||
LANGUAGE = C++
|
||||
|
||||
LOCATION_PLUGIN_DESTDIR = $${OUT_PWD}/src/QtLocationPlugin
|
||||
LOCATION_PLUGIN_NAME = QGeoServiceProviderFactoryQGC
|
||||
|
||||
# Turn off serial port warnings
|
||||
DEFINES += _TTY_NOWARN_
|
||||
|
||||
MacBuild {
|
||||
QMAKE_TARGET_BUNDLE_PREFIX = org.qgroundcontrol
|
||||
QMAKE_BUNDLE = qgroundcontrol
|
||||
}
|
||||
|
||||
#
|
||||
# Build-specific settings
|
||||
#
|
||||
|
||||
ReleaseBuild {
|
||||
DEFINES += QT_NO_DEBUG QT_MESSAGELOGCONTEXT
|
||||
CONFIG += force_debug_info # Enable debugging symbols on release builds
|
||||
!iOSBuild {
|
||||
!AndroidBuild {
|
||||
CONFIG += ltcg # Turn on link time code generation
|
||||
}
|
||||
}
|
||||
|
||||
WindowsBuild {
|
||||
# Run compilation using VS compiler using multiple threads
|
||||
QMAKE_CXXFLAGS += -MP
|
||||
|
||||
# Enable function level linking and enhanced optimized debugging
|
||||
QMAKE_CFLAGS_RELEASE += /Gy /Zo
|
||||
QMAKE_CXXFLAGS_RELEASE += /Gy /Zo
|
||||
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += /Gy /Zo
|
||||
QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO += /Gy /Zo
|
||||
|
||||
# Eliminate duplicate COMDATs
|
||||
QMAKE_LFLAGS_RELEASE += /OPT:ICF
|
||||
QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO += /OPT:ICF
|
||||
}
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
################################################################################
|
||||
#
|
||||
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
#
|
||||
# QGroundControl is licensed according to the terms in the file
|
||||
# COPYING.md in the root of the source code directory.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
#
|
||||
# [REQUIRED] Add support for <inttypes.h> to Windows.
|
||||
#
|
||||
WindowsBuild {
|
||||
INCLUDEPATH += libs/msinttypes
|
||||
}
|
||||
|
||||
#
|
||||
# [REQUIRED] Add support for the MAVLink communications protocol.
|
||||
#
|
||||
# By default MAVLink dialect is hardwired to arudpilotmega. The reason being
|
||||
# the current codebase supports both PX4 and APM flight stack. PX4 flight stack
|
||||
# only uses common MAVLink specifications, whereas APM flight stack uses custom
|
||||
# MAVLink specifications which adds to common. So by using the adupilotmega dialect
|
||||
# QGC can support both in the same codebase.
|
||||
|
||||
# Once the mavlink helper routines include support for multiple dialects within
|
||||
# a single compiled codebase this hardwiring of dialect can go away. But until then
|
||||
# this "workaround" is needed.
|
||||
|
||||
# In the mean time, it’s possible to define a completely different dialect by defining the
|
||||
# location and name below.
|
||||
|
||||
# check for user defined settings in user_config.pri if not already set as qmake argument
|
||||
isEmpty(MAVLINKPATH_REL) {
|
||||
exists(user_config.pri):infile(user_config.pri, MAVLINKPATH_REL) {
|
||||
MAVLINKPATH_REL = $$fromfile(user_config.pri, MAVLINKPATH_REL)
|
||||
message($$sprintf("Using user-supplied relativ mavlink path '%1' specified in user_config.pri", $$MAVLINKPATH_REL))
|
||||
} else {
|
||||
MAVLINKPATH_REL = libs/mavlink/include/mavlink/v2.0
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty(MAVLINKPATH) {
|
||||
exists(user_config.pri):infile(user_config.pri, MAVLINKPATH) {
|
||||
MAVLINKPATH = $$fromfile(user_config.pri, MAVLINKPATH)
|
||||
message($$sprintf("Using user-supplied mavlink path '%1' specified in user_config.pri", $$MAVLINKPATH))
|
||||
} else {
|
||||
MAVLINKPATH = $$SOURCE_DIR/$$MAVLINKPATH_REL
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty(MAVLINK_CONF) {
|
||||
exists(user_config.pri):infile(user_config.pri, MAVLINK_CONF) {
|
||||
MAVLINK_CONF = $$fromfile(user_config.pri, MAVLINK_CONF)
|
||||
message($$sprintf("Using user-supplied mavlink dialect '%1' specified in user_config.pri", $$MAVLINK_CONF))
|
||||
} else {
|
||||
MAVLINK_CONF = all
|
||||
message($$sprintf("Using MAVLink dialect '%1'.", $$MAVLINK_CONF))
|
||||
}
|
||||
}
|
||||
|
||||
# If defined, all APM specific MAVLink messages are disabled
|
||||
contains (CONFIG, QGC_DISABLE_APM_MAVLINK) {
|
||||
message("Disable APM MAVLink support")
|
||||
DEFINES += NO_ARDUPILOT_DIALECT
|
||||
CONFIG += ArdupilotDisabled
|
||||
} else {
|
||||
CONFIG += ArdupilotEnabled
|
||||
}
|
||||
|
||||
# Then we add the proper include paths dependent on the dialect.
|
||||
INCLUDEPATH += $$MAVLINKPATH
|
||||
|
||||
count(MAVLINK_CONF, 1) {
|
||||
exists($$MAVLINKPATH/$$MAVLINK_CONF) {
|
||||
INCLUDEPATH += $$MAVLINKPATH/$$MAVLINK_CONF
|
||||
DEFINES += $$sprintf('QGC_USE_%1_MESSAGES', $$upper($$MAVLINK_CONF))
|
||||
} else {
|
||||
error($$sprintf("MAVLink dialect '%1' does not exist at '%2'!", $$MAVLINK_CONF, $$MAVLINKPATH_REL))
|
||||
}
|
||||
} else {
|
||||
error(Only a single mavlink dialect can be specified in MAVLINK_CONF)
|
||||
}
|
||||
|
||||
#
|
||||
# [REQUIRED] EIGEN matrix library
|
||||
# NOMINMAX constant required to make internal min/max work.
|
||||
INCLUDEPATH += libs/eigen
|
||||
DEFINES += NOMINMAX
|
||||
|
||||
#
|
||||
# [REQUIRED] Events submodule
|
||||
HEADERS+= \
|
||||
libs/libevents/libevents/libs/cpp/protocol/receive.h \
|
||||
libs/libevents/libevents/libs/cpp/parse/health_and_arming_checks.h \
|
||||
libs/libevents/libevents/libs/cpp/parse/parser.h \
|
||||
libs/libevents/libevents/libs/cpp/generated/events_generated.h \
|
||||
libs/libevents/libevents_definitions.h
|
||||
SOURCES += \
|
||||
libs/libevents/libevents/libs/cpp/protocol/receive.cpp \
|
||||
libs/libevents/libevents/libs/cpp/parse/health_and_arming_checks.cpp \
|
||||
libs/libevents/libevents/libs/cpp/parse/parser.cpp \
|
||||
libs/libevents/definitions.cpp
|
||||
INCLUDEPATH += \
|
||||
libs/libevents \
|
||||
libs/libevents/libs/cpp/parse
|
||||
#
|
||||
# [REQUIRED] shapelib library
|
||||
INCLUDEPATH += libs/shapelib
|
||||
SOURCES += \
|
||||
libs/shapelib/shpopen.c \
|
||||
libs/shapelib/safileio.c
|
||||
|
||||
#
|
||||
# [REQUIRED] zlib library
|
||||
WindowsBuild {
|
||||
INCLUDEPATH += $$SOURCE_DIR/libs/zlib/windows/include
|
||||
LIBS += -L$$SOURCE_DIR/libs/zlib/windows/lib
|
||||
LIBS += -lzlibstat
|
||||
} else {
|
||||
LIBS += -lz
|
||||
}
|
||||
|
||||
#
|
||||
# [REQUIRED] LZMA decompression library
|
||||
HEADERS+= \
|
||||
libs/xz-embedded/linux/include/linux/xz.h \
|
||||
libs/xz-embedded/linux/lib/xz/xz_lzma2.h \
|
||||
libs/xz-embedded/linux/lib/xz/xz_private.h \
|
||||
libs/xz-embedded/linux/lib/xz/xz_stream.h \
|
||||
libs/xz-embedded/userspace/xz_config.h
|
||||
SOURCES += \
|
||||
libs/xz-embedded/linux/lib/xz/xz_crc32.c \
|
||||
libs/xz-embedded/linux/lib/xz/xz_crc64.c \
|
||||
libs/xz-embedded/linux/lib/xz/xz_dec_lzma2.c \
|
||||
libs/xz-embedded/linux/lib/xz/xz_dec_stream.c
|
||||
INCLUDEPATH += \
|
||||
libs/xz-embedded/userspace \
|
||||
libs/xz-embedded/linux/include/linux
|
||||
DEFINES += XZ_DEC_ANY_CHECK XZ_USE_CRC64
|
||||
|
||||
# [REQUIRED] QMDNS Engine
|
||||
HEADERS+= \
|
||||
libs/qmdnsengine_export.h \
|
||||
libs/qmdnsengine/src/src/bitmap_p.h \
|
||||
libs/qmdnsengine/src/src/browser_p.h \
|
||||
libs/qmdnsengine/src/src/cache_p.h \
|
||||
libs/qmdnsengine/src/src/hostname_p.h \
|
||||
libs/qmdnsengine/src/src/message_p.h \
|
||||
libs/qmdnsengine/src/src/prober_p.h \
|
||||
libs/qmdnsengine/src/src/provider_p.h \
|
||||
libs/qmdnsengine/src/src/query_p.h \
|
||||
libs/qmdnsengine/src/src/record_p.h \
|
||||
libs/qmdnsengine/src/src/resolver_p.h \
|
||||
libs/qmdnsengine/src/src/server_p.h \
|
||||
libs/qmdnsengine/src/src/service_p.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/abstractserver.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/bitmap.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/browser.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/cache.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/dns.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/hostname.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/mdns.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/message.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/prober.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/provider.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/query.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/record.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/resolver.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/server.h \
|
||||
libs/qmdnsengine/src/include/qmdnsengine/service.h
|
||||
SOURCES += \
|
||||
libs/qmdnsengine/src/src/abstractserver.cpp \
|
||||
libs/qmdnsengine/src/src/bitmap.cpp \
|
||||
libs/qmdnsengine/src/src/browser.cpp \
|
||||
libs/qmdnsengine/src/src/cache.cpp \
|
||||
libs/qmdnsengine/src/src/dns.cpp \
|
||||
libs/qmdnsengine/src/src/hostname.cpp \
|
||||
libs/qmdnsengine/src/src/mdns.cpp \
|
||||
libs/qmdnsengine/src/src/message.cpp \
|
||||
libs/qmdnsengine/src/src/prober.cpp \
|
||||
libs/qmdnsengine/src/src/provider.cpp \
|
||||
libs/qmdnsengine/src/src/query.cpp \
|
||||
libs/qmdnsengine/src/src/record.cpp \
|
||||
libs/qmdnsengine/src/src/resolver.cpp \
|
||||
libs/qmdnsengine/src/src/server.cpp \
|
||||
libs/qmdnsengine/src/src/service.cpp
|
||||
INCLUDEPATH += \
|
||||
libs/ \
|
||||
libs/qmdnsengine/src/include/ \
|
||||
libs/qmdnsengine/src/src/
|
||||
|
||||
#
|
||||
# [REQUIRED] SDL dependency. Provides joystick/gamepad support.
|
||||
# The SDL is packaged with QGC for the Mac and Windows. Linux support requires installing the SDL
|
||||
# library (development libraries and static binaries).
|
||||
#
|
||||
MacBuild {
|
||||
INCLUDEPATH += \
|
||||
$$SOURCE_DIR/libs/Frameworks/SDL2.framework/Headers
|
||||
LIBS += \
|
||||
-F$$SOURCE_DIR/libs/Frameworks \
|
||||
-framework SDL2
|
||||
} else:LinuxBuild {
|
||||
PKGCONFIG = sdl2
|
||||
} else:WindowsBuild {
|
||||
INCLUDEPATH += $$SOURCE_DIR/libs/sdl2/msvc/include
|
||||
INCLUDEPATH += $$SOURCE_DIR/libs/OpenSSL/Windows/x64/include
|
||||
LIBS += -L$$SOURCE_DIR/libs/sdl2/msvc/lib/x64
|
||||
LIBS += -lSDL2
|
||||
}
|
||||
|
||||
# Include Android OpenSSL libs
|
||||
AndroidBuild {
|
||||
include($$SOURCE_DIR/libs/OpenSSL/android_openssl/openssl.pri)
|
||||
#message("ANDROID_EXTRA_LIBS")
|
||||
#message($$ANDROID_TARGET_ARCH)
|
||||
#message($$ANDROID_EXTRA_LIBS)
|
||||
}
|
||||
|
||||
# Pairing
|
||||
contains(DEFINES, QGC_ENABLE_PAIRING) {
|
||||
MacBuild {
|
||||
#- Pairing is generally not supported on macOS. This is here solely for development.
|
||||
exists(/usr/local/Cellar/openssl/1.0.2t/include) {
|
||||
INCLUDEPATH += /usr/local/Cellar/openssl/1.0.2t/include
|
||||
LIBS += -L/usr/local/Cellar/openssl/1.0.2t/lib
|
||||
LIBS += -lcrypto
|
||||
} else {
|
||||
# There is some circular reference settings going on between QGCExternalLibs.pri and gqgroundcontrol.pro.
|
||||
# So this duplicates some of the enable/disable logic which would normally be in qgroundcontrol.pro.
|
||||
DEFINES -= QGC_ENABLE_PAIRING
|
||||
}
|
||||
} else:WindowsBuild {
|
||||
#- Pairing is not supported on Windows
|
||||
DEFINES -= QGC_ENABLE_PAIRING
|
||||
} else {
|
||||
LIBS += -lcrypto
|
||||
AndroidBuild {
|
||||
contains(QT_ARCH, arm) {
|
||||
LIBS += $$ANDROID_EXTRA_LIBS
|
||||
INCLUDEPATH += $$SOURCE_DIR/libs/OpenSSL/Android/arch-armeabi-v7a/include
|
||||
} else {
|
||||
LIBS += $$ANDROID_EXTRA_LIBS
|
||||
INCLUDEPATH += $$SOURCE_DIR/libs/OpenSSL/Android/arch-x86/include
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# [OPTIONAL] Zeroconf for UDP links
|
||||
#
|
||||
contains (DEFINES, DISABLE_ZEROCONF) {
|
||||
message("Skipping support for Zeroconf (manual override from command line)")
|
||||
DEFINES -= DISABLE_ZEROCONF
|
||||
# Otherwise the user can still disable this feature in the user_config.pri file.
|
||||
} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_ZEROCONF) {
|
||||
message("Skipping support for Zeroconf (manual override from user_config.pri)")
|
||||
# Mac support is built into OS
|
||||
} else:MacBuild|iOSBuild {
|
||||
message("Including support for Zeroconf (Bonjour)")
|
||||
DEFINES += QGC_ZEROCONF_ENABLED
|
||||
} else {
|
||||
message("Skipping support for Zeroconf (unsupported platform)")
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
################################################################################
|
||||
#
|
||||
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
#
|
||||
# QGroundControl is licensed according to the terms in the file
|
||||
# COPYING.md in the root of the source code directory.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# These are the Post Link steps which are common to all builds
|
||||
|
||||
QMAKE_POST_LINK += echo "Post Link Common"
|
||||
|
||||
#
|
||||
# Perform platform specific setup
|
||||
#
|
||||
|
||||
MacBuild {
|
||||
|
||||
# Qt is screwed up if you use qmake to create an XCode Project which has a DESTDIR set on it.
|
||||
# This is because XCode builds create the .app in BUILT_PRODUCTS_DIR. If you use a DESTDIR then
|
||||
# Qt adds a Copy Phase to the build which copies the .app from the BUILT_PRODUCTS_DIR to DESTDIR.
|
||||
# This causes all sort of problem which are too long to list here. In order to work around this
|
||||
# We have to duplicate the post link commands here to work from two different locations. And to deal
|
||||
# with the differences between post list command running in a shell script (XCode) versus a makefile (Qt Creator)
|
||||
macx-xcode {
|
||||
# SDL2 Framework
|
||||
QMAKE_POST_LINK += && rsync -a --delete $$SOURCE_DIR/libs/Frameworks/SDL2.framework $BUILT_PRODUCTS_DIR/$${TARGET}.app/Contents/Frameworks
|
||||
QMAKE_POST_LINK += && install_name_tool -change "@rpath/SDL2.framework/Versions/A/SDL2" "@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2" $BUILT_PRODUCTS_DIR/$${TARGET}.app/Contents/MacOS/$${TARGET}
|
||||
} else {
|
||||
# SDL2 Framework
|
||||
QMAKE_POST_LINK += && rsync -a --delete $$SOURCE_DIR/libs/Frameworks/SDL2.framework $${TARGET}.app/Contents/Frameworks
|
||||
QMAKE_POST_LINK += && install_name_tool -change "@rpath/SDL2.framework/Versions/A/SDL2" "@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2" $${TARGET}.app/Contents/MacOS/$${TARGET}
|
||||
}
|
||||
}
|
||||
|
||||
WindowsBuild {
|
||||
#BASEDIR_WIN = $$replace(SOURCE_DIR, "/", "\\")
|
||||
QT_BIN_DIR = $$dirname(QMAKE_QMAKE)
|
||||
|
||||
# Copy dependencies
|
||||
DebugBuild: DLL_QT_DEBUGCHAR = "d"
|
||||
ReleaseBuild: DLL_QT_DEBUGCHAR = ""
|
||||
COPY_FILE_LIST = \
|
||||
$$SOURCE_DIR\\libs\\sdl2\\msvc\\lib\\x64\\SDL2.dll \
|
||||
$$SOURCE_DIR\\libs\\OpenSSL\\windows\\libcrypto-1_1-x64.dll \
|
||||
$$SOURCE_DIR\\libs\\OpenSSL\\windows\\libssl-1_1-x64.dll
|
||||
|
||||
for(COPY_FILE, COPY_FILE_LIST) {
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$COPY_FILE\" \"$$DESTDIR\"
|
||||
}
|
||||
|
||||
ReleaseBuild {
|
||||
# Copy Visual Studio DLLs
|
||||
# Note that this is only done for release because the debugging versions of these DLLs cannot be redistributed.
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\msvcp140.dll\" \"$$DESTDIR\"
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\msvcp140_1.dll\" \"$$DESTDIR\"
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\vcruntime140.dll\" \"$$DESTDIR\"
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\vcruntime140_1.dll\" \"$$DESTDIR\"
|
||||
}
|
||||
|
||||
DEPLOY_TARGET = $$shell_quote($$shell_path($$DESTDIR\\$${TARGET}.exe))
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$QT_BIN_DIR\\windeployqt --qmldir=$${SOURCE_DIR}\\src $${DEPLOY_TARGET}
|
||||
}
|
||||
|
||||
LinuxBuild {
|
||||
QMAKE_POST_LINK += && mkdir -p $$DESTDIR/Qt/libs && mkdir -p $$DESTDIR/Qt/plugins
|
||||
QMAKE_RPATHDIR += $ORIGIN/Qt/libs
|
||||
|
||||
# QT_INSTALL_LIBS
|
||||
QT_LIB_LIST += \
|
||||
libQt5Charts.so.5 \
|
||||
libQt5Core.so.5 \
|
||||
libQt5DBus.so.5 \
|
||||
libQt5Gui.so.5 \
|
||||
libQt5Location.so.5 \
|
||||
libQt5Multimedia.so.5 \
|
||||
libQt5MultimediaQuick.so.5 \
|
||||
libQt5Network.so.5 \
|
||||
libQt5OpenGL.so.5 \
|
||||
libQt5Positioning.so.5 \
|
||||
libQt5PositioningQuick.so.5 \
|
||||
libQt5PrintSupport.so.5 \
|
||||
libQt5Qml.so.5 \
|
||||
libQt5QmlModels.so.5 \
|
||||
libQt5QmlWorkerScript.so.5 \
|
||||
libQt5Quick.so.5 \
|
||||
libQt5QuickControls2.so.5 \
|
||||
libQt5QuickShapes.so.5 \
|
||||
libQt5QuickTemplates2.so.5 \
|
||||
libQt5QuickWidgets.so.5 \
|
||||
libQt5SerialPort.so.5 \
|
||||
libQt5Sql.so.5 \
|
||||
libQt5Svg.so.5 \
|
||||
libQt5Test.so.5 \
|
||||
libQt5Widgets.so.5 \
|
||||
libQt5X11Extras.so.5 \
|
||||
libQt5XcbQpa.so.5 \
|
||||
libQt5Xml.so.5 \
|
||||
libicui18n.so* \
|
||||
libQt5TextToSpeech.so.5
|
||||
|
||||
# Not all Qt libs are built in all systems. CI doesn't build Wayland, for example.
|
||||
QT_LIB_OPTIONALS = \
|
||||
libQt5WaylandClient.so.5 \
|
||||
libQt5WaylandCompositor.so.5
|
||||
for(QT_LIB, QT_LIB_OPTIONALS) {
|
||||
exists("$$[QT_INSTALL_LIBS]/$$QT_LIB") {
|
||||
QT_LIB_LIST += $$QT_LIB
|
||||
}
|
||||
}
|
||||
|
||||
exists($$[QT_INSTALL_LIBS]/libicudata.so.56) {
|
||||
# Some Qt distributions link with *.so.56
|
||||
QT_LIB_LIST += \
|
||||
libicudata.so.56 \
|
||||
libicui18n.so.56 \
|
||||
libicuuc.so.56
|
||||
} else {
|
||||
QT_LIB_LIST += \
|
||||
libicudata.so \
|
||||
libicui18n.so \
|
||||
libicuuc.so
|
||||
}
|
||||
# Copy only if non-existing to avoid file timestamp updates
|
||||
for(QT_LIB, QT_LIB_LIST) {
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY -n --dereference $$[QT_INSTALL_LIBS]/$$QT_LIB $$DESTDIR/Qt/libs/
|
||||
}
|
||||
|
||||
# QT_INSTALL_PLUGINS
|
||||
QT_PLUGIN_LIST = \
|
||||
bearer \
|
||||
geoservices \
|
||||
iconengines \
|
||||
imageformats \
|
||||
platforminputcontexts \
|
||||
platforms \
|
||||
position \
|
||||
sqldrivers \
|
||||
texttospeech
|
||||
|
||||
!contains(DEFINES, __rasp_pi2__) {
|
||||
QT_PLUGIN_LIST += xcbglintegrations
|
||||
}
|
||||
|
||||
for(QT_PLUGIN, QT_PLUGIN_LIST) {
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY -n --dereference --recursive $$[QT_INSTALL_PLUGINS]/$$QT_PLUGIN $$DESTDIR/Qt/plugins/
|
||||
}
|
||||
|
||||
# QT_INSTALL_QML
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY -n --dereference --recursive $$[QT_INSTALL_QML] $$DESTDIR/Qt/
|
||||
|
||||
# QGroundControl start script
|
||||
contains (CONFIG, QGC_DISABLE_CUSTOM_BUILD) | !exists($$PWD/custom/custom.pri) {
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY $$SOURCE_DIR/deploy/qgroundcontrol-start.sh $$DESTDIR
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY $$SOURCE_DIR/deploy/qgroundcontrol.desktop $$DESTDIR
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY $$SOURCE_DIR/resources/icons/qgroundcontrol.png $$DESTDIR
|
||||
} else {
|
||||
include($$PWD/custom/custom_deploy.pri)
|
||||
}
|
||||
|
||||
QMAKE_POST_LINK += && SEARCHDIR="$$DESTDIR/Qt" RPATHDIR="$$DESTDIR/Qt/libs" "$$PWD/deploy/linux-fixup-rpaths.bash"
|
||||
|
||||
# https://doc.qt.io/qt-5/qt-conf.html
|
||||
QMAKE_POST_LINK += && $$QMAKE_COPY "$$SOURCE_DIR/deploy/qt.conf" "$$DESTDIR"
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
################################################################################
|
||||
#
|
||||
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
#
|
||||
# QGroundControl is licensed according to the terms in the file
|
||||
# COPYING.md in the root of the source code directory.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
# These are the Post Link steps which are specific to installer builds
|
||||
|
||||
installer {
|
||||
DEFINES += QGC_INSTALL_RELEASE
|
||||
MacBuild {
|
||||
QMAKE_POST_LINK += && echo macdeployqt
|
||||
QMAKE_POST_LINK += && $$dirname(QMAKE_QMAKE)/macdeployqt $${TARGET}.app -appstore-compliant -verbose=1 -qmldir=$${SOURCE_DIR}/src
|
||||
|
||||
# macdeployqt is missing some relocations once in a while. "Fix" it:
|
||||
QMAKE_POST_LINK += && rsync -a --delete /Library/Frameworks/GStreamer.framework $${TARGET}.app/Contents/Frameworks
|
||||
QMAKE_POST_LINK += && echo libexec
|
||||
QMAKE_POST_LINK += && ln -sf $${TARGET}.app/Contents/Frameworks $${TARGET}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/libexec/Frameworks
|
||||
QMAKE_POST_LINK += && install_name_tool -change /Library/Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer $${TARGET}.app/Contents/MacOS/$${TARGET}
|
||||
QMAKE_POST_LINK += && rm -rf $${TARGET}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/{bin,etc,share,Headers,include,Commands}
|
||||
QMAKE_POST_LINK += && rm -rf $${TARGET}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/{*.a,*.la,glib-2.0,gst-validate-launcher,pkgconfig}
|
||||
|
||||
codesign {
|
||||
# Disabled for now since it's not working correctly yet
|
||||
#QMAKE_POST_LINK += && echo codesign
|
||||
#QMAKE_POST_LINK += && codesign --deep $${TARGET}.app -s WQREC9W69J
|
||||
}
|
||||
|
||||
# Create package
|
||||
QMAKE_POST_LINK += && echo hdiutil
|
||||
QMAKE_POST_LINK += && mkdir -p package
|
||||
QMAKE_POST_LINK += && mkdir -p staging
|
||||
QMAKE_POST_LINK += && rsync -a --delete $${TARGET}.app staging
|
||||
QMAKE_POST_LINK += && rm -rf /tmp/tmp.dmg
|
||||
QMAKE_POST_LINK += && rm -rf package/$${TARGET}.dmg
|
||||
QMAKE_POST_LINK += && hdiutil create /tmp/tmp.dmg -ov -volname "$${TARGET}-$${MAC_VERSION}" -fs HFS+ -srcfolder "staging"
|
||||
QMAKE_POST_LINK += && hdiutil convert /tmp/tmp.dmg -format UDBZ -o package/$${TARGET}.dmg
|
||||
QMAKE_POST_LINK += && rm /tmp/tmp.dmg
|
||||
}
|
||||
WindowsBuild {
|
||||
QMAKE_POST_LINK += $$escape_expand(\\n) $$quote("\"C:\\Program Files \(x86\)\\NSIS\\makensis.exe\"" $$(QGC_NSIS_INSTALLER_PARAMETERS) /DDRIVER_MSI="\"$${QGC_INSTALLER_DRIVER_MSI}\"" /DINSTALLER_ICON="\"$${QGC_INSTALLER_ICON}\"" /DHEADER_BITMAP="\"$${QGC_INSTALLER_HEADER_BITMAP}\"" /DAPPNAME="\"$${QGC_APP_NAME}\"" /DEXENAME="\"$${TARGET}\"" /DORGNAME="\"$${QGC_ORG_NAME}\"" /DDESTDIR=$${DESTDIR} /NOCD "\"/XOutFile $${DESTDIR}\\$${TARGET}-installer.exe\"" "\"$${QGC_INSTALLER_SCRIPT}\"")
|
||||
OTHER_FILES += $${QGC_INSTALLER_SCRIPT}
|
||||
}
|
||||
LinuxBuild {
|
||||
#-- TODO: This uses hardcoded paths. It should use $${DESTDIR}
|
||||
QMAKE_POST_LINK += && mkdir -p package
|
||||
QMAKE_POST_LINK += && tar -cj --exclude='package' -f package/QGroundControl.tar.bz2 staging --transform 's/$${DESTDIR}/qgroundcontrol/'
|
||||
}
|
||||
AndroidBuild {
|
||||
_ANDROID_KEYSTORE_PASSWORD = $$(ANDROID_KEYSTORE_PASSWORD)
|
||||
QMAKE_POST_LINK += && mkdir -p package
|
||||
isEmpty(_ANDROID_KEYSTORE_PASSWORD) {
|
||||
message(Keystore password not available - not signing package)
|
||||
# This is for builds in forks and PR where the Android keystore password is not available
|
||||
QMAKE_POST_LINK += && make apk
|
||||
QMAKE_POST_LINK += && cp android-build/build/outputs/apk/debug/android-build-debug.apk package/QGroundControl$${ANDROID_TRUE_BITNESS}.apk
|
||||
} else {
|
||||
QMAKE_POST_LINK += && make apk_install_target INSTALL_ROOT=android-build
|
||||
QMAKE_POST_LINK += && androiddeployqt --verbose --input android-QGroundControl-deployment-settings.json --output android-build --release --sign $${SOURCE_DIR}/android/android_release.keystore QGCAndroidKeyStore --storepass $$(ANDROID_KEYSTORE_PASSWORD)
|
||||
QMAKE_POST_LINK += && cp android-build/build/outputs/apk/release/android-build-release-signed.apk package/QGroundControl$${ANDROID_TRUE_BITNESS}.apk
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
# QGroundControl Ground Control Station
|
||||
|
||||
[![Releases](https://img.shields.io/github/release/mavlink/QGroundControl.svg)](https://github.com/mavlink/QGroundControl/releases)
|
||||
|
||||
*QGroundControl* (QGC) is an intuitive and powerful ground control station (GCS) for UAVs.
|
||||
|
||||
The primary goal of QGC is ease of use for both first time and professional users.
|
||||
It provides full flight control and mission planning for any MAVLink enabled drone, and vehicle setup for both PX4 and ArduPilot powered UAVs. Instructions for *using QGroundControl* are provided in the [User Manual](https://docs.qgroundcontrol.com/en/) (you may not need them because the UI is very intuitive!)
|
||||
|
||||
All the code is open-source, so you can contribute and evolve it as you want.
|
||||
The [Developer Guide](https://dev.qgroundcontrol.com/en/) explains how to [build](https://dev.qgroundcontrol.com/en/getting_started/) and extend QGC.
|
||||
|
||||
|
||||
Key Links:
|
||||
* [Website](http://qgroundcontrol.com) (qgroundcontrol.com)
|
||||
* [User Manual](https://docs.qgroundcontrol.com/en/)
|
||||
* [Developer Guide](https://dev.qgroundcontrol.com/en/)
|
||||
* [Discussion/Support](https://docs.qgroundcontrol.com/en/Support/Support.html)
|
||||
* [Contributing](https://dev.qgroundcontrol.com/en/contribute/)
|
||||
* [License](https://github.com/mavlink/qgroundcontrol/blob/master/COPYING.md)
|
|
@ -0,0 +1,24 @@
|
|||
<RCC>
|
||||
<qresource prefix="/unittest">
|
||||
<file alias="SectionTest.plan">src/MissionManager/UnitTest/SectionTest.plan</file>
|
||||
<file alias="UT-MavCmdInfoCommon.json">src/MissionManager/UnitTest/UT-MavCmdInfoCommon.json</file>
|
||||
<file alias="UT-MavCmdInfoFixedWing.json">src/MissionManager/UnitTest/UT-MavCmdInfoFixedWing.json</file>
|
||||
<file alias="UT-MavCmdInfoMultiRotor.json">src/MissionManager/UnitTest/UT-MavCmdInfoMultiRotor.json</file>
|
||||
<file alias="UT-MavCmdInfoRover.json">src/MissionManager/UnitTest/UT-MavCmdInfoRover.json</file>
|
||||
<file alias="UT-MavCmdInfoSub.json">src/MissionManager/UnitTest/UT-MavCmdInfoSub.json</file>
|
||||
<file alias="UT-MavCmdInfoVTOL.json">src/MissionManager/UnitTest/UT-MavCmdInfoVTOL.json</file>
|
||||
<file alias="MissionPlanner.waypoints">src/MissionManager/UnitTest/MissionPlanner.waypoints</file>
|
||||
<file alias="OldFileFormat.mission">src/MissionManager/UnitTest/OldFileFormat.mission</file>
|
||||
<file alias="PolygonAreaTest.kml">src/MissionManager/UnitTest/PolygonAreaTest.kml</file>
|
||||
<file alias="PolygonGood.kml">src/MissionManager/UnitTest/PolygonGood.kml</file>
|
||||
<file alias="PolygonMissingNode.kml">src/MissionManager/UnitTest/PolygonMissingNode.kml</file>
|
||||
<file alias="PolygonBadXml.kml">src/MissionManager/UnitTest/PolygonBadXml.kml</file>
|
||||
<file alias="PolygonBadCoordinatesNode.kml">src/MissionManager/UnitTest/PolygonBadCoordinatesNode.kml</file>
|
||||
<file alias="MockLinkOptionsDlg.qml">src/comm/MockLinkOptionsDlg.qml</file>
|
||||
<file alias="TranslationTest.json">src/qgcunittest/TranslationTest.json</file>
|
||||
<file alias="TranslationTest_de_DE.ts">src/qgcunittest/TranslationTest_de_DE.ts</file>
|
||||
</qresource>
|
||||
<qresource prefix="/qml">
|
||||
<file alias="QGroundControl/Controls/MockLinkOptionsDlg.qml">src/comm/MockLinkOptionsDlg.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,159 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
require 'yaml'
|
||||
|
||||
current_dir = File.dirname(File.expand_path(__FILE__))
|
||||
configfile = YAML.load_file("#{current_dir}/.vagrantconfig.yml")
|
||||
yaml_config = configfile['configs']['dev']
|
||||
|
||||
env_global = [
|
||||
'JOBS=4',
|
||||
'SHADOW_BUILD_DIR=/tmp/shadow_build_dir',
|
||||
'CODESIGN=nocodesign',
|
||||
]
|
||||
|
||||
packages = [
|
||||
'build-essential',
|
||||
'fuse',
|
||||
'git',
|
||||
'libgstreamer-plugins-base1.0-dev',
|
||||
'libgstreamer1.0-0:amd64',
|
||||
'libgstreamer1.0-dev',
|
||||
'libsdl2-dev',
|
||||
'libudev-dev',
|
||||
'speech-dispatcher',
|
||||
'wget'
|
||||
]
|
||||
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
# This trick is used to prefer a VM box over docker
|
||||
config.vm.provider "virtualbox"
|
||||
config.vm.provider "vmware_fusion"
|
||||
|
||||
config.vm.box = "ubuntu/jammy64"
|
||||
config.vm.provider :docker do |docker, override|
|
||||
override.vm.box = "tknerr/baseimage-ubuntu-16.04"
|
||||
end
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.customize ["modifyvm", :id, "--memory", "6144"]
|
||||
vb.customize ["modifyvm", :id, "--cpus", "1"]
|
||||
vb.gui = true
|
||||
end
|
||||
["vmware_fusion", "vmware_workstation"].each do |p|
|
||||
config.vm.provider p do |v|
|
||||
v.vmx["memsize"] = "6144"
|
||||
v.vmx["numvcpus"] = "1"
|
||||
v.gui = true
|
||||
end
|
||||
end
|
||||
if Vagrant.has_plugin?("vagrant-cachier")
|
||||
config.cache.scope = :box
|
||||
config.cache.synced_folder_opts = {
|
||||
owner: "_apt"
|
||||
}
|
||||
end
|
||||
|
||||
# the "dev configuration puts the build products and a suitable
|
||||
# environment into the /vagrant directory. This allows you to run
|
||||
# qgroundcontrol on the host machine with:
|
||||
# "cd shadow-build/release; ./qgroundcontrol-start.sh"
|
||||
|
||||
$config_shell = <<-'SHELL'
|
||||
set -e
|
||||
set -x
|
||||
|
||||
export %{build_env}
|
||||
export JOBS=$((`cat /proc/cpuinfo | grep -c ^processor`+1))
|
||||
|
||||
sudo apt-get update -y
|
||||
# we need this long command to keep packages (grub-pc esp.) from prompting for input
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install %{apt_pkgs} xubuntu-desktop qtcreator
|
||||
sudo systemctl set-default graphical.target
|
||||
|
||||
echo 'Initialising submodules'
|
||||
su - vagrant -c 'cd %{project_root_dir}; git submodule init && git submodule update'
|
||||
|
||||
# with reference to https://github.com/jurplel/install-qt-action/blob/master/src/main.ts and .github/workflows/linux_release.yml:
|
||||
echo 'Installing QT'
|
||||
apt-get install -y python3-pip
|
||||
su - vagrant -c "pip3 install --user aqtinstall"
|
||||
|
||||
apt-get install -y patchelf
|
||||
|
||||
dir="%{qt_deps_unpack_dir}"
|
||||
version="5.15.2"
|
||||
host="linux"
|
||||
target="desktop"
|
||||
modules="qtcharts"
|
||||
su - vagrant -c "rm -rf ${dir}"
|
||||
su - vagrant -c "mkdir -p ${dir}"
|
||||
su - vagrant -c "python3 -m aqt install-qt -O ${dir} ${host} ${target} ${version} -m ${modules}"
|
||||
|
||||
mkdir -p /vagrant/shadow-build
|
||||
|
||||
# write out a pair of scripts to make rebuilding on the VM easy:
|
||||
su - vagrant -c "cat <<QMAKE >do-qmake.sh
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
cd %{shadow_build_dir}
|
||||
export LD_LIBRARY_PATH=%{qt_deps_lib_unpack_dir}
|
||||
export PATH=%{qt_deps_bin_unpack_dir}:\$PATH
|
||||
qmake -r %{pro} CONFIG+=\${CONFIG} CONFIG+=WarningsAsErrorsOn -spec %{spec}
|
||||
QMAKE
|
||||
"
|
||||
|
||||
su - vagrant -c "cat <<MAKE >do-make.sh
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
cd %{shadow_build_dir}
|
||||
export LD_LIBRARY_PATH=%{qt_deps_lib_unpack_dir}
|
||||
export PATH=%{qt_deps_bin_unpack_dir}:\$PATH
|
||||
make -j${JOBS}
|
||||
MAKE
|
||||
"
|
||||
su - vagrant -c "chmod +x do-qmake.sh do-make.sh"
|
||||
|
||||
# increase the allowed number of open files (the link step takes a
|
||||
# lot of open filehandles!):
|
||||
echo '* soft nofile 2048' >/etc/security/limits.d/fileno.conf
|
||||
|
||||
# now run the scripts:
|
||||
su - vagrant -c ./do-qmake.sh
|
||||
su - vagrant -c ./do-make.sh
|
||||
|
||||
SHELL
|
||||
|
||||
config.vm.provision "dev", type: "shell", inline: $config_shell % {
|
||||
:shadow_build_dir => yaml_config['shadow_build_dir'],
|
||||
:qt_deps_tarball => yaml_config['qt_deps_tarball'],
|
||||
:pro => yaml_config['pro'],
|
||||
:spec => yaml_config['spec'],
|
||||
:apt_pkgs => (packages).join(' '),
|
||||
:build_env => env_global.select { |item| item.is_a?(String) }.join(' '),
|
||||
|
||||
:project_root_dir => yaml_config['project_root_dir'],
|
||||
:qt_deps_unpack_parent_dir => yaml_config['qt_deps_unpack_parent_dir'],
|
||||
:qt_deps_unpack_dir => yaml_config['qt_deps_unpack_dir'],
|
||||
:qt_deps_bin_unpack_dir => yaml_config['qt_deps_bin_unpack_dir'],
|
||||
:qt_deps_lib_unpack_dir => yaml_config['qt_deps_lib_unpack_dir'],
|
||||
:qt_deps_plugins_unpack_dir => yaml_config['qt_deps_plugins_unpack_dir'],
|
||||
:qt_deps_qml_unpack_dir => yaml_config['qt_deps_qml_unpack_dir'],
|
||||
|
||||
:qt_deps_dir => yaml_config['qt_deps_dir'],
|
||||
:qt_deps_bin_dir => yaml_config['qt_deps_bin_dir'],
|
||||
:qt_deps_lib_dir => yaml_config['qt_deps_lib_dir'],
|
||||
:qt_deps_plugins_dir => yaml_config['qt_deps_plugins_dir'],
|
||||
:qt_deps_qml_dir => yaml_config['qt_deps_qml_dir'],
|
||||
}
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,153 @@
|
|||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
project(VideoReceiverApp LANGUAGES C CXX)
|
||||
|
||||
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel;Coverage")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
include(FeatureSummary)
|
||||
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
add_compile_options(-Wall -Wextra -Wno-address-of-packed-member)
|
||||
endif()
|
||||
|
||||
# CMake build type
|
||||
# Debug Release RelWithDebInfo MinSizeRel Coverage
|
||||
if (NOT CMAKE_BUILD_TYPE)
|
||||
# default to release with debug symbols
|
||||
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
|
||||
endif()
|
||||
|
||||
set(QGC_ROOT ${CMAKE_SOURCE_DIR}/..)
|
||||
|
||||
# Add folder where are supportive functions
|
||||
list(APPEND CMAKE_MODULE_PATH ${QGC_ROOT}/cmake)
|
||||
|
||||
# Configure Qt5 to get necessary variables
|
||||
include(Qt5QGCConfiguration)
|
||||
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
|
||||
message(STATUS "Qt version: ${QT_VERSION}")
|
||||
message(STATUS "Qt spec: ${QT_MKSPEC}")
|
||||
|
||||
set(COMPANY "Auterion")
|
||||
set(COPYRIGHT "Copyright (c) 2020 VideoReceiverApp. All rights reserved.")
|
||||
set(IDENTIFIER "labs.auterion.VideoReceiverApp")
|
||||
|
||||
include(Git)
|
||||
message(STATUS "VideoReceiverApp version: ${APP_VERSION_STR}")
|
||||
|
||||
#=============================================================================
|
||||
# ccache
|
||||
#
|
||||
option(CCACHE "Use ccache if available" ON)
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if (CCACHE AND CCACHE_PROGRAM AND NOT DEFINED ENV{CCACHE_DISABLE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# Compile QML
|
||||
#
|
||||
option(COMPILE_QML "Pre-compile QML files using the Qt Quick compiler." FALSE)
|
||||
add_feature_info(COMPILE_QML COMPILE_QML "Pre-compile QML files using the Qt Quick compiler.")
|
||||
if(COMPILE_QML)
|
||||
find_package(Qt5QuickCompiler)
|
||||
|
||||
set_package_properties(Qt5QuickCompiler PROPERTIES
|
||||
DESCRIPTION "Pre-compile QML files using the Qt Quick compiler."
|
||||
TYPE OPTIONAL
|
||||
)
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# Debug QML
|
||||
#
|
||||
option(DEBUG_QML "Build VideoReceiverApp with QML debugging/profiling support." FALSE)
|
||||
add_feature_info(DEBUG_QML DEBUG_QML "Build VideoReceiverApp with QML debugging/profiling support.")
|
||||
if(DEBUG_QML)
|
||||
message(STATUS "To enable the QML debugger/profiler, run with: '-qmljsdebugger=port:1234'")
|
||||
add_definitions(-DQMLJSDEBUGGER)
|
||||
add_definitions(-DQT_DECLARATIVE_DEBUG)
|
||||
add_definitions(-DQT_QML_DEBUG)
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# GStreamer
|
||||
#
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(GST
|
||||
gstreamer-1.0>=1.14
|
||||
gstreamer-video-1.0>=1.14
|
||||
gstreamer-gl-1.0>=1.14
|
||||
egl
|
||||
)
|
||||
|
||||
if (GST_FOUND)
|
||||
include_directories(
|
||||
${GST_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
|
||||
#=============================================================================
|
||||
# Qt5
|
||||
#
|
||||
find_package(Qt5 ${QT_VERSION}
|
||||
COMPONENTS
|
||||
Bluetooth
|
||||
Charts
|
||||
Concurrent
|
||||
Core
|
||||
Location
|
||||
Multimedia
|
||||
Network
|
||||
Positioning
|
||||
Quick
|
||||
QuickWidgets
|
||||
OpenGL
|
||||
Sql
|
||||
Svg
|
||||
Test
|
||||
TextToSpeech
|
||||
Widgets
|
||||
Xml
|
||||
REQUIRED
|
||||
HINTS
|
||||
${QT_LIBRARY_HINTS}
|
||||
)
|
||||
|
||||
# Sets the default flags for compilation and linking.
|
||||
include(CompileOptions)
|
||||
|
||||
include_directories(
|
||||
${QGC_ROOT}/src
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
${Qt5Location_PRIVATE_INCLUDE_DIRS}
|
||||
VideoReceiver
|
||||
)
|
||||
|
||||
add_subdirectory(${QGC_ROOT}/libs/qmlglsink qmlglsink.build)
|
||||
add_subdirectory(${QGC_ROOT}/src/VideoReceiver VideoReceiver.build)
|
||||
|
||||
set(VIDEORECIVERAPP_SOURCES main.cpp ${QGC_ROOT}/src/QGCLoggingCategory.cc)
|
||||
set(VIDEORECIVERAPP_RESOURCES qml.qrc)
|
||||
|
||||
if(ANDROID)
|
||||
add_library(VideoReceiverApp SHARED ${VIDEORECIVERAPP_SOURCES} ${VIDEORECIVERAPP_RESOURCES})
|
||||
else()
|
||||
add_executable(VideoReceiverApp ${VIDEORECIVERAPP_SOURCES} ${VIDEORECIVERAPP_RESOURCES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(VideoReceiverApp
|
||||
PRIVATE
|
||||
VideoReceiver
|
||||
Qt5::Core
|
||||
Qt5::Multimedia
|
||||
Qt5::OpenGL
|
||||
Qt5::Quick
|
||||
Qt5::QuickWidgets
|
||||
)
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>QQmlGlSinkTest</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Open Source Flight Systems GmbH - Internal Build</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>labs.auterion.VideoReceiverApp</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,52 @@
|
|||
# VideoReceiverApp
|
||||
|
||||
## Application
|
||||
|
||||
This is a simple test application developed to make VideoReceiver library development and testing easier. It can also be used as part of CI for system tests.
|
||||
|
||||
## Use cases and options
|
||||
|
||||
Application's behaviour depends on the executable name. There are two modes - QML and console. QML mode is enabled by renaming application executable to something that starts with **Q** (for example QVideoReceiverApp). In this case **video-sink** option is not available and application always tries to use **qmlglsink** for video rendering. In regular case (executable name does not start with **Q**) **autovideosink** or **fakesink** are used, depending on options.
|
||||
|
||||
### Available options and required arguments
|
||||
```VideoReceiverApp [options] url```
|
||||
|
||||
for example:
|
||||
|
||||
```VideoReceiverApp -d --stop-decoding 30 rtsp://127.0.0.1:8554/test```
|
||||
|
||||
#### Options
|
||||
```-h, --help``` - displays help
|
||||
|
||||
```-t, --timeout <seconds>``` - specifies source timeout
|
||||
|
||||
```-c, --connect <attempts>``` - specifies number of connection attempts
|
||||
|
||||
```-d, --decode``` - enables or disables video decoding and rendering
|
||||
|
||||
```--no-decode``` - disables video decoding and rendering if it was enabled by default
|
||||
|
||||
```--stop-decoding <seconds>``` - specifies amount of seconds after which decoding should be stopped
|
||||
|
||||
```-r, --record <file>``` - enables record video into file
|
||||
|
||||
```-f, --format <format>``` - specifies recording file format, where format 0 - MKV, 1 - MOV, 2 - MP4
|
||||
|
||||
```--stop-recording <seconds>``` - specifies amount of seconds after which recording should be stopped
|
||||
```--video-sink <sink>``` - specifies which video sink to use : 0 - autovideosink, 1 - fakesink
|
||||
|
||||
#### Arguments
|
||||
```url``` - required, specifies video URL.
|
||||
Following URLs are supported:
|
||||
```rtsp://<host>:<port>/mount/point``` - usual RTSP URL
|
||||
|
||||
```udp://<interface>:<port>``` - H.264 over RTP/UDP
|
||||
|
||||
```udp265://<interface>:<port>``` - H.265 over RTP/UDP
|
||||
|
||||
```tsusb://<interface>:<port>``` - Taisync's forwarded H.264 byte aligned NALU stream over UDP
|
||||
|
||||
```tcp://<host>:<port>``` - MPEG-2 TS over TCP
|
||||
|
||||
```mpegts://<interface>:<port>``` - MPEG-2 TS over UDP
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest package="labs.mavlink.VideoReceiverApp" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1" android:versionCode="100000" android:installLocation="auto">
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="labs.mavlink.VideoReceiverApp.QGLSinkActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="sensorLandscape" android:launchMode="singleTask" android:keepScreenOn="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
|
||||
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/>
|
||||
<action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
|
||||
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
|
||||
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
|
||||
</intent-filter>
|
||||
|
||||
<!-- Rest of Standard Manifest -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<!-- Messages maps -->
|
||||
|
||||
<!-- Splash screen -->
|
||||
<!--
|
||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
|
||||
-->
|
||||
<!-- Splash screen -->
|
||||
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<!-- Background running -->
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
|
||||
|
||||
<!-- Needed to keep working while 'asleep' -->
|
||||
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
|
||||
<!-- Support devices without USB host mode since there are other connection types -->
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
|
||||
|
||||
<!-- Support devices without Bluetooth since there are other connection types -->
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
|
||||
<!-- Support devices that don't have location services -->
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.location" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.usb.accessory"/>
|
||||
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest android:versionName="@QT_ANDROID_APP_VERSION@" package="@QT_ANDROID_APP_PACKAGE_NAME@" android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="@QT_ANDROID_APP_VERSION_CODE@">
|
||||
<application android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtApplication">
|
||||
<activity android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:screenOrientation="unspecified" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Messages maps -->
|
||||
<!--<meta-data android:name="android.app.ministro_not_found_msg" android:value="@string/ministro_not_found_msg"/>
|
||||
<meta-data android:name="android.app.ministro_needed_msg" android:value="@string/ministro_needed_msg"/>
|
||||
<meta-data android:name="android.app.fatal_error_msg" android:value="@string/fatal_error_msg"/>-->
|
||||
</activity>
|
||||
</application>
|
||||
<supports-screens android:anyDensity="true" android:normalScreens="true" android:smallScreens="true" android:largeScreens="true"/>
|
||||
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="19"/>
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
</manifest>
|
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 787 KiB |
After Width: | Height: | Size: 2.3 MiB |
After Width: | Height: | Size: 1.8 MiB |
|
@ -0,0 +1,64 @@
|
|||
buildscript {
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "https://repo1.maven.org/maven2"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:1.1.0'
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
|
||||
android {
|
||||
/*******************************************************
|
||||
* The following variables:
|
||||
* - androidBuildToolsVersion,
|
||||
* - androidCompileSdkVersion
|
||||
* - qt5AndroidDir - holds the path to qt android files
|
||||
* needed to build any Qt application
|
||||
* on Android.
|
||||
*
|
||||
* are defined in gradle.properties file. This file is
|
||||
* updated by QtCreator and androiddeployqt tools.
|
||||
* Changing them manually might break the compilation!
|
||||
*******************************************************/
|
||||
|
||||
compileSdkVersion androidCompileSdkVersion.toInteger()
|
||||
|
||||
buildToolsVersion androidBuildToolsVersion
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
|
||||
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
|
||||
res.srcDirs = [qt5AndroidDir + '/res', 'res']
|
||||
resources.srcDirs = ['src']
|
||||
renderscript.srcDirs = ['src']
|
||||
assets.srcDirs = ['assets']
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
cruncherEnabled = false
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#Wed Apr 10 15:27:10 PDT 2013
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
|
@ -0,0 +1,90 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,25 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<array name="qt_sources">
|
||||
<item>https://download.qt-project.org/ministro/android/qt5/qt-5.4</item>
|
||||
</array>
|
||||
|
||||
<!-- The following is handled automatically by the deployment tool. It should
|
||||
not be edited manually. -->
|
||||
|
||||
<array name="bundled_libs">
|
||||
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||
</array>
|
||||
|
||||
<array name="qt_libs">
|
||||
<!-- %%INSERT_QT_LIBS%% -->
|
||||
</array>
|
||||
|
||||
<array name="bundled_in_lib">
|
||||
<!-- %%INSERT_BUNDLED_IN_LIB%% -->
|
||||
</array>
|
||||
<array name="bundled_in_assets">
|
||||
<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
|
||||
</array>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,94 @@
|
|||
package labs.mavlink.VideoReceiverApp;
|
||||
|
||||
/* Copyright 2013 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Written by: Mike Goza April 2014
|
||||
//
|
||||
// These routines interface with the Android USB Host devices for serial port communication.
|
||||
// The code uses the usb-serial-for-android software library. The QGCActivity class is the
|
||||
// interface to the C++ routines through jni calls. Do not change the functions without also
|
||||
// changing the corresponding calls in the C++ routines or you will break the interface.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.usb.UsbAccessory;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.widget.Toast;
|
||||
import android.util.Log;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Bundle;
|
||||
import android.app.PendingIntent;
|
||||
import android.view.WindowManager;
|
||||
import android.os.Bundle;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
import org.qtproject.qt5.android.bindings.QtApplication;
|
||||
|
||||
public class QGLSinkActivity extends QtActivity
|
||||
{
|
||||
public native void nativeInit();
|
||||
|
||||
// QGLSinkActivity singleton
|
||||
public QGLSinkActivity() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
nativeInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void onInit(int status) {
|
||||
}
|
||||
|
||||
public void jniOnLoad() {
|
||||
nativeInit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2012, Collabora Ltd.
|
||||
* Author: Youness Alaoui
|
||||
*
|
||||
* Copyright (C) 2015, Collabora Ltd.
|
||||
* Author: Justin Kim <justin.kim@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
package org.freedesktop.gstreamer.androidmedia;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
public class GstAhcCallback implements Camera.PreviewCallback,
|
||||
Camera.ErrorCallback,
|
||||
Camera.AutoFocusCallback {
|
||||
public long mUserData;
|
||||
public long mCallback;
|
||||
|
||||
public static native void gst_ah_camera_on_preview_frame(byte[] data, Camera camera,
|
||||
long callback, long user_data);
|
||||
public static native void gst_ah_camera_on_error(int error, Camera camera,
|
||||
long callback, long user_data);
|
||||
public static native void gst_ah_camera_on_auto_focus(boolean success, Camera camera,
|
||||
long callback, long user_data);
|
||||
|
||||
public GstAhcCallback(long callback, long user_data) {
|
||||
mCallback = callback;
|
||||
mUserData = user_data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
gst_ah_camera_on_preview_frame(data, camera, mCallback, mUserData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int error, Camera camera) {
|
||||
gst_ah_camera_on_error(error, camera, mCallback, mUserData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
gst_ah_camera_on_auto_focus(success, camera, mCallback, mUserData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2016 SurroundIO
|
||||
* Author: Martin Kelly <martin@surround.io>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.freedesktop.gstreamer.androidmedia;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
|
||||
public class GstAhsCallback implements SensorEventListener {
|
||||
public long mUserData;
|
||||
public long mSensorCallback;
|
||||
public long mAccuracyCallback;
|
||||
|
||||
public static native void gst_ah_sensor_on_sensor_changed(SensorEvent event,
|
||||
long callback, long user_data);
|
||||
public static native void gst_ah_sensor_on_accuracy_changed(Sensor sensor, int accuracy,
|
||||
long callback, long user_data);
|
||||
|
||||
public GstAhsCallback(long sensor_callback,
|
||||
long accuracy_callback, long user_data) {
|
||||
mSensorCallback = sensor_callback;
|
||||
mAccuracyCallback = accuracy_callback;
|
||||
mUserData = user_data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
gst_ah_sensor_on_sensor_changed(event, mSensorCallback, mUserData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
gst_ah_sensor_on_accuracy_changed(sensor, accuracy,
|
||||
mAccuracyCallback, mUserData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2015, Collabora Ltd.
|
||||
* Author: Matthieu Bouron <matthieu.bouron@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
package org.freedesktop.gstreamer.androidmedia;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
|
||||
|
||||
public class GstAmcOnFrameAvailableListener implements OnFrameAvailableListener
|
||||
{
|
||||
private long context = 0;
|
||||
|
||||
public synchronized void onFrameAvailable (SurfaceTexture surfaceTexture) {
|
||||
native_onFrameAvailable(context, surfaceTexture);
|
||||
}
|
||||
|
||||
public synchronized long getContext () {
|
||||
return context;
|
||||
}
|
||||
|
||||
public synchronized void setContext (long c) {
|
||||
context = c;
|
||||
}
|
||||
|
||||
private native void native_onFrameAvailable (long context, SurfaceTexture surfaceTexture);
|
||||
}
|
|
@ -0,0 +1,496 @@
|
|||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#include <QQuickWindow>
|
||||
#include <QQuickItem>
|
||||
#include <QRunnable>
|
||||
#include <QCommandLineParser>
|
||||
#include <QTimer>
|
||||
|
||||
#include <gst/gst.h>
|
||||
|
||||
#include "QGCLoggingCategory.h"
|
||||
|
||||
QGC_LOGGING_CATEGORY(AppLog, "VideoReceiverApp")
|
||||
|
||||
#if defined(__android__)
|
||||
#include <QtAndroidExtras>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
static jobject _class_loader = nullptr;
|
||||
static jobject _context = nullptr;
|
||||
|
||||
extern "C" {
|
||||
void gst_amc_jni_set_java_vm(JavaVM *java_vm);
|
||||
|
||||
jobject gst_android_get_application_class_loader(void) {
|
||||
return _class_loader;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_android_init(JNIEnv* env, jobject context)
|
||||
{
|
||||
jobject class_loader = nullptr;
|
||||
|
||||
jclass context_cls = env->GetObjectClass(context);
|
||||
|
||||
if (!context_cls) {
|
||||
return;
|
||||
}
|
||||
|
||||
jmethodID get_class_loader_id = env->GetMethodID(context_cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return;
|
||||
}
|
||||
|
||||
class_loader = env->CallObjectMethod(context, get_class_loader_id);
|
||||
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
env->ExceptionClear();
|
||||
return;
|
||||
}
|
||||
|
||||
_context = env->NewGlobalRef(context);
|
||||
_class_loader = env->NewGlobalRef(class_loader);
|
||||
}
|
||||
|
||||
static const char kJniClassName[] {"labs/mavlink/VideoReceiverApp/QGLSinkActivity"};
|
||||
|
||||
static void setNativeMethods(void)
|
||||
{
|
||||
JNINativeMethod javaMethods[] {
|
||||
{"nativeInit", "()V", reinterpret_cast<void *>(gst_android_init)}
|
||||
};
|
||||
|
||||
QAndroidJniEnvironment jniEnv;
|
||||
|
||||
if (jniEnv->ExceptionCheck()) {
|
||||
jniEnv->ExceptionDescribe();
|
||||
jniEnv->ExceptionClear();
|
||||
}
|
||||
|
||||
jclass objectClass = jniEnv->FindClass(kJniClassName);
|
||||
|
||||
if (!objectClass) {
|
||||
qWarning() << "Couldn't find class:" << kJniClassName;
|
||||
return;
|
||||
}
|
||||
|
||||
jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0]));
|
||||
|
||||
if (val < 0) {
|
||||
qWarning() << "Error registering methods: " << val;
|
||||
} else {
|
||||
qDebug() << "Main Native Functions Registered";
|
||||
}
|
||||
|
||||
if (jniEnv->ExceptionCheck()) {
|
||||
jniEnv->ExceptionDescribe();
|
||||
jniEnv->ExceptionClear();
|
||||
}
|
||||
}
|
||||
|
||||
jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
{
|
||||
Q_UNUSED(reserved);
|
||||
|
||||
JNIEnv* env;
|
||||
|
||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
setNativeMethods();
|
||||
|
||||
gst_amc_jni_set_java_vm(vm);
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <GStreamer.h>
|
||||
#include <VideoReceiver.h>
|
||||
|
||||
class VideoReceiverApp : public QRunnable
|
||||
{
|
||||
public:
|
||||
VideoReceiverApp(QCoreApplication& app, bool qmlAllowed)
|
||||
: _app(app)
|
||||
, _qmlAllowed(qmlAllowed)
|
||||
{}
|
||||
|
||||
void run();
|
||||
|
||||
int exec();
|
||||
|
||||
void startStreaming();
|
||||
void startDecoding();
|
||||
void startRecording();
|
||||
|
||||
protected:
|
||||
void _dispatch(std::function<void()> code);
|
||||
|
||||
private:
|
||||
QCoreApplication& _app;
|
||||
bool _qmlAllowed;
|
||||
VideoReceiver* _receiver = nullptr;
|
||||
QQuickWindow* _window = nullptr;
|
||||
QQuickItem* _widget = nullptr;
|
||||
void* _videoSink = nullptr;
|
||||
QString _url;
|
||||
unsigned _timeout = 5;
|
||||
unsigned _connect = 1;
|
||||
bool _decode = true;
|
||||
unsigned _stopDecodingAfter = 0;
|
||||
bool _record = false;
|
||||
QString _videoFile;
|
||||
unsigned int _fileFormat = VideoReceiver::FILE_FORMAT_MIN;
|
||||
unsigned _stopRecordingAfter = 15;
|
||||
bool _useFakeSink = false;
|
||||
bool _streaming = false;
|
||||
bool _decoding = false;
|
||||
bool _recording = false;
|
||||
};
|
||||
|
||||
void
|
||||
VideoReceiverApp::run()
|
||||
{
|
||||
if((_videoSink = GStreamer::createVideoSink(nullptr, _widget)) == nullptr) {
|
||||
qCDebug(AppLog) << "createVideoSink failed";
|
||||
return;
|
||||
}
|
||||
|
||||
_receiver->startDecoding(_videoSink);
|
||||
}
|
||||
|
||||
int
|
||||
VideoReceiverApp::exec()
|
||||
{
|
||||
QCommandLineParser parser;
|
||||
|
||||
parser.addHelpOption();
|
||||
|
||||
parser.addPositionalArgument("url",
|
||||
QCoreApplication::translate("main", "Source URL."));
|
||||
|
||||
QCommandLineOption timeoutOption(QStringList() << "t" << "timeout",
|
||||
QCoreApplication::translate("main", "Source timeout."),
|
||||
QCoreApplication::translate("main", "seconds"));
|
||||
|
||||
parser.addOption(timeoutOption);
|
||||
|
||||
QCommandLineOption connectOption(QStringList() << "c" << "connect",
|
||||
QCoreApplication::translate("main", "Number of connection attempts."),
|
||||
QCoreApplication::translate("main", "attempts"));
|
||||
|
||||
parser.addOption(connectOption);
|
||||
|
||||
QCommandLineOption decodeOption(QStringList() << "d" << "decode",
|
||||
QCoreApplication::translate("main", "Decode and render video."));
|
||||
|
||||
parser.addOption(decodeOption);
|
||||
|
||||
QCommandLineOption noDecodeOption("no-decode",
|
||||
QCoreApplication::translate("main", "Don't decode and render video."));
|
||||
|
||||
parser.addOption(noDecodeOption);
|
||||
|
||||
QCommandLineOption stopDecodingOption("stop-decoding",
|
||||
QCoreApplication::translate("main", "Stop decoding after time."),
|
||||
QCoreApplication::translate("main", "seconds"));
|
||||
|
||||
parser.addOption(stopDecodingOption);
|
||||
|
||||
QCommandLineOption recordOption(QStringList() << "r" << "record",
|
||||
QCoreApplication::translate("main", "Record video."),
|
||||
QGuiApplication::translate("main", "file"));
|
||||
|
||||
parser.addOption(recordOption);
|
||||
|
||||
QCommandLineOption formatOption(QStringList() << "f" << "format",
|
||||
QCoreApplication::translate("main", "File format."),
|
||||
QCoreApplication::translate("main", "format"));
|
||||
|
||||
parser.addOption(formatOption);
|
||||
|
||||
QCommandLineOption stopRecordingOption("stop-recording",
|
||||
QCoreApplication::translate("main", "Stop recording after time."),
|
||||
QCoreApplication::translate("main", "seconds"));
|
||||
|
||||
parser.addOption(stopRecordingOption);
|
||||
|
||||
QCommandLineOption videoSinkOption("video-sink",
|
||||
QCoreApplication::translate("main", "Use video sink: 0 - autovideosink, 1 - fakesink"),
|
||||
QCoreApplication::translate("main", "sink"));
|
||||
|
||||
if (!_qmlAllowed) {
|
||||
parser.addOption(videoSinkOption);
|
||||
}
|
||||
|
||||
parser.process(_app);
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
|
||||
if (args.size() != 1) {
|
||||
parser.showHelp(0);
|
||||
}
|
||||
|
||||
_url = args.at(0);
|
||||
|
||||
if (parser.isSet(timeoutOption)) {
|
||||
_timeout = parser.value(timeoutOption).toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(connectOption)) {
|
||||
_connect = parser.value(connectOption).toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(decodeOption) && parser.isSet(noDecodeOption)) {
|
||||
parser.showHelp(0);
|
||||
}
|
||||
|
||||
if (parser.isSet(decodeOption)) {
|
||||
_decode = true;
|
||||
}
|
||||
|
||||
if (parser.isSet(noDecodeOption)) {
|
||||
_decode = false;
|
||||
}
|
||||
|
||||
if (_decode && parser.isSet(stopDecodingOption)) {
|
||||
_stopDecodingAfter = parser.value(stopDecodingOption).toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(recordOption)) {
|
||||
_record = true;
|
||||
_videoFile = parser.value(recordOption);
|
||||
}
|
||||
|
||||
if (parser.isSet(formatOption)) {
|
||||
_fileFormat += parser.value(formatOption).toUInt();
|
||||
}
|
||||
|
||||
if (_record && parser.isSet(stopRecordingOption)) {
|
||||
_stopRecordingAfter = parser.value(stopRecordingOption).toUInt();
|
||||
}
|
||||
|
||||
if (parser.isSet(videoSinkOption)) {
|
||||
_useFakeSink = parser.value(videoSinkOption).toUInt() > 0;
|
||||
}
|
||||
|
||||
_receiver = GStreamer::createVideoReceiver(nullptr);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
if (_decode && _qmlAllowed) {
|
||||
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
|
||||
|
||||
_window = static_cast<QQuickWindow*>(engine.rootObjects().first());
|
||||
Q_ASSERT(_window != nullptr);
|
||||
|
||||
_widget = _window->findChild<QQuickItem*>("videoItem");
|
||||
Q_ASSERT(_widget != nullptr);
|
||||
}
|
||||
|
||||
startStreaming();
|
||||
|
||||
QObject::connect(_receiver, &VideoReceiver::timeout, [](){
|
||||
qCDebug(AppLog) << "Streaming timeout";
|
||||
});
|
||||
|
||||
QObject::connect(_receiver, &VideoReceiver::streamingChanged, [this](bool active){
|
||||
_streaming = active;
|
||||
if (_streaming) {
|
||||
qCDebug(AppLog) << "Streaming started";
|
||||
} else {
|
||||
qCDebug(AppLog) << "Streaming stopped";
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(_receiver, &VideoReceiver::decodingChanged, [this](bool active){
|
||||
_decoding = active;
|
||||
if (_decoding) {
|
||||
qCDebug(AppLog) << "Decoding started";
|
||||
} else {
|
||||
qCDebug(AppLog) << "Decoding stopped";
|
||||
if (_streaming) {
|
||||
if (!_recording) {
|
||||
_dispatch([this](){
|
||||
_receiver->stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(_receiver, &VideoReceiver::recordingChanged, [this](bool active){
|
||||
_recording = active;
|
||||
if (_recording) {
|
||||
qCDebug(AppLog) << "Recording started";
|
||||
} else {
|
||||
qCDebug(AppLog) << "Recording stopped";
|
||||
if (_streaming) {
|
||||
if (!_decoding) {
|
||||
_dispatch([this](){
|
||||
_receiver->stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(_receiver, &VideoReceiver::onStartComplete, [this](VideoReceiver::STATUS status){
|
||||
if (status != VideoReceiver::STATUS_OK) {
|
||||
qCDebug(AppLog) << "Video receiver start failed";
|
||||
_dispatch([this](){
|
||||
if (--_connect > 0) {
|
||||
qCDebug(AppLog) << "Restarting ...";
|
||||
_dispatch([this](){
|
||||
startStreaming();
|
||||
});
|
||||
} else {
|
||||
qCDebug(AppLog) << "Closing...";
|
||||
delete _receiver;
|
||||
_app.exit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
qCDebug(AppLog) << "Video receiver started";
|
||||
}
|
||||
});
|
||||
|
||||
QObject::connect(_receiver, &VideoReceiver::onStopComplete, [this](VideoReceiver::STATUS ){
|
||||
qCDebug(AppLog) << "Video receiver stopped";
|
||||
|
||||
_dispatch([this](){
|
||||
if (--_connect > 0) {
|
||||
qCDebug(AppLog) << "Restarting ...";
|
||||
_dispatch([this](){
|
||||
startStreaming();
|
||||
});
|
||||
} else {
|
||||
qCDebug(AppLog) << "Closing...";
|
||||
delete _receiver;
|
||||
_app.exit();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return _app.exec();
|
||||
}
|
||||
|
||||
void
|
||||
VideoReceiverApp::startStreaming()
|
||||
{
|
||||
_receiver->start(_url, _timeout);
|
||||
|
||||
if (_decode) {
|
||||
startDecoding();
|
||||
}
|
||||
|
||||
if (_record) {
|
||||
startRecording();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoReceiverApp::startDecoding()
|
||||
{
|
||||
if (_qmlAllowed) {
|
||||
_window->scheduleRenderJob(this, QQuickWindow::BeforeSynchronizingStage);
|
||||
} else {
|
||||
if (_videoSink == nullptr) {
|
||||
if ((_videoSink = gst_element_factory_make(_useFakeSink ? "fakesink" : "autovideosink", nullptr)) == nullptr) {
|
||||
qCDebug(AppLog) << "Failed to create video sink";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_receiver->startDecoding(_videoSink);
|
||||
}
|
||||
|
||||
if (_stopDecodingAfter > 0) {
|
||||
unsigned connect = _connect;
|
||||
QTimer::singleShot(_stopDecodingAfter * 1000, Qt::PreciseTimer, [this, connect](){
|
||||
if (connect != _connect) {
|
||||
return;
|
||||
}
|
||||
_receiver->stopDecoding();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoReceiverApp::startRecording()
|
||||
{
|
||||
_receiver->startRecording(_videoFile, static_cast<VideoReceiver::FILE_FORMAT>(_fileFormat));
|
||||
|
||||
if (_stopRecordingAfter > 0) {
|
||||
unsigned connect = _connect;
|
||||
QTimer::singleShot(_stopRecordingAfter * 1000, [this, connect](){
|
||||
if (connect != _connect) {
|
||||
return;
|
||||
}
|
||||
_receiver->stopRecording();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoReceiverApp::_dispatch(std::function<void()> code)
|
||||
{
|
||||
QTimer* timer = new QTimer();
|
||||
timer->moveToThread(qApp->thread());
|
||||
timer->setSingleShot(true);
|
||||
QObject::connect(timer, &QTimer::timeout, [=](){
|
||||
code();
|
||||
timer->deleteLater();
|
||||
});
|
||||
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
}
|
||||
|
||||
|
||||
static bool isQtApp(const char* app)
|
||||
{
|
||||
const char* s;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
if ((s = strrchr(app, '\\')) != nullptr) {
|
||||
#else
|
||||
if ((s = strrchr(app, '/')) != nullptr) {
|
||||
#endif
|
||||
s += 1;
|
||||
} else {
|
||||
s = app;
|
||||
}
|
||||
|
||||
return s[0] == 'Q' || s[0] == 'q';
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GStreamer::initialize(argc, argv, 3);
|
||||
|
||||
if (isQtApp(argv[0])) {
|
||||
QGuiApplication app(argc, argv);
|
||||
VideoReceiverApp videoApp(app, true);
|
||||
return videoApp.exec();
|
||||
} else {
|
||||
QCoreApplication app(argc, argv);
|
||||
VideoReceiverApp videoApp(app, false);
|
||||
return videoApp.exec();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import QtQuick 2.12
|
||||
import QtQuick.Window 2.12
|
||||
import QtQuick.Layouts 1.3
|
||||
import org.freedesktop.gstreamer.GLVideoItem 1.0
|
||||
|
||||
Window {
|
||||
visible: true
|
||||
width: 640
|
||||
height: 480
|
||||
title: qsTr("VideoReceiverApp")
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
GstGLVideoItem {
|
||||
id: video
|
||||
objectName: "videoItem"
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1,110 @@
|
|||
QT += androidextras
|
||||
|
||||
include($$PWD/libs/qtandroidserialport/src/qtandroidserialport.pri)
|
||||
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$OUT_PWD/ANDROID_PACKAGE_SOURCE_DIR # Tells Qt location of package files for build
|
||||
ANDROID_PACKAGE_QGC_SOURCE_DIR = $$PWD/android # Original location of QGC package files
|
||||
ANDROID_PACKAGE_CUSTOM_SOURCE_DIR = $$PWD/custom/android # Original location for custom build override package files
|
||||
|
||||
# We always move the package files to the ANDROID_PACKAGE_SOURCE_DIR build dir so we can modify the manifest as needed
|
||||
|
||||
android_source_dir_target.target = $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
|
||||
android_source_dir_target.commands = \
|
||||
$$QMAKE_MKDIR $$ANDROID_PACKAGE_SOURCE_DIR && \
|
||||
$$QMAKE_COPY_DIR $$ANDROID_PACKAGE_QGC_SOURCE_DIR/* $$ANDROID_PACKAGE_SOURCE_DIR
|
||||
PRE_TARGETDEPS += $$android_source_dir_target.target
|
||||
QMAKE_EXTRA_TARGETS += android_source_dir_target
|
||||
exists($$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR/AndroidManifest.xml) {
|
||||
android_source_dir_target.depends = $$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR/AndroidManifest.xml
|
||||
} else {
|
||||
android_source_dir_target.depends = $$ANDROID_PACKAGE_QGC_SOURCE_DIR/AndroidManifest.xml
|
||||
}
|
||||
|
||||
# Custom builds can override android package file
|
||||
|
||||
exists($$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR) {
|
||||
message("Merging$$ $$ANDROID_PACKAGE_QGC_SOURCE_DIR and $$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR to $$ANDROID_PACKAGE_SOURCE_DIR")
|
||||
|
||||
android_source_dir_target.commands = $$android_source_dir_target.commands && \
|
||||
$$QMAKE_COPY_DIR $$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR/* $$ANDROID_PACKAGE_SOURCE_DIR && \
|
||||
$$QMAKE_STREAM_EDITOR -i \"s/package=\\\"org.mavlink.qgroundcontrol\\\"/package=\\\"$$QGC_ANDROID_PACKAGE\\\"/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
|
||||
}
|
||||
|
||||
# Insert package name into manifest file
|
||||
|
||||
android_source_dir_target.commands = $$android_source_dir_target.commands && \
|
||||
$$QMAKE_STREAM_EDITOR -i \"s/%%QGC_INSERT_PACKAGE_NAME%%/$$QGC_ANDROID_PACKAGE/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
|
||||
|
||||
# Update manifest activity intent filter as needed
|
||||
|
||||
QGC_INSERT_ACTIVITY_INTENT_FILTER = ""
|
||||
AndroidHomeApp {
|
||||
# QGC is the android home application
|
||||
QGC_INSERT_ACTIVITY_INTENT_FILTER = $$QGC_INSERT_ACTIVITY_INTENT_FILTER "\r\n<category android:name=\\\"android.intent.category.HOME\\\"\\\/>\r\n<category android:name=\\\"android.intent.category.DEFAULT\\\"\\\/>"
|
||||
}
|
||||
!contains(DEFINES, NO_SERIAL_LINK) {
|
||||
# Add usb device support
|
||||
QGC_INSERT_ACTIVITY_INTENT_FILTER = $$QGC_INSERT_ACTIVITY_INTENT_FILTER "\r\n<action android:name=\\\"android.hardware.usb.action.USB_DEVICE_ATTACHED\\\"\\\/>\r\n<action android:name=\\\"android.hardware.usb.action.USB_DEVICE_DETACHED\\\"\\\/>\r\n<action android:name=\\\"android.hardware.usb.action.USB_ACCESSORY_ATTACHED\\\"\\\/>"
|
||||
}
|
||||
contains(DEFINES, QGC_ENABLE_BLUETOOTH) {
|
||||
QGC_INSERT_ACTIVITY_INTENT_FILTER = $$QGC_INSERT_ACTIVITY_INTENT_FILTER "\r\n<action android:name=\\\"android.bluetooth.device.action.ACL_CONNECTED\\\"\\\/>\r\n<action android:name=\\\"android.bluetooth.device.action.ACL_DISCONNECTED\\\"\\\/>"
|
||||
}
|
||||
android_source_dir_target.commands = $$android_source_dir_target.commands && \
|
||||
$$QMAKE_STREAM_EDITOR -i \"s/<!-- %%QGC_INSERT_ACTIVITY_INTENT_FILTER -->/$$QGC_INSERT_ACTIVITY_INTENT_FILTER/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
|
||||
|
||||
# Update manifest activity meta data as needed
|
||||
|
||||
contains(DEFINES, NO_SERIAL_LINK) {
|
||||
# No need to add anything to manifest
|
||||
android_source_dir_target.commands = $$android_source_dir_target.commands && \
|
||||
$$QMAKE_STREAM_EDITOR -i \"s/<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->//\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
|
||||
} else {
|
||||
# Updates the manifest for usb device support
|
||||
android_source_dir_target.commands = $$android_source_dir_target.commands && \
|
||||
$$QMAKE_STREAM_EDITOR -i \"s/<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->/<meta-data android:resource=\\\"@xml\\\/device_filter\\\" android:name=\\\"android.hardware.usb.action.USB_DEVICE_ATTACHED\\\"\\\/>\r\n<meta-data android:resource=\\\"@xml\\\/device_filter\\\" android:name=\\\"android.hardware.usb.action.USB_DEVICE_DETACHED\\\"\\\/>\r\n<meta-data android:resource=\\\"@xml\\\/device_filter\\\" android:name=\\\"android.hardware.usb.action.USB_ACCESSORY_ATTACHED\\\"\\\/>/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
|
||||
}
|
||||
|
||||
# OTHER_FILES makes the specified files be visible in Qt Creator for editing
|
||||
|
||||
exists($$PWD/custom/android/AndroidManifest.xml) {
|
||||
OTHER_FILES += \
|
||||
$$PWD/custom/android/AndroidManifest.xml
|
||||
} else {
|
||||
OTHER_FILES += \
|
||||
$$PWD/android/AndroidManifest.xml
|
||||
}
|
||||
|
||||
OTHER_FILES += \
|
||||
$$PWD/android/res/xml/device_filter.xml \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbId.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \
|
||||
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \
|
||||
$$PWD/android/src/org/mavlink/qgroundcontrol/QGCActivity.java \
|
||||
$$PWD/android/src/org/mavlink/qgroundcontrol/UsbIoManager.java \
|
||||
$$PWD/android/src/org/mavlink/qgroundcontrol/TaiSync.java \
|
||||
$$PWD/android/src/org/freedesktop/gstreamer/androidmedia/GstAhcCallback.java \
|
||||
$$PWD/android/src/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java \
|
||||
$$PWD/android/src/org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener.java
|
||||
|
||||
DISTFILES += \
|
||||
$$PWD/android/gradle/wrapper/gradle-wrapper.jar \
|
||||
$$PWD/android/gradlew \
|
||||
$$PWD/android/res/values/libs.xml \
|
||||
$$PWD/android/build.gradle \
|
||||
$$PWD/android/gradle/wrapper/gradle-wrapper.properties \
|
||||
$$PWD/android/gradlew.bat
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/android/src/AndroidInterface.cc
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/android/src/AndroidInterface.h
|
||||
|
||||
INCLUDEPATH += \
|
||||
$$PWD/android/src
|
|
@ -0,0 +1,104 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest package="%%QGC_INSERT_PACKAGE_NAME%%" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.mavlink.qgroundcontrol.QGCActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="sensorLandscape" android:launchMode="singleTask" android:keepScreenOn="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
|
||||
<!-- %%QGC_INSERT_ACTIVITY_INTENT_FILTER -->
|
||||
</intent-filter>
|
||||
|
||||
<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->
|
||||
|
||||
<!-- Application arguments -->
|
||||
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
|
||||
<!-- Application arguments -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Used to specify custom system library path to run with local system libs -->
|
||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
|
||||
<!-- Messages maps -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
|
||||
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
|
||||
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
|
||||
are done populating your window with content. -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
|
||||
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
|
||||
<!-- Splash screen -->
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<!-- Background running -->
|
||||
<!-- auto screen scale factor -->
|
||||
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
|
||||
<!-- auto screen scale factor -->
|
||||
<!-- extract android style -->
|
||||
<!-- available android:values :
|
||||
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
|
||||
* full - useful QWidget & Quick Controls 1 apps
|
||||
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
|
||||
* none - useful for apps that don't use any of the above Qt modules
|
||||
-->
|
||||
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
|
||||
<!-- extract android style -->
|
||||
</activity>
|
||||
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
|
||||
</application>
|
||||
|
||||
<uses-sdk android:targetSdkVersion="30"/>
|
||||
|
||||
<!-- Needed to keep working while 'asleep' -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||
|
||||
<!-- Need MulticastLock to receive broadcast UDP packets -->
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
|
||||
|
||||
<!-- Support devices without USB host mode since there are other connection types -->
|
||||
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
|
||||
|
||||
<!-- Support devices without Bluetooth since there are other connection types -->
|
||||
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
|
||||
|
||||
<!-- Support devices that don't have location services -->
|
||||
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.location" android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.usb.accessory" android:required="false"/>
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
</manifest>
|
After Width: | Height: | Size: 173 KiB |
After Width: | Height: | Size: 787 KiB |
After Width: | Height: | Size: 2.3 MiB |
After Width: | Height: | Size: 1.8 MiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,22 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<array name="qt_sources">
|
||||
<item>https://download.qt.io/ministro/android/qt5/qt-5.14</item>
|
||||
</array>
|
||||
|
||||
<!-- The following is handled automatically by the deployment tool. It should
|
||||
not be edited manually. -->
|
||||
|
||||
<array name="bundled_libs">
|
||||
<!-- %%INSERT_EXTRA_LIBS%% -->
|
||||
</array>
|
||||
|
||||
<array name="qt_libs">
|
||||
<!-- %%INSERT_QT_LIBS%% -->
|
||||
</array>
|
||||
|
||||
<array name="load_local_libs">
|
||||
<!-- %%INSERT_LOCAL_LIBS%% -->
|
||||
</array>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Allow anything connected -->
|
||||
<usb-device />
|
||||
<usb-accessory model="android.usbaoa" manufacturer="taisync" version="1.0"/>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
|
||||
*
|
||||
* QGroundControl is licensed according to the terms in the file
|
||||
* COPYING.md in the root of the source code directory.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtAndroidExtras/QtAndroidExtras>
|
||||
#include <QtAndroidExtras/QAndroidJniObject>
|
||||
#include "QGCApplication.h"
|
||||
#include "AndroidInterface.h"
|
||||
#include <QAndroidJniObject>
|
||||
#include <QtAndroid>
|
||||
|
||||
QString AndroidInterface::getSDCardPath()
|
||||
{
|
||||
QAndroidJniObject value = QAndroidJniObject::callStaticObjectMethod("org/mavlink/qgroundcontrol/QGCActivity", "getSDCardPath",
|
||||
"()Ljava/lang/String;");
|
||||
QString sdCardPath = value.toString();
|
||||
|
||||
QString readPermission("android.permission.READ_EXTERNAL_STORAGE");
|
||||
QString writePermission("android.permission.WRITE_EXTERNAL_STORAGE");
|
||||
|
||||
if (QtAndroid::checkPermission(readPermission) == QtAndroid::PermissionResult::Denied ||
|
||||
QtAndroid::checkPermission(writePermission) == QtAndroid::PermissionResult::Denied) {
|
||||
QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync(QStringList({ readPermission, writePermission }));
|
||||
if (resultHash[readPermission] == QtAndroid::PermissionResult::Denied ||
|
||||
resultHash[writePermission] == QtAndroid::PermissionResult::Denied) {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
return sdCardPath;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/****************************************************************************
|
||||
*
|
||||
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
|
||||
*
|
||||
* QGroundControl is licensed according to the terms in the file
|
||||
* COPYING.md in the root of the source code directory.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <jni.h>
|
||||
#include <QtCore/private/qjni_p.h>
|
||||
#include <QtCore/private/qjnihelpers_p.h>
|
||||
|
||||
class AndroidInterface
|
||||
{
|
||||
public:
|
||||
static QString getSDCardPath();
|
||||
};
|
|
@ -0,0 +1,322 @@
|
|||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* USB CDC/ACM serial driver implementation.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
* @see <a
|
||||
* href="http://www.usb.org/developers/devclass_docs/usbcdc11.pdf">Universal
|
||||
* Serial Bus Class Definitions for Communication Devices, v1.1</a>
|
||||
*/
|
||||
public class CdcAcmSerialDriver extends CommonUsbSerialDriver {
|
||||
|
||||
private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
|
||||
|
||||
private UsbInterface mControlInterface;
|
||||
private UsbInterface mDataInterface;
|
||||
|
||||
private UsbEndpoint mControlEndpoint;
|
||||
private UsbEndpoint mReadEndpoint;
|
||||
private UsbEndpoint mWriteEndpoint;
|
||||
|
||||
private boolean mRts = false;
|
||||
private boolean mDtr = false;
|
||||
|
||||
private static final int USB_RECIP_INTERFACE = 0x01;
|
||||
private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
|
||||
private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2
|
||||
private static final int GET_LINE_CODING = 0x21;
|
||||
private static final int SET_CONTROL_LINE_STATE = 0x22;
|
||||
private static final int SEND_BREAK = 0x23;
|
||||
|
||||
public CdcAcmSerialDriver(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
Log.d(TAG, "device " + mDevice);
|
||||
mControlInterface = null;
|
||||
mDataInterface = null;
|
||||
mWriteEndpoint = null;
|
||||
mReadEndpoint = null;
|
||||
|
||||
// locate all needed interfaces
|
||||
for(int i = 0; i < mDevice.getInterfaceCount();i++){
|
||||
UsbInterface iface = mDevice.getInterface(i);
|
||||
|
||||
switch(iface.getInterfaceClass()){
|
||||
case UsbConstants.USB_CLASS_COMM:
|
||||
mControlInterface = iface;
|
||||
Log.d(TAG, "control iface=" + iface);
|
||||
break;
|
||||
case UsbConstants.USB_CLASS_CDC_DATA:
|
||||
mDataInterface = iface;
|
||||
Log.d(TAG, "data iface=" + iface);
|
||||
break;
|
||||
default:
|
||||
Log.d(TAG, "skipping iface=" + iface);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// failback to the old way
|
||||
if(mControlInterface == null) {
|
||||
mControlInterface = mDevice.getInterface(0);
|
||||
Log.d(TAG, "Failback: Control iface=" + mControlInterface);
|
||||
}
|
||||
|
||||
if (!mConnection.claimInterface(mControlInterface, true)) {
|
||||
throw new IOException("Could not claim control interface.");
|
||||
}
|
||||
|
||||
mControlEndpoint = mControlInterface.getEndpoint(0);
|
||||
Log.d(TAG, "Control endpoint: " + mControlEndpoint);
|
||||
|
||||
|
||||
if(mDataInterface == null) {
|
||||
mDataInterface = mDevice.getInterface(1);
|
||||
Log.d(TAG, "Failback: data iface=" + mDataInterface);
|
||||
}
|
||||
|
||||
if (!mConnection.claimInterface(mDataInterface, true)) {
|
||||
throw new IOException("Could not claim data interface.");
|
||||
}
|
||||
|
||||
for(int i = 0; i < mDataInterface.getEndpointCount(); i++) {
|
||||
UsbEndpoint endpoint = mDataInterface.getEndpoint(i);
|
||||
switch (endpoint.getDirection()) {
|
||||
case UsbConstants.USB_DIR_OUT:
|
||||
mWriteEndpoint = endpoint;
|
||||
Log.d(TAG, "Write endpoint: " + mWriteEndpoint);
|
||||
break;
|
||||
case UsbConstants.USB_DIR_IN:
|
||||
mReadEndpoint = endpoint;
|
||||
Log.d(TAG, "Read endpoint: " + mReadEndpoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(mReadEndpoint == null || mWriteEndpoint == null){
|
||||
// failback to the old method
|
||||
mReadEndpoint = mDataInterface.getEndpoint(0);
|
||||
Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection());
|
||||
mWriteEndpoint = mDataInterface.getEndpoint(1);
|
||||
Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection());
|
||||
}
|
||||
}
|
||||
|
||||
private int sendAcmControlMessage(int request, int value, byte[] buf) {
|
||||
return mConnection.controlTransfer(
|
||||
USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
mConnection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||
final int numBytesRead;
|
||||
synchronized (mReadBufferLock) {
|
||||
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
|
||||
timeoutMillis);
|
||||
if (numBytesRead < 0) {
|
||||
// This sucks: we get -1 on timeout, not 0 as preferred.
|
||||
// We *should* use UsbRequest, except it has a bug/api oversight
|
||||
// where there is no way to determine the number of bytes read
|
||||
// in response :\ -- http://b.android.com/28023
|
||||
return 0;
|
||||
}
|
||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||
}
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
// TODO(mikey): Nearly identical to FtdiSerial write. Refactor.
|
||||
int offset = 0;
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
|
||||
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
|
||||
if (offset == 0) {
|
||||
writeBuffer = src;
|
||||
} else {
|
||||
// bulkTransfer does not support offsets, make a copy.
|
||||
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
|
||||
writeBuffer = mWriteBuffer;
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
|
||||
timeoutMillis);
|
||||
}
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length=" + src.length);
|
||||
}
|
||||
|
||||
//Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) {
|
||||
byte stopBitsByte;
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1: stopBitsByte = 0; break;
|
||||
case STOPBITS_1_5: stopBitsByte = 1; break;
|
||||
case STOPBITS_2: stopBitsByte = 2; break;
|
||||
default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits);
|
||||
}
|
||||
|
||||
byte parityBitesByte;
|
||||
switch (parity) {
|
||||
case PARITY_NONE: parityBitesByte = 0; break;
|
||||
case PARITY_ODD: parityBitesByte = 1; break;
|
||||
case PARITY_EVEN: parityBitesByte = 2; break;
|
||||
case PARITY_MARK: parityBitesByte = 3; break;
|
||||
case PARITY_SPACE: parityBitesByte = 4; break;
|
||||
default: throw new IllegalArgumentException("Bad value for parity: " + parity);
|
||||
}
|
||||
|
||||
byte[] msg = {
|
||||
(byte) ( baudRate & 0xff),
|
||||
(byte) ((baudRate >> 8 ) & 0xff),
|
||||
(byte) ((baudRate >> 16) & 0xff),
|
||||
(byte) ((baudRate >> 24) & 0xff),
|
||||
stopBitsByte,
|
||||
parityBitesByte,
|
||||
(byte) dataBits};
|
||||
sendAcmControlMessage(SET_LINE_CODING, 0, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return mDtr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
mDtr = value;
|
||||
setDtrRts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false; // TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return mRts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
mRts = value;
|
||||
setDtrRts();
|
||||
}
|
||||
|
||||
private void setDtrRts() {
|
||||
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
|
||||
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUINO),
|
||||
new int[] {
|
||||
UsbId.ARDUINO_UNO,
|
||||
UsbId.ARDUINO_UNO_R3,
|
||||
UsbId.ARDUINO_MEGA_2560,
|
||||
UsbId.ARDUINO_MEGA_2560_R3,
|
||||
UsbId.ARDUINO_SERIAL_ADAPTER,
|
||||
UsbId.ARDUINO_SERIAL_ADAPTER_R3,
|
||||
UsbId.ARDUINO_MEGA_ADK,
|
||||
UsbId.ARDUINO_MEGA_ADK_R3,
|
||||
UsbId.ARDUINO_LEONARDO,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_VAN_OOIJEN_TECH),
|
||||
new int[] {
|
||||
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ATMEL),
|
||||
new int[] {
|
||||
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_LEAFLABS),
|
||||
new int[] {
|
||||
UsbId.LEAFLABS_MAPLE,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PX4),
|
||||
new int[] {
|
||||
UsbId.DEVICE_PX4FMU,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_UBLOX),
|
||||
new int[] {
|
||||
UsbId.DEVICE_UBLOX_5,
|
||||
UsbId.DEVICE_UBLOX_6,
|
||||
UsbId.DEVICE_UBLOX_7,
|
||||
UsbId.DEVICE_UBLOX_8,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_OPENPILOT),
|
||||
new int[] {
|
||||
UsbId.DEVICE_CC3D,
|
||||
UsbId.DEVICE_REVOLUTION,
|
||||
UsbId.DEVICE_SPARKY2,
|
||||
UsbId.DEVICE_OPLINK,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUPILOT_CHIBIOS1),
|
||||
new int[] {
|
||||
UsbId.DEVICE_ARDUPILOT_CHIBIOS,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUPILOT_CHIBIOS2),
|
||||
new int[] {
|
||||
UsbId.DEVICE_ARDUPILOT_CHIBIOS,
|
||||
});
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_DRAGONLINK),
|
||||
new int[] {
|
||||
UsbId.DEVICE_DRAGONLINK,
|
||||
});
|
||||
return supportedDevices;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/* Copyright 2013 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A base class shared by several driver implementations.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
abstract class CommonUsbSerialDriver implements UsbSerialDriver {
|
||||
|
||||
public static final int DEFAULT_READ_BUFFER_SIZE = 16 * 1024;
|
||||
public static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024;
|
||||
|
||||
protected final UsbDevice mDevice;
|
||||
protected final Object mReadBufferLock = new Object();
|
||||
protected final Object mWriteBufferLock = new Object();
|
||||
|
||||
protected UsbDeviceConnection mConnection = null;
|
||||
|
||||
private int _permissionStatus = permissionStatusRequestRequired;
|
||||
|
||||
/** Internal read buffer. Guarded by {@link #mReadBufferLock}. */
|
||||
protected byte[] mReadBuffer;
|
||||
|
||||
/** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */
|
||||
protected byte[] mWriteBuffer;
|
||||
|
||||
public CommonUsbSerialDriver(UsbDevice device) {
|
||||
mDevice = device;
|
||||
|
||||
mReadBuffer = new byte[DEFAULT_READ_BUFFER_SIZE];
|
||||
mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnection(UsbDeviceConnection connection) {
|
||||
mConnection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int permissionStatus() {
|
||||
return _permissionStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPermissionStatus(int permissionStatus) {
|
||||
_permissionStatus = permissionStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently-bound USB device.
|
||||
*
|
||||
* @return the device
|
||||
*/
|
||||
@Override
|
||||
public final UsbDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently-bound USB device connection.
|
||||
*
|
||||
* @return the device connection
|
||||
*/
|
||||
@Override
|
||||
public final UsbDeviceConnection getDeviceConnection() {
|
||||
return mConnection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the size of the internal buffer used to exchange data with the USB
|
||||
* stack for read operations. Most users should not need to change this.
|
||||
*
|
||||
* @param bufferSize the size in bytes
|
||||
*/
|
||||
public final void setReadBufferSize(int bufferSize) {
|
||||
synchronized (mReadBufferLock) {
|
||||
if (bufferSize == mReadBuffer.length) {
|
||||
return;
|
||||
}
|
||||
mReadBuffer = new byte[bufferSize];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the internal buffer used to exchange data with the USB
|
||||
* stack for write operations. Most users should not need to change this.
|
||||
*
|
||||
* @param bufferSize the size in bytes
|
||||
*/
|
||||
public final void setWriteBufferSize(int bufferSize) {
|
||||
synchronized (mWriteBufferLock) {
|
||||
if (bufferSize == mWriteBuffer.length) {
|
||||
return;
|
||||
}
|
||||
mWriteBuffer = new byte[bufferSize];
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void open() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract void close() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract int read(final byte[] dest, final int timeoutMillis) throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract int write(final byte[] src, final int timeoutMillis) throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract void setParameters(
|
||||
int baudRate, int dataBits, int stopBits, int parity) throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract boolean getCD() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract boolean getCTS() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract boolean getDSR() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract boolean getDTR() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract void setDTR(boolean value) throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract boolean getRI() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract boolean getRTS() throws IOException;
|
||||
|
||||
@Override
|
||||
public abstract void setRTS(boolean value) throws IOException;
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean flushReadBuffers, boolean flushWriteBuffers) throws IOException {
|
||||
return !flushReadBuffers && !flushWriteBuffers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Cp2102SerialDriver extends CommonUsbSerialDriver {
|
||||
|
||||
private static final String TAG = Cp2102SerialDriver.class.getSimpleName();
|
||||
|
||||
private static final int DEFAULT_BAUD_RATE = 9600;
|
||||
|
||||
private static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
/*
|
||||
* Configuration Request Types
|
||||
*/
|
||||
private static final int REQTYPE_HOST_TO_DEVICE = 0x41;
|
||||
|
||||
/*
|
||||
* Configuration Request Codes
|
||||
*/
|
||||
private static final int SILABSER_IFC_ENABLE_REQUEST_CODE = 0x00;
|
||||
private static final int SILABSER_SET_BAUDDIV_REQUEST_CODE = 0x01;
|
||||
private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03;
|
||||
private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07;
|
||||
private static final int SILABSER_SET_BAUDRATE = 0x1E;
|
||||
private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12;
|
||||
|
||||
private static final int FLUSH_READ_CODE = 0x0a;
|
||||
private static final int FLUSH_WRITE_CODE = 0x05;
|
||||
|
||||
/*
|
||||
* SILABSER_IFC_ENABLE_REQUEST_CODE
|
||||
*/
|
||||
private static final int UART_ENABLE = 0x0001;
|
||||
private static final int UART_DISABLE = 0x0000;
|
||||
|
||||
/*
|
||||
* SILABSER_SET_BAUDDIV_REQUEST_CODE
|
||||
*/
|
||||
private static final int BAUD_RATE_GEN_FREQ = 0x384000;
|
||||
|
||||
/*
|
||||
* SILABSER_SET_MHS_REQUEST_CODE
|
||||
*/
|
||||
private static final int MCR_DTR = 0x0001;
|
||||
private static final int MCR_RTS = 0x0002;
|
||||
private static final int MCR_ALL = 0x0003;
|
||||
|
||||
private static final int CONTROL_WRITE_DTR = 0x0100;
|
||||
private static final int CONTROL_WRITE_RTS = 0x0200;
|
||||
|
||||
private UsbEndpoint mReadEndpoint;
|
||||
private UsbEndpoint mWriteEndpoint;
|
||||
|
||||
public Cp2102SerialDriver(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
private int setConfigSingle(int request, int value) {
|
||||
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
|
||||
0, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
boolean opened = false;
|
||||
try {
|
||||
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
|
||||
UsbInterface usbIface = mDevice.getInterface(i);
|
||||
if (mConnection.claimInterface(usbIface, true)) {
|
||||
Log.d(TAG, "claimInterface " + i + " SUCCESS");
|
||||
} else {
|
||||
Log.d(TAG, "claimInterface " + i + " FAIL");
|
||||
}
|
||||
}
|
||||
|
||||
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
|
||||
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
|
||||
UsbEndpoint ep = dataIface.getEndpoint(i);
|
||||
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
|
||||
if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
|
||||
mReadEndpoint = ep;
|
||||
} else {
|
||||
mWriteEndpoint = ep;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE);
|
||||
setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, MCR_ALL | CONTROL_WRITE_DTR | CONTROL_WRITE_RTS);
|
||||
setConfigSingle(SILABSER_SET_BAUDDIV_REQUEST_CODE, BAUD_RATE_GEN_FREQ / DEFAULT_BAUD_RATE);
|
||||
// setParameters(DEFAULT_BAUD_RATE, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY);
|
||||
opened = true;
|
||||
} finally {
|
||||
if (!opened) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
|
||||
mConnection.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||
final int numBytesRead;
|
||||
synchronized (mReadBufferLock) {
|
||||
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
|
||||
timeoutMillis);
|
||||
if (numBytesRead < 0) {
|
||||
// This sucks: we get -1 on timeout, not 0 as preferred.
|
||||
// We *should* use UsbRequest, except it has a bug/api oversight
|
||||
// where there is no way to determine the number of bytes read
|
||||
// in response :\ -- http://b.android.com/28023
|
||||
return 0;
|
||||
}
|
||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||
}
|
||||
return numBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
int offset = 0;
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
|
||||
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
|
||||
if (offset == 0) {
|
||||
writeBuffer = src;
|
||||
} else {
|
||||
// bulkTransfer does not support offsets, make a copy.
|
||||
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
|
||||
writeBuffer = mWriteBuffer;
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
|
||||
timeoutMillis);
|
||||
}
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length=" + src.length);
|
||||
}
|
||||
|
||||
//Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
private void setBaudRate(int baudRate) throws IOException {
|
||||
byte[] data = new byte[] {
|
||||
(byte) ( baudRate & 0xff),
|
||||
(byte) ((baudRate >> 8 ) & 0xff),
|
||||
(byte) ((baudRate >> 16) & 0xff),
|
||||
(byte) ((baudRate >> 24) & 0xff)
|
||||
};
|
||||
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE,
|
||||
0, 0, data, 4, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (ret < 0) {
|
||||
throw new IOException("Error setting baud rate.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
|
||||
throws IOException {
|
||||
setBaudRate(baudRate);
|
||||
|
||||
int configDataBits = 0;
|
||||
switch (dataBits) {
|
||||
case DATABITS_5:
|
||||
configDataBits |= 0x0500;
|
||||
break;
|
||||
case DATABITS_6:
|
||||
configDataBits |= 0x0600;
|
||||
break;
|
||||
case DATABITS_7:
|
||||
configDataBits |= 0x0700;
|
||||
break;
|
||||
case DATABITS_8:
|
||||
configDataBits |= 0x0800;
|
||||
break;
|
||||
default:
|
||||
configDataBits |= 0x0800;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);
|
||||
|
||||
int configParityBits = 0; // PARITY_NONE
|
||||
switch (parity) {
|
||||
case PARITY_ODD:
|
||||
configParityBits |= 0x0010;
|
||||
break;
|
||||
case PARITY_EVEN:
|
||||
configParityBits |= 0x0020;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configParityBits);
|
||||
|
||||
int configStopBits = 0;
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1:
|
||||
configStopBits |= 0;
|
||||
break;
|
||||
case STOPBITS_2:
|
||||
configStopBits |= 2;
|
||||
break;
|
||||
}
|
||||
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configStopBits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers,
|
||||
boolean purgeWriteBuffers) throws IOException {
|
||||
int value = (purgeReadBuffers ? FLUSH_READ_CODE : 0)
|
||||
| (purgeWriteBuffers ? FLUSH_WRITE_CODE : 0);
|
||||
|
||||
if (value != 0) {
|
||||
setConfigSingle(SILABSER_FLUSH_REQUEST_CODE, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILAB),
|
||||
new int[] {
|
||||
UsbId.SILAB_CP2102
|
||||
});
|
||||
return supportedDevices;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,445 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE:
|
||||
// This code has been modified from the original source. It now uses the FTDI driver provided by
|
||||
// ftdichip.com to communicate with an FTDI device. The previous code did not work with all FTDI
|
||||
// devices.
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbRequest;
|
||||
import android.util.Log;
|
||||
|
||||
import com.ftdi.j2xx.D2xxManager;
|
||||
import com.ftdi.j2xx.FT_Device;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mavlink.qgroundcontrol.QGCActivity;
|
||||
|
||||
/**
|
||||
* A {@link CommonUsbSerialDriver} implementation for a variety of FTDI devices
|
||||
* <p>
|
||||
* This driver is based on
|
||||
* <a href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>, and is
|
||||
* copyright and subject to the following terms:
|
||||
*
|
||||
* <pre>
|
||||
* Copyright (C) 2003 by Intra2net AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License
|
||||
* version 2.1 as published by the Free Software Foundation;
|
||||
*
|
||||
* opensource@intra2net.com
|
||||
* http://www.intra2net.com/en/developer/libftdi
|
||||
* </pre>
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* Some FTDI devices have not been tested; see later listing of supported and
|
||||
* unsupported devices. Devices listed as "supported" support the following
|
||||
* features:
|
||||
* <ul>
|
||||
* <li>Read and write of serial data (see {@link #read(byte[], int)} and
|
||||
* {@link #write(byte[], int)}.
|
||||
* <li>Setting baud rate (see {@link #setBaudRate(int)}).
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* Supported and tested devices:
|
||||
* <ul>
|
||||
* <li>{@value DeviceType#TYPE_R}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* <p>
|
||||
* Unsupported but possibly working devices (please contact the author with
|
||||
* feedback or patches):
|
||||
* <ul>
|
||||
* <li>{@value DeviceType#TYPE_2232C}</li>
|
||||
* <li>{@value DeviceType#TYPE_2232H}</li>
|
||||
* <li>{@value DeviceType#TYPE_4232H}</li>
|
||||
* <li>{@value DeviceType#TYPE_AM}</li>
|
||||
* <li>{@value DeviceType#TYPE_BM}</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
* @see <a href="http://code.google.com/p/usb-serial-for-android/">USB Serial
|
||||
* for Android project page</a>
|
||||
* @see <a href="http://www.ftdichip.com/">FTDI Homepage</a>
|
||||
* @see <a href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>
|
||||
*/
|
||||
public class FtdiSerialDriver extends CommonUsbSerialDriver {
|
||||
|
||||
public static final int USB_TYPE_STANDARD = 0x00 << 5;
|
||||
public static final int USB_TYPE_CLASS = 0x00 << 5;
|
||||
public static final int USB_TYPE_VENDOR = 0x00 << 5;
|
||||
public static final int USB_TYPE_RESERVED = 0x00 << 5;
|
||||
|
||||
public static final int USB_RECIP_DEVICE = 0x00;
|
||||
public static final int USB_RECIP_INTERFACE = 0x01;
|
||||
public static final int USB_RECIP_ENDPOINT = 0x02;
|
||||
public static final int USB_RECIP_OTHER = 0x03;
|
||||
|
||||
public static final int USB_ENDPOINT_IN = 0x80;
|
||||
public static final int USB_ENDPOINT_OUT = 0x00;
|
||||
|
||||
public static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
|
||||
public static final int USB_READ_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
// From ftdi.h
|
||||
/**
|
||||
* Reset the port.
|
||||
*/
|
||||
private static final int SIO_RESET_REQUEST = 0;
|
||||
|
||||
/**
|
||||
* Set the modem control register.
|
||||
*/
|
||||
private static final int SIO_MODEM_CTRL_REQUEST = 1;
|
||||
|
||||
/**
|
||||
* Set flow control register.
|
||||
*/
|
||||
private static final int SIO_SET_FLOW_CTRL_REQUEST = 2;
|
||||
|
||||
/**
|
||||
* Set baud rate.
|
||||
*/
|
||||
private static final int SIO_SET_BAUD_RATE_REQUEST = 3;
|
||||
|
||||
/**
|
||||
* Set the data characteristics of the port.
|
||||
*/
|
||||
private static final int SIO_SET_DATA_REQUEST = 4;
|
||||
|
||||
private static final int SIO_RESET_SIO = 0;
|
||||
private static final int SIO_RESET_PURGE_RX = 1;
|
||||
private static final int SIO_RESET_PURGE_TX = 2;
|
||||
|
||||
public static final int FTDI_DEVICE_OUT_REQTYPE =
|
||||
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT;
|
||||
|
||||
public static final int FTDI_DEVICE_IN_REQTYPE =
|
||||
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN;
|
||||
|
||||
/**
|
||||
* Length of the modem status header, transmitted with every read.
|
||||
*/
|
||||
private static final int MODEM_STATUS_HEADER_LENGTH = 2;
|
||||
|
||||
private final String TAG = FtdiSerialDriver.class.getSimpleName();
|
||||
|
||||
private DeviceType mType;
|
||||
|
||||
/**
|
||||
* FTDI chip types.
|
||||
*/
|
||||
private static enum DeviceType {
|
||||
TYPE_BM, TYPE_AM, TYPE_2232C, TYPE_R, TYPE_2232H, TYPE_4232H;
|
||||
}
|
||||
|
||||
private int mInterface = 0; /* INTERFACE_ANY */
|
||||
|
||||
private int mMaxPacketSize = 64; // TODO(mikey): detect
|
||||
|
||||
/**
|
||||
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
|
||||
* since it gives no indication of number of bytes read. Set this to
|
||||
* {@code true} on platforms where it is fixed.
|
||||
*/
|
||||
private static final boolean ENABLE_ASYNC_READS = false;
|
||||
|
||||
FT_Device m_ftDev;
|
||||
|
||||
/**
|
||||
* Filter FTDI status bytes from buffer
|
||||
* @param src The source buffer (which contains status bytes)
|
||||
* @param dest The destination buffer to write the status bytes into (can be src)
|
||||
* @param totalBytesRead Number of bytes read to src
|
||||
* @param maxPacketSize The USB endpoint max packet size
|
||||
* @return The number of payload bytes
|
||||
*/
|
||||
private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) {
|
||||
final int packetsCount = totalBytesRead / maxPacketSize + 1;
|
||||
for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) {
|
||||
final int count = (packetIdx == (packetsCount - 1))
|
||||
? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH
|
||||
: maxPacketSize - MODEM_STATUS_HEADER_LENGTH;
|
||||
if (count > 0) {
|
||||
System.arraycopy(src,
|
||||
packetIdx * maxPacketSize + MODEM_STATUS_HEADER_LENGTH,
|
||||
dest,
|
||||
packetIdx * (maxPacketSize - MODEM_STATUS_HEADER_LENGTH),
|
||||
count);
|
||||
}
|
||||
}
|
||||
|
||||
return totalBytesRead - (packetsCount * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param usbDevice the {@link UsbDevice} to use
|
||||
* @param usbConnection the {@link UsbDeviceConnection} to use
|
||||
* @throws UsbSerialRuntimeException if the given device is incompatible
|
||||
* with this driver
|
||||
*/
|
||||
public FtdiSerialDriver(UsbDevice usbDevice) {
|
||||
super(usbDevice);
|
||||
mType = null;
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
|
||||
SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != 0) {
|
||||
throw new IOException("Reset failed: result=" + result);
|
||||
}
|
||||
|
||||
// TODO(mikey): autodetect.
|
||||
mType = DeviceType.TYPE_R;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
D2xxManager ftD2xx = null;
|
||||
try {
|
||||
ftD2xx = D2xxManager.getInstance(QGCActivity.m_context);
|
||||
} catch (D2xxManager.D2xxException ex) {
|
||||
QGCActivity.qgcLogDebug("D2xxManager.getInstance threw exception: " + ex.getMessage());
|
||||
}
|
||||
|
||||
if (ftD2xx == null) {
|
||||
String errMsg = "Unable to retrieve D2xxManager instance.";
|
||||
QGCActivity.qgcLogWarning(errMsg);
|
||||
throw new IOException(errMsg);
|
||||
}
|
||||
QGCActivity.qgcLogDebug("Opened D2xxManager");
|
||||
|
||||
int DevCount = ftD2xx.createDeviceInfoList(QGCActivity.m_context);
|
||||
QGCActivity.qgcLogDebug("Found " + DevCount + " ftdi devices.");
|
||||
if (DevCount < 1) {
|
||||
throw new IOException("No FTDI Devices found");
|
||||
}
|
||||
|
||||
m_ftDev = null;
|
||||
try {
|
||||
m_ftDev = ftD2xx.openByIndex(QGCActivity.m_context, 0);
|
||||
} catch (NullPointerException e) {
|
||||
QGCActivity.qgcLogDebug("ftD2xx.openByIndex exception: " + e.getMessage());
|
||||
} finally {
|
||||
if (m_ftDev == null) {
|
||||
throw new IOException("No FTDI Devices found");
|
||||
}
|
||||
}
|
||||
QGCActivity.qgcLogDebug("Opened FTDI device.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (m_ftDev != null) {
|
||||
try {
|
||||
m_ftDev.close();
|
||||
} catch (Exception e) {
|
||||
QGCActivity.qgcLogWarning("close exception: " + e.getMessage());
|
||||
}
|
||||
m_ftDev = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||
int totalBytesRead = 0;
|
||||
int bytesAvailable = m_ftDev.getQueueStatus();
|
||||
|
||||
if (bytesAvailable > 0) {
|
||||
bytesAvailable = Math.min(4096, bytesAvailable);
|
||||
try {
|
||||
totalBytesRead = m_ftDev.read(dest, bytesAvailable, timeoutMillis);
|
||||
} catch (NullPointerException e) {
|
||||
final String errorMsg = "Error reading: " + e.getMessage();
|
||||
QGCActivity.qgcLogWarning(errorMsg);
|
||||
throw new IOException(errorMsg, e);
|
||||
}
|
||||
}
|
||||
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
try {
|
||||
m_ftDev.write(src);
|
||||
return src.length;
|
||||
} catch (Exception e) {
|
||||
QGCActivity.qgcLogWarning("Error writing: " + e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int setBaudRate(int baudRate) throws IOException {
|
||||
try {
|
||||
m_ftDev.setBaudRate(baudRate);
|
||||
return baudRate;
|
||||
} catch (Exception e) {
|
||||
QGCActivity.qgcLogWarning("Error setting baud rate: " + e.getMessage());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
|
||||
setBaudRate(baudRate);
|
||||
|
||||
switch (dataBits) {
|
||||
case 7:
|
||||
dataBits = D2xxManager.FT_DATA_BITS_7;
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
dataBits = D2xxManager.FT_DATA_BITS_8;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (stopBits) {
|
||||
default:
|
||||
case 0:
|
||||
stopBits = D2xxManager.FT_STOP_BITS_1;
|
||||
break;
|
||||
case 1:
|
||||
stopBits = D2xxManager.FT_STOP_BITS_2;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (parity) {
|
||||
default:
|
||||
case 0:
|
||||
parity = D2xxManager.FT_PARITY_NONE;
|
||||
break;
|
||||
case 1:
|
||||
parity = D2xxManager.FT_PARITY_ODD;
|
||||
break;
|
||||
case 2:
|
||||
parity = D2xxManager.FT_PARITY_EVEN;
|
||||
break;
|
||||
case 3:
|
||||
parity = D2xxManager.FT_PARITY_MARK;
|
||||
break;
|
||||
case 4:
|
||||
parity = D2xxManager.FT_PARITY_SPACE;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
m_ftDev.setDataCharacteristics((byte)dataBits, (byte)stopBits, (byte)parity);
|
||||
} catch (Exception e) {
|
||||
QGCActivity.qgcLogWarning("Error setDataCharacteristics: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||
if (purgeReadBuffers) {
|
||||
try {
|
||||
m_ftDev.purge(D2xxManager.FT_PURGE_RX);
|
||||
} catch (Exception e) {
|
||||
String errMsg = "Error purgeHwBuffers(RX): "+ e.getMessage();
|
||||
QGCActivity.qgcLogWarning(errMsg);
|
||||
throw new IOException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
if (purgeWriteBuffers) {
|
||||
try {
|
||||
m_ftDev.purge(D2xxManager.FT_PURGE_TX);
|
||||
} catch (Exception e) {
|
||||
String errMsg = "Error purgeHwBuffers(TX): " + e.getMessage();
|
||||
QGCActivity.qgcLogWarning(errMsg);
|
||||
throw new IOException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI),
|
||||
new int[] {
|
||||
UsbId.FTDI_FT232R,
|
||||
UsbId.FTDI_FT231X,
|
||||
});
|
||||
/*
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PX4),
|
||||
new int[] {
|
||||
UsbId.DEVICE_PX4FMU
|
||||
});
|
||||
*/
|
||||
return supportedDevices;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,523 @@
|
|||
/* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
|
||||
/*
|
||||
* Ported to usb-serial-for-android
|
||||
* by Felix Hädicke <felixhaedicke@web.de>
|
||||
*
|
||||
* Based on the pyprolific driver written
|
||||
* by Emmanuel Blot <emmanuel.blot@free.fr>
|
||||
* See https://github.com/eblot/pyftdi
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbConstants;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbEndpoint;
|
||||
import android.hardware.usb.UsbInterface;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ProlificSerialDriver extends CommonUsbSerialDriver {
|
||||
private static final int USB_READ_TIMEOUT_MILLIS = 1000;
|
||||
private static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
|
||||
|
||||
private static final int USB_RECIP_INTERFACE = 0x01;
|
||||
|
||||
private static final int PROLIFIC_VENDOR_READ_REQUEST = 0x01;
|
||||
private static final int PROLIFIC_VENDOR_WRITE_REQUEST = 0x01;
|
||||
|
||||
private static final int PROLIFIC_VENDOR_OUT_REQTYPE = UsbConstants.USB_DIR_OUT
|
||||
| UsbConstants.USB_TYPE_VENDOR;
|
||||
|
||||
private static final int PROLIFIC_VENDOR_IN_REQTYPE = UsbConstants.USB_DIR_IN
|
||||
| UsbConstants.USB_TYPE_VENDOR;
|
||||
|
||||
private static final int PROLIFIC_CTRL_OUT_REQTYPE = UsbConstants.USB_DIR_OUT
|
||||
| UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
|
||||
|
||||
private static final int WRITE_ENDPOINT = 0x02;
|
||||
private static final int READ_ENDPOINT = 0x83;
|
||||
private static final int INTERRUPT_ENDPOINT = 0x81;
|
||||
|
||||
private static final int FLUSH_RX_REQUEST = 0x08;
|
||||
private static final int FLUSH_TX_REQUEST = 0x09;
|
||||
|
||||
private static final int SET_LINE_REQUEST = 0x20;
|
||||
private static final int SET_CONTROL_REQUEST = 0x22;
|
||||
|
||||
private static final int CONTROL_DTR = 0x01;
|
||||
private static final int CONTROL_RTS = 0x02;
|
||||
|
||||
private static final int STATUS_FLAG_CD = 0x01;
|
||||
private static final int STATUS_FLAG_DSR = 0x02;
|
||||
private static final int STATUS_FLAG_RI = 0x08;
|
||||
private static final int STATUS_FLAG_CTS = 0x80;
|
||||
|
||||
private static final int STATUS_BUFFER_SIZE = 10;
|
||||
private static final int STATUS_BYTE_IDX = 8;
|
||||
|
||||
private static final int DEVICE_TYPE_HX = 0;
|
||||
private static final int DEVICE_TYPE_0 = 1;
|
||||
private static final int DEVICE_TYPE_1 = 2;
|
||||
|
||||
private int mDeviceType = DEVICE_TYPE_HX;
|
||||
|
||||
private UsbEndpoint mReadEndpoint;
|
||||
private UsbEndpoint mWriteEndpoint;
|
||||
private UsbEndpoint mInterruptEndpoint;
|
||||
|
||||
private int mControlLinesValue = 0;
|
||||
|
||||
private int mBaudRate = -1, mDataBits = -1, mStopBits = -1, mParity = -1;
|
||||
|
||||
private int mStatus = 0;
|
||||
private volatile Thread mReadStatusThread = null;
|
||||
private final Object mReadStatusThreadLock = new Object();
|
||||
boolean mStopReadStatusThread = false;
|
||||
private IOException mReadStatusException = null;
|
||||
|
||||
private final String TAG = ProlificSerialDriver.class.getSimpleName();
|
||||
|
||||
private final byte[] inControlTransfer(int requestType, int request,
|
||||
int value, int index, int length) throws IOException {
|
||||
byte[] buffer = new byte[length];
|
||||
int result = mConnection.controlTransfer(requestType, request, value,
|
||||
index, buffer, length, USB_READ_TIMEOUT_MILLIS);
|
||||
if (result != length) {
|
||||
throw new IOException(
|
||||
String.format("ControlTransfer with value 0x%x failed: %d",
|
||||
value, result));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private final void outControlTransfer(int requestType, int request,
|
||||
int value, int index, byte[] data) throws IOException {
|
||||
int length = (data == null) ? 0 : data.length;
|
||||
int result = mConnection.controlTransfer(requestType, request, value,
|
||||
index, data, length, USB_WRITE_TIMEOUT_MILLIS);
|
||||
if (result != length) {
|
||||
throw new IOException(
|
||||
String.format("ControlTransfer with value 0x%x failed: %d",
|
||||
value, result));
|
||||
}
|
||||
}
|
||||
|
||||
private final byte[] vendorIn(int value, int index, int length)
|
||||
throws IOException {
|
||||
return inControlTransfer(PROLIFIC_VENDOR_IN_REQTYPE,
|
||||
PROLIFIC_VENDOR_READ_REQUEST, value, index, length);
|
||||
}
|
||||
|
||||
private final void vendorOut(int value, int index, byte[] data)
|
||||
throws IOException {
|
||||
outControlTransfer(PROLIFIC_VENDOR_OUT_REQTYPE,
|
||||
PROLIFIC_VENDOR_WRITE_REQUEST, value, index, data);
|
||||
}
|
||||
|
||||
private final void ctrlOut(int request, int value, int index, byte[] data)
|
||||
throws IOException {
|
||||
outControlTransfer(PROLIFIC_CTRL_OUT_REQTYPE, request, value, index,
|
||||
data);
|
||||
}
|
||||
|
||||
private void doBlackMagic() throws IOException {
|
||||
vendorIn(0x8484, 0, 1);
|
||||
vendorOut(0x0404, 0, null);
|
||||
vendorIn(0x8484, 0, 1);
|
||||
vendorIn(0x8383, 0, 1);
|
||||
vendorIn(0x8484, 0, 1);
|
||||
vendorOut(0x0404, 1, null);
|
||||
vendorIn(0x8484, 0, 1);
|
||||
vendorIn(0x8383, 0, 1);
|
||||
vendorOut(0, 1, null);
|
||||
vendorOut(1, 0, null);
|
||||
vendorOut(2, (mDeviceType == DEVICE_TYPE_HX) ? 0x44 : 0x24, null);
|
||||
}
|
||||
|
||||
private void resetDevice() throws IOException {
|
||||
purgeHwBuffers(true, true);
|
||||
}
|
||||
|
||||
private void setControlLines(int newControlLinesValue) throws IOException {
|
||||
ctrlOut(SET_CONTROL_REQUEST, newControlLinesValue, 0, null);
|
||||
mControlLinesValue = newControlLinesValue;
|
||||
}
|
||||
|
||||
private final void readStatusThreadFunction() {
|
||||
try {
|
||||
while (!mStopReadStatusThread) {
|
||||
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
|
||||
int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint,
|
||||
buffer,
|
||||
STATUS_BUFFER_SIZE,
|
||||
500);
|
||||
if (readBytesCount > 0) {
|
||||
if (readBytesCount == STATUS_BUFFER_SIZE) {
|
||||
mStatus = buffer[STATUS_BYTE_IDX] & 0xff;
|
||||
} else {
|
||||
throw new IOException(
|
||||
String.format("Invalid CTS / DSR / CD / RI status buffer received, expected %d bytes, but received %d",
|
||||
STATUS_BUFFER_SIZE,
|
||||
readBytesCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
mReadStatusException = e;
|
||||
}
|
||||
}
|
||||
|
||||
private final int getStatus() throws IOException {
|
||||
if ((mReadStatusThread == null) && (mReadStatusException == null)) {
|
||||
synchronized (mReadStatusThreadLock) {
|
||||
if (mReadStatusThread == null) {
|
||||
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
|
||||
int readBytes = mConnection.bulkTransfer(mInterruptEndpoint,
|
||||
buffer,
|
||||
STATUS_BUFFER_SIZE,
|
||||
100);
|
||||
if (readBytes != STATUS_BUFFER_SIZE) {
|
||||
Log.w(TAG, "Could not read initial CTS / DSR / CD / RI status");
|
||||
} else {
|
||||
mStatus = buffer[STATUS_BYTE_IDX] & 0xff;
|
||||
}
|
||||
|
||||
mReadStatusThread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
readStatusThreadFunction();
|
||||
}
|
||||
});
|
||||
mReadStatusThread.setDaemon(true);
|
||||
mReadStatusThread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* throw and clear an exception which occurred in the status read thread */
|
||||
IOException readStatusException = mReadStatusException;
|
||||
if (mReadStatusException != null) {
|
||||
mReadStatusException = null;
|
||||
throw readStatusException;
|
||||
}
|
||||
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
private final boolean testStatusFlag(int flag) throws IOException {
|
||||
return ((getStatus() & flag) == flag);
|
||||
}
|
||||
|
||||
public ProlificSerialDriver(UsbDevice device) {
|
||||
super(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() throws IOException {
|
||||
UsbInterface usbInterface = mDevice.getInterface(0);
|
||||
|
||||
if (!mConnection.claimInterface(usbInterface, true)) {
|
||||
throw new IOException("Error claiming Prolific interface 0");
|
||||
}
|
||||
|
||||
boolean openSuccessful = false;
|
||||
try {
|
||||
for (int i = 0; i < usbInterface.getEndpointCount(); ++i) {
|
||||
UsbEndpoint currentEndpoint = usbInterface.getEndpoint(i);
|
||||
|
||||
switch (currentEndpoint.getAddress()) {
|
||||
case READ_ENDPOINT:
|
||||
mReadEndpoint = currentEndpoint;
|
||||
break;
|
||||
|
||||
case WRITE_ENDPOINT:
|
||||
mWriteEndpoint = currentEndpoint;
|
||||
break;
|
||||
|
||||
case INTERRUPT_ENDPOINT:
|
||||
mInterruptEndpoint = currentEndpoint;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDevice.getDeviceClass() == 0x02) {
|
||||
mDeviceType = DEVICE_TYPE_0;
|
||||
} else {
|
||||
try {
|
||||
Method getRawDescriptorsMethod
|
||||
= mConnection.getClass().getMethod("getRawDescriptors");
|
||||
byte[] rawDescriptors
|
||||
= (byte[]) getRawDescriptorsMethod.invoke(mConnection);
|
||||
byte maxPacketSize0 = rawDescriptors[7];
|
||||
if (maxPacketSize0 == 64) {
|
||||
mDeviceType = DEVICE_TYPE_HX;
|
||||
} else if ((mDevice.getDeviceClass() == 0x00)
|
||||
|| (mDevice.getDeviceClass() == 0xff)) {
|
||||
mDeviceType = DEVICE_TYPE_1;
|
||||
} else {
|
||||
Log.w(TAG, "Could not detect PL2303 subtype, "
|
||||
+ "Assuming that it is a HX device");
|
||||
mDeviceType = DEVICE_TYPE_HX;
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.w(TAG, "Method UsbDeviceConnection.getRawDescriptors, "
|
||||
+ "required for PL2303 subtype detection, not "
|
||||
+ "available! Assuming that it is a HX device");
|
||||
mDeviceType = DEVICE_TYPE_HX;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "An unexpected exception occurred while trying "
|
||||
+ "to detect PL2303 subtype", e);
|
||||
}
|
||||
}
|
||||
|
||||
setControlLines(mControlLinesValue);
|
||||
resetDevice();
|
||||
|
||||
doBlackMagic();
|
||||
openSuccessful = true;
|
||||
} finally {
|
||||
if (!openSuccessful) {
|
||||
try {
|
||||
mConnection.releaseInterface(usbInterface);
|
||||
} catch (Exception ingored) {
|
||||
// Do not cover possible exceptions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
try {
|
||||
mStopReadStatusThread = true;
|
||||
synchronized (mReadStatusThreadLock) {
|
||||
if (mReadStatusThread != null) {
|
||||
try {
|
||||
mReadStatusThread.join();
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "An error occurred while waiting for status read thread", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetDevice();
|
||||
} finally {
|
||||
mConnection.releaseInterface(mDevice.getInterface(0));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] dest, int timeoutMillis) throws IOException {
|
||||
synchronized (mReadBufferLock) {
|
||||
int readAmt = Math.min(dest.length, mReadBuffer.length);
|
||||
int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
|
||||
readAmt, timeoutMillis);
|
||||
if (numBytesRead < 0) {
|
||||
return 0;
|
||||
}
|
||||
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
|
||||
return numBytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] src, int timeoutMillis) throws IOException {
|
||||
int offset = 0;
|
||||
|
||||
while (offset < src.length) {
|
||||
final int writeLength;
|
||||
final int amtWritten;
|
||||
|
||||
synchronized (mWriteBufferLock) {
|
||||
final byte[] writeBuffer;
|
||||
|
||||
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
|
||||
if (offset == 0) {
|
||||
writeBuffer = src;
|
||||
} else {
|
||||
// bulkTransfer does not support offsets, make a copy.
|
||||
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
|
||||
writeBuffer = mWriteBuffer;
|
||||
}
|
||||
|
||||
amtWritten = mConnection.bulkTransfer(mWriteEndpoint,
|
||||
writeBuffer, writeLength, timeoutMillis);
|
||||
}
|
||||
|
||||
if (amtWritten <= 0) {
|
||||
throw new IOException("Error writing " + writeLength
|
||||
+ " bytes at offset " + offset + " length="
|
||||
+ src.length);
|
||||
}
|
||||
|
||||
offset += amtWritten;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameters(int baudRate, int dataBits, int stopBits,
|
||||
int parity) throws IOException {
|
||||
if ((mBaudRate == baudRate) && (mDataBits == dataBits)
|
||||
&& (mStopBits == stopBits) && (mParity == parity)) {
|
||||
// Make sure no action is performed if there is nothing to change
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] lineRequestData = new byte[7];
|
||||
|
||||
lineRequestData[0] = (byte) (baudRate & 0xff);
|
||||
lineRequestData[1] = (byte) ((baudRate >> 8) & 0xff);
|
||||
lineRequestData[2] = (byte) ((baudRate >> 16) & 0xff);
|
||||
lineRequestData[3] = (byte) ((baudRate >> 24) & 0xff);
|
||||
|
||||
switch (stopBits) {
|
||||
case STOPBITS_1:
|
||||
lineRequestData[4] = 0;
|
||||
break;
|
||||
|
||||
case STOPBITS_1_5:
|
||||
lineRequestData[4] = 1;
|
||||
break;
|
||||
|
||||
case STOPBITS_2:
|
||||
lineRequestData[4] = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
|
||||
}
|
||||
|
||||
switch (parity) {
|
||||
case PARITY_NONE:
|
||||
lineRequestData[5] = 0;
|
||||
break;
|
||||
|
||||
case PARITY_ODD:
|
||||
lineRequestData[5] = 1;
|
||||
break;
|
||||
|
||||
case PARITY_EVEN:
|
||||
lineRequestData[5] = 2;
|
||||
break;
|
||||
|
||||
case PARITY_MARK:
|
||||
lineRequestData[5] = 3;
|
||||
break;
|
||||
|
||||
case PARITY_SPACE:
|
||||
lineRequestData[5] = 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown parity value: " + parity);
|
||||
}
|
||||
|
||||
lineRequestData[6] = (byte) dataBits;
|
||||
|
||||
ctrlOut(SET_LINE_REQUEST, 0, 0, lineRequestData);
|
||||
|
||||
resetDevice();
|
||||
|
||||
mBaudRate = baudRate;
|
||||
mDataBits = dataBits;
|
||||
mStopBits = stopBits;
|
||||
mParity = parity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCD() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_CD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCTS() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_CTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDSR() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_DSR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getDTR() throws IOException {
|
||||
return ((mControlLinesValue & CONTROL_DTR) == CONTROL_DTR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDTR(boolean value) throws IOException {
|
||||
int newControlLinesValue;
|
||||
if (value) {
|
||||
newControlLinesValue = mControlLinesValue | CONTROL_DTR;
|
||||
} else {
|
||||
newControlLinesValue = mControlLinesValue & ~CONTROL_DTR;
|
||||
}
|
||||
setControlLines(newControlLinesValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRI() throws IOException {
|
||||
return testStatusFlag(STATUS_FLAG_RI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRTS() throws IOException {
|
||||
return ((mControlLinesValue & CONTROL_RTS) == CONTROL_RTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRTS(boolean value) throws IOException {
|
||||
int newControlLinesValue;
|
||||
if (value) {
|
||||
newControlLinesValue = mControlLinesValue | CONTROL_RTS;
|
||||
} else {
|
||||
newControlLinesValue = mControlLinesValue & ~CONTROL_RTS;
|
||||
}
|
||||
setControlLines(newControlLinesValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
|
||||
if (purgeReadBuffers) {
|
||||
vendorOut(FLUSH_RX_REQUEST, 0, null);
|
||||
}
|
||||
|
||||
if (purgeWriteBuffers) {
|
||||
vendorOut(FLUSH_TX_REQUEST, 0, null);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Map<Integer, int[]> getSupportedDevices() {
|
||||
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
|
||||
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PROLIFIC),
|
||||
new int[] { UsbId.PROLIFIC_PL2303, });
|
||||
return supportedDevices;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
/* Copyright 2012 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
/**
|
||||
* Registry of USB vendor/product ID constants.
|
||||
*
|
||||
* Culled from various sources; see
|
||||
* <a href="http://www.linux-usb.org/usb.ids">usb.ids</a> for one listing.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public final class UsbId {
|
||||
|
||||
public static final int VENDOR_FTDI = 0x0403;
|
||||
public static final int FTDI_FT232R = 0x6001;
|
||||
public static final int FTDI_FT231X = 0x6015;
|
||||
|
||||
public static final int VENDOR_PX4 = 0x26AC;
|
||||
public static final int DEVICE_PX4FMU = 0x11;
|
||||
|
||||
public static final int VENDOR_ATMEL = 0x03EB;
|
||||
public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044;
|
||||
|
||||
public static final int VENDOR_ARDUINO = 0x2341;
|
||||
public static final int ARDUINO_UNO = 0x0001;
|
||||
public static final int ARDUINO_MEGA_2560 = 0x0010;
|
||||
public static final int ARDUINO_SERIAL_ADAPTER = 0x003b;
|
||||
public static final int ARDUINO_MEGA_ADK = 0x003f;
|
||||
public static final int ARDUINO_MEGA_2560_R3 = 0x0042;
|
||||
public static final int ARDUINO_UNO_R3 = 0x0043;
|
||||
public static final int ARDUINO_MEGA_ADK_R3 = 0x0044;
|
||||
public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044;
|
||||
public static final int ARDUINO_LEONARDO = 0x8036;
|
||||
|
||||
public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0;
|
||||
public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483;
|
||||
|
||||
public static final int VENDOR_LEAFLABS = 0x1eaf;
|
||||
public static final int LEAFLABS_MAPLE = 0x0004;
|
||||
|
||||
public static final int VENDOR_SILAB = 0x10c4;
|
||||
public static final int SILAB_CP2102 = 0xea60;
|
||||
|
||||
public static final int VENDOR_PROLIFIC = 0x067b;
|
||||
public static final int PROLIFIC_PL2303 = 0x2303;
|
||||
|
||||
public static final int VENDOR_UBLOX = 0x1546;
|
||||
public static final int DEVICE_UBLOX_5 = 0x01a5;
|
||||
public static final int DEVICE_UBLOX_6 = 0x01a6;
|
||||
public static final int DEVICE_UBLOX_7 = 0x01a7;
|
||||
public static final int DEVICE_UBLOX_8 = 0x01a8;
|
||||
|
||||
public static final int VENDOR_OPENPILOT = 0x20A0;
|
||||
public static final int DEVICE_REVOLUTION = 0x415E;
|
||||
public static final int DEVICE_OPLINK = 0x415C;
|
||||
public static final int DEVICE_SPARKY2 = 0x41D0;
|
||||
public static final int DEVICE_CC3D = 0x415D;
|
||||
|
||||
public static final int VENDOR_ARDUPILOT_CHIBIOS1 = 0x0483;
|
||||
public static final int VENDOR_ARDUPILOT_CHIBIOS2 = 0x1209;
|
||||
public static final int DEVICE_ARDUPILOT_CHIBIOS = 0x5740;
|
||||
|
||||
public static final int VENDOR_DRAGONLINK = 0x1FC9;
|
||||
public static final int DEVICE_DRAGONLINK = 0x0083;
|
||||
|
||||
private UsbId() {
|
||||
throw new IllegalAccessError("Non-instantiable class.");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
|
||||
/**
|
||||
* Driver interface for a USB serial device.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public interface UsbSerialDriver {
|
||||
|
||||
/** 5 data bits. */
|
||||
public static final int DATABITS_5 = 5;
|
||||
|
||||
/** 6 data bits. */
|
||||
public static final int DATABITS_6 = 6;
|
||||
|
||||
/** 7 data bits. */
|
||||
public static final int DATABITS_7 = 7;
|
||||
|
||||
/** 8 data bits. */
|
||||
public static final int DATABITS_8 = 8;
|
||||
|
||||
/** No flow control. */
|
||||
public static final int FLOWCONTROL_NONE = 0;
|
||||
|
||||
/** RTS/CTS input flow control. */
|
||||
public static final int FLOWCONTROL_RTSCTS_IN = 1;
|
||||
|
||||
/** RTS/CTS output flow control. */
|
||||
public static final int FLOWCONTROL_RTSCTS_OUT = 2;
|
||||
|
||||
/** XON/XOFF input flow control. */
|
||||
public static final int FLOWCONTROL_XONXOFF_IN = 4;
|
||||
|
||||
/** XON/XOFF output flow control. */
|
||||
public static final int FLOWCONTROL_XONXOFF_OUT = 8;
|
||||
|
||||
/** No parity. */
|
||||
public static final int PARITY_NONE = 0;
|
||||
|
||||
/** Odd parity. */
|
||||
public static final int PARITY_ODD = 1;
|
||||
|
||||
/** Even parity. */
|
||||
public static final int PARITY_EVEN = 2;
|
||||
|
||||
/** Mark parity. */
|
||||
public static final int PARITY_MARK = 3;
|
||||
|
||||
/** Space parity. */
|
||||
public static final int PARITY_SPACE = 4;
|
||||
|
||||
/** 1 stop bit. */
|
||||
public static final int STOPBITS_1 = 1;
|
||||
|
||||
/** 1.5 stop bits. */
|
||||
public static final int STOPBITS_1_5 = 3;
|
||||
|
||||
/** 2 stop bits. */
|
||||
public static final int STOPBITS_2 = 2;
|
||||
|
||||
public static final int permissionStatusSuccess = 0;
|
||||
public static final int permissionStatusDenied = 1;
|
||||
public static final int permissionStatusRequested = 2;
|
||||
public static final int permissionStatusRequestRequired = 3;
|
||||
public static final int permissionStatusOpen = 4;
|
||||
public int permissionStatus();
|
||||
public void setPermissionStatus(int permissionStatus);
|
||||
|
||||
public void setConnection(UsbDeviceConnection connection);
|
||||
|
||||
/**
|
||||
* Returns the currently-bound USB device.
|
||||
*
|
||||
* @return the device
|
||||
*/
|
||||
public UsbDevice getDevice();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the currently-bound USB device.
|
||||
*
|
||||
* @return the device
|
||||
*/
|
||||
public UsbDeviceConnection getDeviceConnection();
|
||||
|
||||
/**
|
||||
* Opens and initializes the device as a USB serial device. Upon success,
|
||||
* caller must ensure that {@link #close()} is eventually called.
|
||||
*
|
||||
* @throws IOException on error opening or initializing the device.
|
||||
*/
|
||||
public void open() throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the serial device.
|
||||
*
|
||||
* @throws IOException on error closing the device.
|
||||
*/
|
||||
public void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Reads as many bytes as possible into the destination buffer.
|
||||
*
|
||||
* @param dest the destination byte buffer
|
||||
* @param timeoutMillis the timeout for reading
|
||||
* @return the actual number of bytes read
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public int read(final byte[] dest, final int timeoutMillis) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes as many bytes as possible from the source buffer.
|
||||
*
|
||||
* @param src the source byte buffer
|
||||
* @param timeoutMillis the timeout for writing
|
||||
* @return the actual number of bytes written
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public int write(final byte[] src, final int timeoutMillis) throws IOException;
|
||||
|
||||
/**
|
||||
* Sets various serial port parameters.
|
||||
*
|
||||
* @param baudRate baud rate as an integer, for example {@code 115200}.
|
||||
* @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6},
|
||||
* {@link #DATABITS_7}, or {@link #DATABITS_8}.
|
||||
* @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or
|
||||
* {@link #STOPBITS_2}.
|
||||
* @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD},
|
||||
* {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or
|
||||
* {@link #PARITY_SPACE}.
|
||||
* @throws IOException on error setting the port parameters
|
||||
*/
|
||||
public void setParameters(
|
||||
int baudRate, int dataBits, int stopBits, int parity) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the CD (Carrier Detect) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getCD() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the CTS (Clear To Send) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getCTS() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the DSR (Data Set Ready) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getDSR() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the DTR (Data Terminal Ready) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getDTR() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the DTR (Data Terminal Ready) bit on the underlying UART, if
|
||||
* supported.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public void setDTR(boolean value) throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the RI (Ring Indicator) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getRI() throws IOException;
|
||||
|
||||
/**
|
||||
* Gets the RTS (Request To Send) bit from the underlying UART.
|
||||
*
|
||||
* @return the current state, or {@code false} if not supported.
|
||||
* @throws IOException if an error occurred during reading
|
||||
*/
|
||||
public boolean getRTS() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the RTS (Request To Send) bit on the underlying UART, if
|
||||
* supported.
|
||||
*
|
||||
* @param value the value to set
|
||||
* @throws IOException if an error occurred during writing
|
||||
*/
|
||||
public void setRTS(boolean value) throws IOException;
|
||||
|
||||
/**
|
||||
* Flush non-transmitted output data and / or non-read input data
|
||||
* @param flushRX {@code true} to flush non-transmitted output data
|
||||
* @param flushTX {@code true} to flush non-read input data
|
||||
* @return {@code true} if the operation was successful, or
|
||||
* {@code false} if the operation is not supported by the driver or device
|
||||
* @throws IOException if an error occurred during flush
|
||||
*/
|
||||
public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
/* Copyright 2011 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE:
|
||||
// This source has been modified from the original such that testIfSupported only tests for a vendor id
|
||||
// match. If that matches it allows all product ids through. This provides for better match on unknown boards.
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper class which finds compatible {@link UsbDevice}s and creates
|
||||
* {@link UsbSerialDriver} instances.
|
||||
*
|
||||
* <p/>
|
||||
* You don't need a Prober to use the rest of the library: it is perfectly
|
||||
* acceptable to instantiate driver instances manually. The Prober simply
|
||||
* provides convenience functions.
|
||||
*
|
||||
* <p/>
|
||||
* For most drivers, the corresponding {@link #probe(UsbManager, UsbDevice)}
|
||||
* method will either return an empty list (device unknown / unsupported) or a
|
||||
* singleton list. However, multi-port drivers may return multiple instances.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
public enum UsbSerialProber {
|
||||
|
||||
// TODO(mikey): Too much boilerplate.
|
||||
|
||||
/**
|
||||
* Prober for {@link FtdiSerialDriver}.
|
||||
*
|
||||
* @see FtdiSerialDriver
|
||||
*/
|
||||
FTDI_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, FtdiSerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new FtdiSerialDriver(usbDevice);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
},
|
||||
|
||||
CDC_ACM_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(UsbManager manager, UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, CdcAcmSerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new CdcAcmSerialDriver(usbDevice);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
},
|
||||
|
||||
SILAB_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, Cp2102SerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
},
|
||||
|
||||
PROLIFIC_SERIAL {
|
||||
@Override
|
||||
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
|
||||
if (!testIfSupported(usbDevice, ProlificSerialDriver.getSupportedDevices())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final UsbSerialDriver driver = new ProlificSerialDriver(usbDevice);
|
||||
return Collections.singletonList(driver);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the supplied {@link UsbDevice} for compatibility with this enum
|
||||
* member, returning one or more driver instances if compatible.
|
||||
*
|
||||
* @param manager the {@link UsbManager} to use
|
||||
* @param usbDevice the raw {@link UsbDevice} to use
|
||||
* @return zero or more {@link UsbSerialDriver}, depending on compatibility
|
||||
* (never {@code null}).
|
||||
*/
|
||||
protected abstract List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice);
|
||||
|
||||
/**
|
||||
* Creates and returns a new {@link UsbSerialDriver} instance for the first
|
||||
* compatible {@link UsbDevice} found on the bus. If none are found,
|
||||
* returns {@code null}.
|
||||
*
|
||||
* <p/>
|
||||
* The order of devices is undefined, therefore if there are multiple
|
||||
* devices on the bus, the chosen device may not be predictable (clients
|
||||
* should use {@link #findAllDevices(UsbManager)} instead).
|
||||
*
|
||||
* @param usbManager the {@link UsbManager} to use.
|
||||
* @return the first available {@link UsbSerialDriver}, or {@code null} if
|
||||
* none are available.
|
||||
*/
|
||||
public static UsbSerialDriver findFirstDevice(final UsbManager usbManager) {
|
||||
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
|
||||
for (final UsbSerialProber prober : values()) {
|
||||
final List<UsbSerialDriver> probedDevices = prober.probe(usbManager, usbDevice);
|
||||
if (!probedDevices.isEmpty()) {
|
||||
return probedDevices.get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link UsbSerialDriver} instance for all compatible
|
||||
* {@link UsbDevice}s found on the bus. If no compatible devices are found,
|
||||
* the list will be empty.
|
||||
*
|
||||
* @param usbManager
|
||||
* @return
|
||||
*/
|
||||
public static List<UsbSerialDriver> findAllDevices(final UsbManager usbManager) {
|
||||
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
|
||||
//Log.i("QGC_UsbSerialProber", "Looking for USB devices");
|
||||
// For each UsbDevice, call probe() for each prober.
|
||||
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
|
||||
//Log.i("QGC_UsbSerialProber", "Probing device: " + usbDevice.getDeviceName() + " mid: " + usbDevice.getVendorId() + " pid: " + usbDevice.getDeviceId());
|
||||
result.addAll(probeSingleDevice(usbManager, usbDevice));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special method for testing a specific device for driver support,
|
||||
* returning any compatible driver(s).
|
||||
*
|
||||
* <p/>
|
||||
* Clients should ordinarily use {@link #findAllDevices(UsbManager)}, which
|
||||
* operates against the entire bus of devices. This method is useful when
|
||||
* testing against only a single target is desired.
|
||||
*
|
||||
* @param usbManager the {@link UsbManager} to use.
|
||||
* @param usbDevice the device to test against.
|
||||
* @return a list containing zero or more {@link UsbSerialDriver} instances.
|
||||
*/
|
||||
public static List<UsbSerialDriver> probeSingleDevice(final UsbManager usbManager,
|
||||
UsbDevice usbDevice)
|
||||
{
|
||||
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
|
||||
for (final UsbSerialProber prober : values()) {
|
||||
final List<UsbSerialDriver> probedDevices = prober.probe(usbManager, usbDevice);
|
||||
result.addAll(probedDevices);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated; Use {@link #findFirstDevice(UsbManager)}.
|
||||
*
|
||||
* @param usbManager
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public static UsbSerialDriver acquire(final UsbManager usbManager) {
|
||||
return findFirstDevice(usbManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated; use {@link #probeSingleDevice(UsbManager, UsbDevice)}.
|
||||
*
|
||||
* @param usbManager
|
||||
* @param usbDevice
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
public static UsbSerialDriver acquire(final UsbManager usbManager, final UsbDevice usbDevice) {
|
||||
final List<UsbSerialDriver> probedDevices = probeSingleDevice(usbManager, usbDevice);
|
||||
if (!probedDevices.isEmpty()) {
|
||||
return probedDevices.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the given device is found in the driver's
|
||||
* vendor/product map.
|
||||
*
|
||||
* @param usbDevice the device to test
|
||||
* @param supportedDevices map of vendor IDs to product ID(s)
|
||||
* @return {@code true} if supported
|
||||
*/
|
||||
private static boolean testIfSupported(final UsbDevice usbDevice, final Map<Integer, int[]> supportedDevices) {
|
||||
return supportedDevices.containsKey(usbDevice.getVendorId());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*/
|
||||
|
||||
package com.hoho.android.usbserial.driver;
|
||||
|
||||
/**
|
||||
* Generic unchecked exception for the usbserial package.
|
||||
*
|
||||
* @author mike wakerly (opensource@hoho.com)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class UsbSerialRuntimeException extends RuntimeException {
|
||||
|
||||
public UsbSerialRuntimeException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UsbSerialRuntimeException(String detailMessage, Throwable throwable) {
|
||||
super(detailMessage, throwable);
|
||||
}
|
||||
|
||||
public UsbSerialRuntimeException(String detailMessage) {
|
||||
super(detailMessage);
|
||||
}
|
||||
|
||||
public UsbSerialRuntimeException(Throwable throwable) {
|
||||
super(throwable);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2012, Collabora Ltd.
|
||||
* Author: Youness Alaoui
|
||||
*
|
||||
* Copyright (C) 2015, Collabora Ltd.
|
||||
* Author: Justin Kim <justin.kim@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
package org.freedesktop.gstreamer.androidmedia;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
public class GstAhcCallback implements Camera.PreviewCallback,
|
||||
Camera.ErrorCallback,
|
||||
Camera.AutoFocusCallback {
|
||||
public long mUserData;
|
||||
public long mCallback;
|
||||
|
||||
public static native void gst_ah_camera_on_preview_frame(byte[] data, Camera camera,
|
||||
long callback, long user_data);
|
||||
public static native void gst_ah_camera_on_error(int error, Camera camera,
|
||||
long callback, long user_data);
|
||||
public static native void gst_ah_camera_on_auto_focus(boolean success, Camera camera,
|
||||
long callback, long user_data);
|
||||
|
||||
public GstAhcCallback(long callback, long user_data) {
|
||||
mCallback = callback;
|
||||
mUserData = user_data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||
gst_ah_camera_on_preview_frame(data, camera, mCallback, mUserData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int error, Camera camera) {
|
||||
gst_ah_camera_on_error(error, camera, mCallback, mUserData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAutoFocus(boolean success, Camera camera) {
|
||||
gst_ah_camera_on_auto_focus(success, camera, mCallback, mUserData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (C) 2016 SurroundIO
|
||||
* Author: Martin Kelly <martin@surround.io>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package org.freedesktop.gstreamer.androidmedia;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
|
||||
public class GstAhsCallback implements SensorEventListener {
|
||||
public long mUserData;
|
||||
public long mSensorCallback;
|
||||
public long mAccuracyCallback;
|
||||
|
||||
public static native void gst_ah_sensor_on_sensor_changed(SensorEvent event,
|
||||
long callback, long user_data);
|
||||
public static native void gst_ah_sensor_on_accuracy_changed(Sensor sensor, int accuracy,
|
||||
long callback, long user_data);
|
||||
|
||||
public GstAhsCallback(long sensor_callback,
|
||||
long accuracy_callback, long user_data) {
|
||||
mSensorCallback = sensor_callback;
|
||||
mAccuracyCallback = accuracy_callback;
|
||||
mUserData = user_data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
gst_ah_sensor_on_sensor_changed(event, mSensorCallback, mUserData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
gst_ah_sensor_on_accuracy_changed(sensor, accuracy,
|
||||
mAccuracyCallback, mUserData);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2015, Collabora Ltd.
|
||||
* Author: Matthieu Bouron <matthieu.bouron@collabora.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation
|
||||
* version 2.1 of the License.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
package org.freedesktop.gstreamer.androidmedia;
|
||||
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
|
||||
|
||||
public class GstAmcOnFrameAvailableListener implements OnFrameAvailableListener
|
||||
{
|
||||
private long context = 0;
|
||||
|
||||
public synchronized void onFrameAvailable (SurfaceTexture surfaceTexture) {
|
||||
native_onFrameAvailable(context, surfaceTexture);
|
||||
}
|
||||
|
||||
public synchronized long getContext () {
|
||||
return context;
|
||||
}
|
||||
|
||||
public synchronized void setContext (long c) {
|
||||
context = c;
|
||||
}
|
||||
|
||||
private native void native_onFrameAvailable (long context, SurfaceTexture surfaceTexture);
|
||||
}
|
|
@ -0,0 +1,798 @@
|
|||
package org.mavlink.qgroundcontrol;
|
||||
|
||||
/* Copyright 2013 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Written by: Mike Goza April 2014
|
||||
//
|
||||
// These routines interface with the Android USB Host devices for serial port communication.
|
||||
// The code uses the usb-serial-for-android software library. The QGCActivity class is the
|
||||
// interface to the C++ routines through jni calls. Do not change the functions without also
|
||||
// changing the corresponding calls in the C++ routines or you will break the interface.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.usb.UsbAccessory;
|
||||
import android.hardware.usb.UsbDevice;
|
||||
import android.hardware.usb.UsbDeviceConnection;
|
||||
import android.hardware.usb.UsbManager;
|
||||
import android.widget.Toast;
|
||||
import android.util.Log;
|
||||
import android.os.PowerManager;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.Bundle;
|
||||
import android.app.PendingIntent;
|
||||
import android.view.WindowManager;
|
||||
import android.os.Bundle;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.StorageVolume;
|
||||
|
||||
import com.hoho.android.usbserial.driver.*;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
import org.qtproject.qt5.android.bindings.QtApplication;
|
||||
|
||||
public class QGCActivity extends QtActivity
|
||||
{
|
||||
public static int BAD_DEVICE_ID = 0;
|
||||
private static QGCActivity _instance = null;
|
||||
private static UsbManager _usbManager = null;
|
||||
private static List<UsbSerialDriver> _drivers;
|
||||
private static HashMap<Integer, UsbIoManager> m_ioManager;
|
||||
private static HashMap<Integer, Long> _userDataHashByDeviceId;
|
||||
private static final String TAG = "QGC_QGCActivity";
|
||||
private static PowerManager.WakeLock _wakeLock;
|
||||
private static final String ACTION_USB_PERMISSION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION";
|
||||
private static PendingIntent _usbPermissionIntent = null;
|
||||
private TaiSync taiSync = null;
|
||||
private Timer probeAccessoriesTimer = null;
|
||||
private static WifiManager.MulticastLock _wifiMulticastLock;
|
||||
|
||||
public static Context m_context;
|
||||
|
||||
private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
private final static UsbIoManager.Listener m_Listener =
|
||||
new UsbIoManager.Listener()
|
||||
{
|
||||
@Override
|
||||
public void onRunError(Exception eA, long userData)
|
||||
{
|
||||
Log.e(TAG, "onRunError Exception");
|
||||
nativeDeviceException(userData, eA.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNewData(final byte[] dataA, long userData)
|
||||
{
|
||||
nativeDeviceNewData(userData, dataA);
|
||||
}
|
||||
};
|
||||
|
||||
private final BroadcastReceiver mOpenAccessoryReceiver =
|
||||
new BroadcastReceiver()
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (ACTION_USB_PERMISSION.equals(action)) {
|
||||
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
|
||||
if (accessory != null && intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||
openAccessory(accessory);
|
||||
}
|
||||
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
|
||||
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
|
||||
if (accessory != null) {
|
||||
closeAccessory(accessory);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static UsbSerialDriver _findDriverByDeviceId(int deviceId) {
|
||||
for (UsbSerialDriver driver: _drivers) {
|
||||
if (driver.getDevice().getDeviceId() == deviceId) {
|
||||
return driver;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static UsbSerialDriver _findDriverByDeviceName(String deviceName) {
|
||||
for (UsbSerialDriver driver: _drivers) {
|
||||
if (driver.getDevice().getDeviceName().equals(deviceName)) {
|
||||
return driver;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private final static BroadcastReceiver _usbReceiver = new BroadcastReceiver() {
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
Log.i(TAG, "BroadcastReceiver USB action " + action);
|
||||
|
||||
if (ACTION_USB_PERMISSION.equals(action)) {
|
||||
synchronized (_instance) {
|
||||
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
if (device != null) {
|
||||
UsbSerialDriver driver = _findDriverByDeviceId(device.getDeviceId());
|
||||
|
||||
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
|
||||
qgcLogDebug("Permission granted to " + device.getDeviceName());
|
||||
driver.setPermissionStatus(UsbSerialDriver.permissionStatusSuccess);
|
||||
} else {
|
||||
qgcLogDebug("Permission denied for " + device.getDeviceName());
|
||||
driver.setPermissionStatus(UsbSerialDriver.permissionStatusDenied);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
|
||||
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
|
||||
if (device != null) {
|
||||
if (_userDataHashByDeviceId.containsKey(device.getDeviceId())) {
|
||||
nativeDeviceHasDisconnected(_userDataHashByDeviceId.get(device.getDeviceId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
nativeUpdateAvailableJoysticks();
|
||||
} catch(Exception e) {
|
||||
Log.e(TAG, "Exception nativeUpdateAvailableJoysticks()");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Native C++ functions which connect back to QSerialPort code
|
||||
private static native void nativeDeviceHasDisconnected(long userData);
|
||||
private static native void nativeDeviceException(long userData, String messageA);
|
||||
private static native void nativeDeviceNewData(long userData, byte[] dataA);
|
||||
private static native void nativeUpdateAvailableJoysticks();
|
||||
|
||||
// Native C++ functions called to log output
|
||||
public static native void qgcLogDebug(String message);
|
||||
public static native void qgcLogWarning(String message);
|
||||
|
||||
public native void nativeInit();
|
||||
|
||||
// QGCActivity singleton
|
||||
public QGCActivity()
|
||||
{
|
||||
_instance = this;
|
||||
_drivers = new ArrayList<UsbSerialDriver>();
|
||||
_userDataHashByDeviceId = new HashMap<Integer, Long>();
|
||||
m_ioManager = new HashMap<Integer, UsbIoManager>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
nativeInit();
|
||||
PowerManager pm = (PowerManager)_instance.getSystemService(Context.POWER_SERVICE);
|
||||
_wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "QGroundControl");
|
||||
if(_wakeLock != null) {
|
||||
_wakeLock.acquire();
|
||||
} else {
|
||||
Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!");
|
||||
}
|
||||
_instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
_usbManager = (UsbManager)_instance.getSystemService(Context.USB_SERVICE);
|
||||
|
||||
// Register for USB Detach and USB Permission intent
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
|
||||
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
|
||||
filter.addAction(ACTION_USB_PERMISSION);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
|
||||
_instance.registerReceiver(_instance._usbReceiver, filter);
|
||||
|
||||
// Create intent for usb permission request
|
||||
_usbPermissionIntent = PendingIntent.getBroadcast(_instance, 0, new Intent(ACTION_USB_PERMISSION), 0);
|
||||
|
||||
// Workaround for QTBUG-73138
|
||||
if (_wifiMulticastLock == null)
|
||||
{
|
||||
WifiManager wifi = (WifiManager) _instance.getSystemService(Context.WIFI_SERVICE);
|
||||
_wifiMulticastLock = wifi.createMulticastLock("QGroundControl");
|
||||
_wifiMulticastLock.setReferenceCounted(true);
|
||||
}
|
||||
|
||||
_wifiMulticastLock.acquire();
|
||||
Log.d(TAG, "Multicast lock: " + _wifiMulticastLock.toString());
|
||||
|
||||
|
||||
try {
|
||||
taiSync = new TaiSync();
|
||||
|
||||
IntentFilter accessoryFilter = new IntentFilter(ACTION_USB_PERMISSION);
|
||||
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
|
||||
registerReceiver(mOpenAccessoryReceiver, accessoryFilter);
|
||||
|
||||
probeAccessoriesTimer = new Timer();
|
||||
probeAccessoriesTimer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
probeAccessories();
|
||||
}
|
||||
}, 0, 3000);
|
||||
} catch(Exception e) {
|
||||
Log.e(TAG, "Exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Plug in of USB ACCESSORY triggers only onResume event.
|
||||
// Then we scan if there is actually anything new
|
||||
probeAccessories();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
if (probeAccessoriesTimer != null) {
|
||||
probeAccessoriesTimer.cancel();
|
||||
}
|
||||
unregisterReceiver(mOpenAccessoryReceiver);
|
||||
try {
|
||||
if (_wifiMulticastLock != null) {
|
||||
_wifiMulticastLock.release();
|
||||
Log.d(TAG, "Multicast lock released.");
|
||||
}
|
||||
if(_wakeLock != null) {
|
||||
_wakeLock.release();
|
||||
}
|
||||
} catch(Exception e) {
|
||||
Log.e(TAG, "Exception onDestroy()");
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public void onInit(int status) {
|
||||
}
|
||||
|
||||
/// Incrementally updates the list of drivers connected to the device
|
||||
private static void updateCurrentDrivers()
|
||||
{
|
||||
List<UsbSerialDriver> currentDrivers = UsbSerialProber.findAllDevices(_usbManager);
|
||||
|
||||
// Remove stale drivers
|
||||
for (int i=_drivers.size()-1; i>=0; i--) {
|
||||
boolean found = false;
|
||||
for (UsbSerialDriver currentDriver: currentDrivers) {
|
||||
if (_drivers.get(i).getDevice().getDeviceId() == currentDriver.getDevice().getDeviceId()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
qgcLogDebug("Remove stale driver " + _drivers.get(i).getDevice().getDeviceName());
|
||||
_drivers.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new drivers
|
||||
for (int i=0; i<currentDrivers.size(); i++) {
|
||||
boolean found = false;
|
||||
for (int j=0; j<_drivers.size(); j++) {
|
||||
if (currentDrivers.get(i).getDevice().getDeviceId() == _drivers.get(j).getDevice().getDeviceId()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
UsbSerialDriver newDriver = currentDrivers.get(i);
|
||||
UsbDevice device = newDriver.getDevice();
|
||||
String deviceName = device.getDeviceName();
|
||||
|
||||
_drivers.add(newDriver);
|
||||
qgcLogDebug("Adding new driver " + deviceName);
|
||||
|
||||
// Request permission if needed
|
||||
if (_usbManager.hasPermission(device)) {
|
||||
qgcLogDebug("Already have permission to use device " + deviceName);
|
||||
newDriver.setPermissionStatus(UsbSerialDriver.permissionStatusSuccess);
|
||||
} else {
|
||||
qgcLogDebug("Requesting permission to use device " + deviceName);
|
||||
newDriver.setPermissionStatus(UsbSerialDriver.permissionStatusRequested);
|
||||
_usbManager.requestPermission(device, _usbPermissionIntent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns array of device info for each unopened device.
|
||||
/// @return Device info format DeviceName:Company:ProductId:VendorId
|
||||
public static String[] availableDevicesInfo()
|
||||
{
|
||||
updateCurrentDrivers();
|
||||
|
||||
if (_drivers.size() <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> deviceInfoList = new ArrayList<String>();
|
||||
|
||||
for (int i=0; i<_drivers.size(); i++) {
|
||||
String deviceInfo;
|
||||
UsbSerialDriver driver = _drivers.get(i);
|
||||
|
||||
if (driver.permissionStatus() != UsbSerialDriver.permissionStatusSuccess) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UsbDevice device = driver.getDevice();
|
||||
|
||||
deviceInfo = device.getDeviceName() + ":";
|
||||
|
||||
if (driver instanceof FtdiSerialDriver) {
|
||||
deviceInfo = deviceInfo + "FTDI:";
|
||||
} else if (driver instanceof CdcAcmSerialDriver) {
|
||||
deviceInfo = deviceInfo + "Cdc Acm:";
|
||||
} else if (driver instanceof Cp2102SerialDriver) {
|
||||
deviceInfo = deviceInfo + "Cp2102:";
|
||||
} else if (driver instanceof ProlificSerialDriver) {
|
||||
deviceInfo = deviceInfo + "Prolific:";
|
||||
} else {
|
||||
deviceInfo = deviceInfo + "Unknown:";
|
||||
}
|
||||
|
||||
deviceInfo = deviceInfo + Integer.toString(device.getProductId()) + ":";
|
||||
deviceInfo = deviceInfo + Integer.toString(device.getVendorId()) + ":";
|
||||
|
||||
deviceInfoList.add(deviceInfo);
|
||||
}
|
||||
|
||||
String[] rgDeviceInfo = new String[deviceInfoList.size()];
|
||||
for (int i=0; i<deviceInfoList.size(); i++) {
|
||||
rgDeviceInfo[i] = deviceInfoList.get(i);
|
||||
}
|
||||
|
||||
return rgDeviceInfo;
|
||||
}
|
||||
|
||||
/// Open the specified device
|
||||
/// @param userData Data to associate with device and pass back through to native calls.
|
||||
/// @return Device id
|
||||
public static int open(Context parentContext, String deviceName, long userData)
|
||||
{
|
||||
int deviceId = BAD_DEVICE_ID;
|
||||
|
||||
m_context = parentContext;
|
||||
|
||||
UsbSerialDriver driver = _findDriverByDeviceName(deviceName);
|
||||
if (driver == null) {
|
||||
qgcLogWarning("Attempt to open unknown device " + deviceName);
|
||||
return BAD_DEVICE_ID;
|
||||
}
|
||||
|
||||
if (driver.permissionStatus() != UsbSerialDriver.permissionStatusSuccess) {
|
||||
qgcLogWarning("Attempt to open device with incorrect permission status " + deviceName + " " + driver.permissionStatus());
|
||||
return BAD_DEVICE_ID;
|
||||
}
|
||||
|
||||
UsbDevice device = driver.getDevice();
|
||||
deviceId = device.getDeviceId();
|
||||
|
||||
try {
|
||||
driver.setConnection(_usbManager.openDevice(device));
|
||||
driver.open();
|
||||
driver.setPermissionStatus(UsbSerialDriver.permissionStatusOpen);
|
||||
|
||||
_userDataHashByDeviceId.put(deviceId, userData);
|
||||
|
||||
UsbIoManager ioManager = new UsbIoManager(driver, m_Listener, userData);
|
||||
m_ioManager.put(deviceId, ioManager);
|
||||
m_Executor.submit(ioManager);
|
||||
|
||||
qgcLogDebug("Port open successful");
|
||||
} catch(IOException exA) {
|
||||
driver.setPermissionStatus(UsbSerialDriver.permissionStatusRequestRequired);
|
||||
_userDataHashByDeviceId.remove(deviceId);
|
||||
|
||||
if(m_ioManager.get(deviceId) != null) {
|
||||
m_ioManager.get(deviceId).stop();
|
||||
m_ioManager.remove(deviceId);
|
||||
}
|
||||
qgcLogWarning("Port open exception: " + exA.getMessage());
|
||||
return BAD_DEVICE_ID;
|
||||
}
|
||||
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public static void startIoManager(int idA)
|
||||
{
|
||||
if (m_ioManager.get(idA) != null)
|
||||
return;
|
||||
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return;
|
||||
|
||||
UsbIoManager managerL = new UsbIoManager(driverL, m_Listener, _userDataHashByDeviceId.get(idA));
|
||||
m_ioManager.put(idA, managerL);
|
||||
m_Executor.submit(managerL);
|
||||
}
|
||||
|
||||
public static void stopIoManager(int idA)
|
||||
{
|
||||
if(m_ioManager.get(idA) == null)
|
||||
return;
|
||||
|
||||
m_ioManager.get(idA).stop();
|
||||
m_ioManager.remove(idA);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Sets the parameters on an open port.
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
// baudRateA - Decimal value of the baud rate. I.E. 9600, 57600, 115200, etc.
|
||||
// dataBitsA - number of data bits. Valid numbers are 5, 6, 7, 8
|
||||
// stopBitsA - number of stop bits. Valid numbers are 1, 2
|
||||
// parityA - No Parity=0, Odd Parity=1, Even Parity=2
|
||||
//
|
||||
// Returns: T/F Success/Failure
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static boolean setParameters(int idA, int baudRateA, int dataBitsA, int stopBitsA, int parityA)
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
driverL.setParameters(baudRateA, dataBitsA, stopBitsA, parityA);
|
||||
return true;
|
||||
}
|
||||
catch(IOException eA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Close the device.
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
//
|
||||
// Returns: T/F Success/Failure
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static boolean close(int idA)
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
stopIoManager(idA);
|
||||
_userDataHashByDeviceId.remove(idA);
|
||||
driverL.setPermissionStatus(UsbSerialDriver.permissionStatusRequestRequired);
|
||||
driverL.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(IOException eA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Write data to the device.
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
// sourceA - byte array of data to write
|
||||
// timeoutMsecA - amount of time in milliseconds to wait for the write to occur
|
||||
//
|
||||
// Returns: number of bytes written
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static int write(int idA, byte[] sourceA, int timeoutMSecA)
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return 0;
|
||||
|
||||
try
|
||||
{
|
||||
return driverL.write(sourceA, timeoutMSecA);
|
||||
}
|
||||
catch(IOException eA)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
UsbIoManager managerL = m_ioManager.get(idA);
|
||||
|
||||
if(managerL != null)
|
||||
{
|
||||
managerL.writeAsync(sourceA);
|
||||
return sourceA.length;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
*/
|
||||
}
|
||||
|
||||
public static boolean isDeviceNameValid(String nameA)
|
||||
{
|
||||
for (UsbSerialDriver driver: _drivers) {
|
||||
if (driver.getDevice().getDeviceName() == nameA)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isDeviceNameOpen(String nameA)
|
||||
{
|
||||
for (UsbSerialDriver driverL: _drivers) {
|
||||
if (nameA.equals(driverL.getDevice().getDeviceName()) && driverL.permissionStatus() == UsbSerialDriver.permissionStatusOpen) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Set the Data Terminal Ready flag on the device
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
// onA - on=T, off=F
|
||||
//
|
||||
// Returns: T/F Success/Failure
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static boolean setDataTerminalReady(int idA, boolean onA)
|
||||
{
|
||||
try
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return false;
|
||||
|
||||
driverL.setDTR(onA);
|
||||
return true;
|
||||
}
|
||||
catch(IOException eA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Set the Request to Send flag
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
// onA - on=T, off=F
|
||||
//
|
||||
// Returns: T/F Success/Failure
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static boolean setRequestToSend(int idA, boolean onA)
|
||||
{
|
||||
try
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return false;
|
||||
|
||||
driverL.setRTS(onA);
|
||||
return true;
|
||||
}
|
||||
catch(IOException eA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Purge the hardware buffers based on the input and output flags
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
// inputA - input buffer purge. purge=T
|
||||
// outputA - output buffer purge. purge=T
|
||||
//
|
||||
// Returns: T/F Success/Failure
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static boolean purgeBuffers(int idA, boolean inputA, boolean outputA)
|
||||
{
|
||||
try
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return false;
|
||||
|
||||
return driverL.purgeHwBuffers(inputA, outputA);
|
||||
}
|
||||
catch(IOException eA)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Get the native device handle (file descriptor)
|
||||
//
|
||||
// Args: idA - ID number from the open command
|
||||
//
|
||||
// Returns: device handle
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
public static int getDeviceHandle(int idA)
|
||||
{
|
||||
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
|
||||
|
||||
if (driverL == null)
|
||||
return -1;
|
||||
|
||||
UsbDeviceConnection connectL = driverL.getDeviceConnection();
|
||||
if (connectL == null)
|
||||
return -1;
|
||||
else
|
||||
return connectL.getFileDescriptor();
|
||||
}
|
||||
|
||||
UsbAccessory openUsbAccessory = null;
|
||||
Object openAccessoryLock = new Object();
|
||||
|
||||
private void openAccessory(UsbAccessory usbAccessory)
|
||||
{
|
||||
Log.i(TAG, "openAccessory: " + usbAccessory.getSerial());
|
||||
try {
|
||||
synchronized(openAccessoryLock) {
|
||||
if ((openUsbAccessory != null && !taiSync.isRunning()) || openUsbAccessory == null) {
|
||||
openUsbAccessory = usbAccessory;
|
||||
taiSync.open(_usbManager.openAccessory(usbAccessory));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "openAccessory exception: " + e);
|
||||
taiSync.close();
|
||||
closeAccessory(openUsbAccessory);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeAccessory(UsbAccessory usbAccessory)
|
||||
{
|
||||
Log.i(TAG, "closeAccessory");
|
||||
|
||||
synchronized(openAccessoryLock) {
|
||||
if (openUsbAccessory != null && usbAccessory == openUsbAccessory && taiSync.isRunning()) {
|
||||
taiSync.close();
|
||||
openUsbAccessory = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object probeAccessoriesLock = new Object();
|
||||
|
||||
private void probeAccessories()
|
||||
{
|
||||
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
synchronized(openAccessoryLock) {
|
||||
// Log.i(TAG, "probeAccessories");
|
||||
UsbAccessory[] accessories = _usbManager.getAccessoryList();
|
||||
if (accessories != null) {
|
||||
for (UsbAccessory usbAccessory : accessories) {
|
||||
if (usbAccessory == null) {
|
||||
continue;
|
||||
}
|
||||
if (_usbManager.hasPermission(usbAccessory)) {
|
||||
openAccessory(usbAccessory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public static String getSDCardPath() {
|
||||
StorageManager storageManager = (StorageManager)_instance.getSystemService(Activity.STORAGE_SERVICE);
|
||||
List<StorageVolume> volumes = storageManager.getStorageVolumes();
|
||||
Method mMethodGetPath;
|
||||
String path = "";
|
||||
for (StorageVolume vol : volumes) {
|
||||
try {
|
||||
mMethodGetPath = vol.getClass().getMethod("getPath");
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
path = (String) mMethodGetPath.invoke(vol);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vol.isRemovable() == true) {
|
||||
Log.i(TAG, "removable sd card mounted " + path);
|
||||
return path;
|
||||
} else {
|
||||
Log.i(TAG, "storage mounted " + path);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
package org.mavlink.qgroundcontrol;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.InetAddress;
|
||||
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
public class TaiSync
|
||||
{
|
||||
private static final int HEADER_SIZE = 0x1C;
|
||||
|
||||
private static final byte PROTOCOL_REQUEST_CONNECTION = 0x00;
|
||||
private static final byte PROTOCOL_VERSION = 0x01;
|
||||
private static final byte PROTOCOL_CHANNEL = 0x02;
|
||||
private static final byte PROTOCOL_DATA = 0x03;
|
||||
|
||||
private static final int VIDEO_PORT = 5600;
|
||||
private static final int TAISYNC_VIDEO_PORT = 8000;
|
||||
private static final int TAISYNC_SETTINGS_PORT = 8200;
|
||||
private static final int TAISYNC_TELEMETRY_PORT = 8400;
|
||||
|
||||
private Object runLock;
|
||||
private boolean running = false;
|
||||
private DatagramSocket udpSocket = null;
|
||||
private Socket tcpSettingsSocket = null;
|
||||
private InputStream settingsInStream = null;
|
||||
private OutputStream settingsOutStream = null;
|
||||
private Socket tcpTelemetrySocket = null;
|
||||
private InputStream telemetryInStream = null;
|
||||
private OutputStream telemetryOutStream = null;
|
||||
private ParcelFileDescriptor mParcelFileDescriptor;
|
||||
private FileInputStream mFileInputStream;
|
||||
private FileOutputStream mFileOutputStream;
|
||||
private ExecutorService mThreadPool;
|
||||
private byte[] mBytes = new byte[32 * 1024];
|
||||
private byte vMaj = 0;
|
||||
|
||||
public TaiSync()
|
||||
{
|
||||
runLock = new Object();
|
||||
mThreadPool = Executors.newFixedThreadPool(3);
|
||||
}
|
||||
|
||||
public boolean isRunning()
|
||||
{
|
||||
synchronized (runLock) {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
|
||||
public void open(ParcelFileDescriptor parcelFileDescriptor) throws IOException
|
||||
{
|
||||
// Log.i("QGC_TaiSync", "Open");
|
||||
|
||||
synchronized (runLock) {
|
||||
if (running) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
}
|
||||
|
||||
mParcelFileDescriptor = parcelFileDescriptor;
|
||||
if (mParcelFileDescriptor == null) {
|
||||
throw new IOException("parcelFileDescriptor is null");
|
||||
}
|
||||
|
||||
FileDescriptor fileDescriptor = mParcelFileDescriptor.getFileDescriptor();
|
||||
mFileInputStream = new FileInputStream(fileDescriptor);
|
||||
mFileOutputStream = new FileOutputStream(fileDescriptor);
|
||||
|
||||
udpSocket = new DatagramSocket();
|
||||
final InetAddress address = InetAddress.getByName("localhost");
|
||||
tcpTelemetrySocket = new Socket(address, TAISYNC_TELEMETRY_PORT);
|
||||
telemetryInStream = tcpTelemetrySocket.getInputStream();
|
||||
telemetryOutStream = tcpTelemetrySocket.getOutputStream();
|
||||
tcpSettingsSocket = new Socket(address, TAISYNC_SETTINGS_PORT);
|
||||
settingsInStream = tcpSettingsSocket.getInputStream();
|
||||
settingsOutStream = tcpSettingsSocket.getOutputStream();
|
||||
|
||||
// Request connection packet
|
||||
sendTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0);
|
||||
|
||||
// Read multiplexed data stream coming from TaiSync accessory
|
||||
mThreadPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
int bytesRead = 0;
|
||||
|
||||
try {
|
||||
while (bytesRead >= 0) {
|
||||
synchronized (runLock) {
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bytesRead = mFileInputStream.read(mBytes);
|
||||
|
||||
if (bytesRead > 0)
|
||||
{
|
||||
if (mBytes[3] == PROTOCOL_VERSION)
|
||||
{
|
||||
vMaj = mBytes[19];
|
||||
// Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]);
|
||||
sendTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0);
|
||||
}
|
||||
else if (mBytes[3] == PROTOCOL_CHANNEL) {
|
||||
int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff);
|
||||
int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff);
|
||||
// Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength);
|
||||
sendTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0);
|
||||
}
|
||||
else if (mBytes[3] == PROTOCOL_DATA) {
|
||||
int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff);
|
||||
int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff);
|
||||
|
||||
int payloadOffset = HEADER_SIZE;
|
||||
int payloadLength = bytesRead - payloadOffset;
|
||||
|
||||
byte[] sBytes = new byte[payloadLength];
|
||||
System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength);
|
||||
|
||||
if (dPort == TAISYNC_VIDEO_PORT) {
|
||||
DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT);
|
||||
udpSocket.send(packet);
|
||||
} else if (dPort == TAISYNC_SETTINGS_PORT) {
|
||||
settingsOutStream.write(sBytes);
|
||||
} else if (dPort == TAISYNC_TELEMETRY_PORT) {
|
||||
telemetryOutStream.write(sBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("QGC_TaiSync", "Exception: " + e);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Read command & control packets to be sent to telemetry port
|
||||
mThreadPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
byte[] inbuf = new byte[256];
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
synchronized (runLock) {
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesRead = telemetryInStream.read(inbuf, 0, inbuf.length);
|
||||
if (bytesRead > 0) {
|
||||
sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, inbuf, bytesRead);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("QGC_TaiSync", "Exception: " + e);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Read incoming requests for settings socket
|
||||
mThreadPool.execute(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
byte[] inbuf = new byte[1024];
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
synchronized (runLock) {
|
||||
if (!running) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int bytesRead = settingsInStream.read(inbuf, 0, inbuf.length);
|
||||
if (bytesRead > 0) {
|
||||
sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e("QGC_TaiSync", "Exception: " + e);
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void sendTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) throws IOException
|
||||
{
|
||||
byte portMSB = (byte)((dataPort >> 8) & 0xFF);
|
||||
byte portLSB = (byte)(dataPort & 0xFF);
|
||||
|
||||
byte[] lA = new byte[4];
|
||||
int len = HEADER_SIZE + dataLen;
|
||||
// Log.i("QGC_TaiSync", "Sending to " + dataPort + " length = " + len);
|
||||
byte[] buffer = new byte[len];
|
||||
|
||||
for (int i = 3; i >= 0; i--) {
|
||||
lA[i] = (byte)(len & 0xFF);
|
||||
len >>= 8;
|
||||
}
|
||||
|
||||
byte[] header = { 0x00, 0x00, 0x00, protocol, // uint32 - protocol
|
||||
0x00, 0x00, portMSB, portLSB, // uint32 - dport
|
||||
lA[0], lA[1], lA[2], lA[3], // uint32 - length
|
||||
0x00, 0x00, 0x00, 0x00, // uint32 - magic
|
||||
0x00, 0x00, 0x00, vMaj, // uint32 - version major
|
||||
0x00, 0x00, 0x00, 0x00, // uint32 - version minor
|
||||
0x00, 0x00, 0x00, 0x00 }; // uint32 - padding
|
||||
|
||||
System.arraycopy(header, 0, buffer, 0, header.length);
|
||||
if (data != null && dataLen > 0) {
|
||||
System.arraycopy(data, 0, buffer, header.length, dataLen);
|
||||
}
|
||||
|
||||
synchronized (runLock) {
|
||||
mFileOutputStream.write(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
// Log.i("QGC_TaiSync", "Close");
|
||||
synchronized (runLock) {
|
||||
running = false;
|
||||
}
|
||||
try {
|
||||
if (udpSocket != null) {
|
||||
udpSocket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
if (tcpTelemetrySocket != null) {
|
||||
tcpTelemetrySocket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
if (tcpSettingsSocket != null) {
|
||||
tcpSettingsSocket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
if (mParcelFileDescriptor != null) {
|
||||
mParcelFileDescriptor.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
udpSocket = null;
|
||||
tcpSettingsSocket = null;
|
||||
tcpTelemetrySocket = null;
|
||||
settingsInStream = null;
|
||||
settingsOutStream = null;
|
||||
mParcelFileDescriptor = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
package org.mavlink.qgroundcontrol;
|
||||
|
||||
/* Copyright 2011 Google Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
* USA.
|
||||
*
|
||||
* Project home page: http://code.google.com/p/usb-serial-for-android/
|
||||
*/
|
||||
|
||||
|
||||
import com.hoho.android.usbserial.driver.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Utility class which services a {@link UsbSerialDriver} in its {@link #run()}
|
||||
* method.
|
||||
*
|
||||
* Original author mike wakerly (opensource@hoho.com)
|
||||
* Modified by Mike Goza
|
||||
*/
|
||||
public class UsbIoManager implements Runnable {
|
||||
private static final int READ_WAIT_MILLIS = 100;
|
||||
private static final int BUFSIZ = 4096;
|
||||
private static final String TAG = "QGC_UsbIoManager";
|
||||
|
||||
private final UsbSerialDriver mDriver;
|
||||
private long mUserData;
|
||||
private final ByteBuffer mReadBuffer = ByteBuffer.allocate(BUFSIZ);
|
||||
private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
|
||||
|
||||
private enum State
|
||||
{
|
||||
STOPPED,
|
||||
RUNNING,
|
||||
STOPPING
|
||||
}
|
||||
|
||||
// Synchronized by 'this'
|
||||
private State mState = State.STOPPED;
|
||||
|
||||
// Synchronized by 'this'
|
||||
private Listener mListener;
|
||||
|
||||
public interface Listener
|
||||
{
|
||||
/**
|
||||
* Called when new incoming data is available.
|
||||
*/
|
||||
public void onNewData(byte[] data, long userData);
|
||||
|
||||
/**
|
||||
* Called when {@link SerialInputOutputManager#run()} aborts due to an
|
||||
* error.
|
||||
*/
|
||||
public void onRunError(Exception e, long userData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance with no listener.
|
||||
*/
|
||||
public UsbIoManager(UsbSerialDriver driver)
|
||||
{
|
||||
this(driver, null, 0);
|
||||
Log.i(TAG, "Instance created");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new instance with the provided listener.
|
||||
*/
|
||||
public UsbIoManager(UsbSerialDriver driver, Listener listener, long userData)
|
||||
{
|
||||
mDriver = driver;
|
||||
mListener = listener;
|
||||
mUserData = userData;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public synchronized void setListener(Listener listener)
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public synchronized Listener getListener()
|
||||
{
|
||||
return mListener;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void writeAsync(byte[] data)
|
||||
{
|
||||
synchronized (mWriteBuffer)
|
||||
{
|
||||
mWriteBuffer.put(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public synchronized void stop()
|
||||
{
|
||||
if (getState() == State.RUNNING)
|
||||
{
|
||||
mState = State.STOPPING;
|
||||
mUserData = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private synchronized State getState()
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Continuously services the read and write buffers until {@link #stop()} is
|
||||
* called, or until a driver exception is raised.
|
||||
*/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (mState != State.STOPPED)
|
||||
throw new IllegalStateException("Already running.");
|
||||
|
||||
mState = State.RUNNING;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (mState != State.RUNNING)
|
||||
break;
|
||||
|
||||
step();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
final Listener listener = getListener();
|
||||
if (listener != null)
|
||||
listener.onRunError(e, mUserData);
|
||||
}
|
||||
finally
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
mState = State.STOPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void step() throws IOException
|
||||
{
|
||||
// Handle incoming data.
|
||||
int len = mDriver.read(mReadBuffer.array(), READ_WAIT_MILLIS);
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
final Listener listener = getListener();
|
||||
if (listener != null)
|
||||
{
|
||||
final byte[] data = new byte[len];
|
||||
mReadBuffer.get(data, 0, len);
|
||||
listener.onNewData(data, mUserData);
|
||||
}
|
||||
mReadBuffer.clear();
|
||||
}
|
||||
/*
|
||||
// Handle outgoing data.
|
||||
byte[] outBuff = null;
|
||||
synchronized (mWriteBuffer)
|
||||
{
|
||||
if (mWriteBuffer.position() > 0)
|
||||
{
|
||||
len = mWriteBuffer.position();
|
||||
outBuff = new byte[len];
|
||||
mWriteBuffer.rewind();
|
||||
mWriteBuffer.get(outBuff, 0, len);
|
||||
mWriteBuffer.clear();
|
||||
}
|
||||
}
|
||||
if (outBuff != null)
|
||||
mDriver.write(outBuff, READ_WAIT_MILLIS);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#!/bin/bash
|
||||
#----------------------------------------------------------
|
||||
# You will need:
|
||||
# - Qt 5.5.x android_armv7 kit
|
||||
# - Android SDK
|
||||
# - Androig NDK
|
||||
# - Current Java
|
||||
# - ant
|
||||
#----------------------------------------------------------
|
||||
# Update with correct location for these
|
||||
export ANDROID_HOME=~/Library/Android/sdk
|
||||
export ANDROID_SDK_ROOT=~/Library/Android/sdk
|
||||
export ANDROID_NDK_ROOT=~/Library/Android/sdk/ndk-bundle
|
||||
export ANDROID_NDK_HOST=darwin-x86_64
|
||||
export ANDROID_NDK_PLATFORM=/android-9
|
||||
export ANDROID_NDK_TOOLCHAIN_PREFIX=arm-linux-androideabi
|
||||
export ANDROID_NDK_TOOLCHAIN_VERSION=4.9
|
||||
export ANDROID_NDK_TOOLS_PREFIX=arm-linux-androideabi
|
||||
#----------------------------------------------------------
|
||||
# To build it, run (replacing the path with where you have Qt installed)
|
||||
#
|
||||
# >source android_environment.sh
|
||||
# cd ../
|
||||
# mkdir android_build
|
||||
# cd android_build
|
||||
# >~/local/Qt/5.4/android_armv7/bin/qmake -r -spec android-g++ CONFIG+=debug ../qgroundcontrol/qgroundcontrol.pro
|
||||
# >make -j24 install INSTALL_ROOT=./android-build/
|
||||
# >~/local/Qt/5.4/android_armv7/bin/androiddeployqt --input ./android-libQGroundControl.so-deployment-settings.json --output ./android-build --deployment bundled --android-platform android-22 --jdk /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home --verbose --ant /usr/local/bin/ant
|
||||
#
|
|
@ -0,0 +1,178 @@
|
|||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_policy(SET CMP0026 OLD) # allow use of the LOCATION target property
|
||||
|
||||
# store the current source directory for future use
|
||||
set(QT_ANDROID_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# check the JAVA_HOME environment variable
|
||||
# (I couldn't find a way to set it from this script, it has to be defined outside)
|
||||
set(JAVA_HOME $ENV{JAVA_HOME})
|
||||
if(NOT JAVA_HOME)
|
||||
message(FATAL_ERROR "The JAVA_HOME environment variable is not set. Please set it to the root directory of the JDK.")
|
||||
endif()
|
||||
|
||||
# make sure that the Android toolchain is used
|
||||
if(NOT ANDROID)
|
||||
message(FATAL_ERROR "Trying to use the CMake Android package without the Android toolchain. Please use the provided toolchain (toolchain/android.toolchain.cmake)")
|
||||
endif()
|
||||
|
||||
# find the Qt root directory
|
||||
if(NOT Qt5Core_DIR)
|
||||
find_package(Qt5Core REQUIRED)
|
||||
endif()
|
||||
get_filename_component(QT_ANDROID_QT_ROOT "${Qt5Core_DIR}/../../.." ABSOLUTE)
|
||||
message(STATUS "Found Qt for Android: ${QT_ANDROID_QT_ROOT}")
|
||||
|
||||
# find the Android SDK
|
||||
if(NOT QT_ANDROID_SDK_ROOT)
|
||||
set(QT_ANDROID_SDK_ROOT $ENV{ANDROID_SDK})
|
||||
if(NOT QT_ANDROID_SDK_ROOT)
|
||||
message(FATAL_ERROR "Could not find the Android SDK. Please set either the ANDROID_SDK environment variable, or the QT_ANDROID_SDK_ROOT CMake variable to the root directory of the Android SDK")
|
||||
endif()
|
||||
endif()
|
||||
string(REPLACE "\\" "/" QT_ANDROID_SDK_ROOT ${QT_ANDROID_SDK_ROOT}) # androiddeployqt doesn't like backslashes in paths
|
||||
message(STATUS "Found Android SDK: ${QT_ANDROID_SDK_ROOT}")
|
||||
|
||||
# find the Android NDK
|
||||
if(NOT QT_ANDROID_NDK_ROOT)
|
||||
set(QT_ANDROID_NDK_ROOT $ENV{ANDROID_NDK})
|
||||
if(NOT QT_ANDROID_NDK_ROOT)
|
||||
set(QT_ANDROID_NDK_ROOT ${ANDROID_NDK})
|
||||
if(NOT QT_ANDROID_NDK_ROOT)
|
||||
message(FATAL_ERROR "Could not find the Android NDK. Please set either the ANDROID_NDK environment or CMake variable, or the QT_ANDROID_NDK_ROOT CMake variable to the root directory of the Android NDK")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
string(REPLACE "\\" "/" QT_ANDROID_NDK_ROOT ${QT_ANDROID_NDK_ROOT}) # androiddeployqt doesn't like backslashes in paths
|
||||
message(STATUS "Found Android NDK: ${QT_ANDROID_NDK_ROOT}")
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
# define a macro to create an Android APK target
|
||||
#
|
||||
# example:
|
||||
# add_qt_android_apk(my_app_apk my_app
|
||||
# NAME "My App"
|
||||
# VERSION_CODE 12
|
||||
# PACKAGE_NAME "org.mycompany.myapp"
|
||||
# PACKAGE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/my-android-sources
|
||||
# BUILDTOOLS_REVISION "23.0.3"
|
||||
# KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
|
||||
# KEYSTORE_PASSWORD xxxx
|
||||
# DEPENDS a_linked_target "path/to/a_linked_library.so" ...
|
||||
# INSTALL
|
||||
#)
|
||||
#
|
||||
macro(add_qt_android_apk TARGET SOURCE_TARGET)
|
||||
|
||||
# parse the macro arguments
|
||||
cmake_parse_arguments(ARG "INSTALL" "NAME;VERSION_CODE;PACKAGE_NAME;PACKAGE_SOURCES;KEYSTORE_PASSWORD;BUILDTOOLS_REVISION" "DEPENDS;KEYSTORE" ${ARGN})
|
||||
|
||||
# extract the full path of the source target binary
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
get_property(QT_ANDROID_APP_PATH TARGET ${SOURCE_TARGET} PROPERTY DEBUG_LOCATION)
|
||||
else()
|
||||
get_property(QT_ANDROID_APP_PATH TARGET ${SOURCE_TARGET} PROPERTY LOCATION)
|
||||
endif()
|
||||
|
||||
# define the application name
|
||||
if(ARG_NAME)
|
||||
set(QT_ANDROID_APP_NAME ${ARG_NAME})
|
||||
else()
|
||||
set(QT_ANDROID_APP_NAME ${SOURCE_TARGET})
|
||||
endif()
|
||||
|
||||
# define the application package name
|
||||
if(ARG_PACKAGE_NAME)
|
||||
set(QT_ANDROID_APP_PACKAGE_NAME ${ARG_PACKAGE_NAME})
|
||||
else()
|
||||
set(QT_ANDROID_APP_PACKAGE_NAME org.qtproject.${SOURCE_TARGET})
|
||||
endif()
|
||||
|
||||
# set the Android SDK build-tools revision
|
||||
if(ARG_BUILDTOOLS_REVISION)
|
||||
set(QT_ANDROID_SDK_BUILDTOOLS_REVISION ${ARG_BUILDTOOLS_REVISION})
|
||||
else()
|
||||
set(QT_ANDROID_SDK_BUILDTOOLS_REVISION "")
|
||||
endif()
|
||||
|
||||
# define the application source package directory
|
||||
if(ARG_PACKAGE_SOURCES)
|
||||
set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT ${ARG_PACKAGE_SOURCES})
|
||||
else()
|
||||
# get version code from arguments, or generate a fixed one if not provided
|
||||
set(QT_ANDROID_APP_VERSION_CODE ${ARG_VERSION_CODE})
|
||||
if(NOT QT_ANDROID_APP_VERSION_CODE)
|
||||
set(QT_ANDROID_APP_VERSION_CODE 1)
|
||||
endif()
|
||||
|
||||
# try to extract the app version from the target properties, or use the version code if not provided
|
||||
get_property(QT_ANDROID_APP_VERSION TARGET ${SOURCE_TARGET} PROPERTY VERSION)
|
||||
if(NOT QT_ANDROID_APP_VERSION)
|
||||
set(QT_ANDROID_APP_VERSION ${QT_ANDROID_APP_VERSION_CODE})
|
||||
endif()
|
||||
|
||||
# create a subdirectory for the extra package sources
|
||||
set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/package")
|
||||
|
||||
# generate a manifest from the template
|
||||
configure_file(${QT_ANDROID_SOURCE_DIR}/AndroidManifest.xml.in ${QT_ANDROID_APP_PACKAGE_SOURCE_ROOT}/AndroidManifest.xml @ONLY)
|
||||
endif()
|
||||
|
||||
# set the list of dependant libraries
|
||||
if(ARG_DEPENDS)
|
||||
foreach(LIB ${ARG_DEPENDS})
|
||||
if(TARGET ${LIB})
|
||||
# item is a CMake target, extract the library path
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
get_property(LIB_PATH TARGET ${LIB} PROPERTY DEBUG_LOCATION)
|
||||
else()
|
||||
get_property(LIB_PATH TARGET ${LIB} PROPERTY LOCATION)
|
||||
endif()
|
||||
set(LIB ${LIB_PATH})
|
||||
endif()
|
||||
if(EXTRA_LIBS)
|
||||
set(EXTRA_LIBS "${EXTRA_LIBS},${LIB}")
|
||||
else()
|
||||
set(EXTRA_LIBS "${LIB}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(QT_ANDROID_APP_EXTRA_LIBS "\"android-extra-libs\": \"${EXTRA_LIBS}\",")
|
||||
endif()
|
||||
|
||||
# make sure that the output directory for the Android package exists
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI})
|
||||
|
||||
# create the configuration file that will feed androiddeployqt
|
||||
configure_file(${QT_ANDROID_SOURCE_DIR}/qtdeploy.json.in ${CMAKE_CURRENT_BINARY_DIR}/qtdeploy.json @ONLY)
|
||||
|
||||
# check if the apk must be signed
|
||||
if(ARG_KEYSTORE)
|
||||
set(SIGN_OPTIONS --release --sign ${ARG_KEYSTORE} --tsa http://timestamp.digicert.com)
|
||||
if(ARG_KEYSTORE_PASSWORD)
|
||||
set(SIGN_OPTIONS ${SIGN_OPTIONS} --storepass ${ARG_KEYSTORE_PASSWORD})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# check if the apk must be installed to the device
|
||||
if(ARG_INSTALL)
|
||||
set(INSTALL_OPTIONS --reinstall)
|
||||
endif()
|
||||
|
||||
# specify the Android API level
|
||||
if(ANDROID_NATIVE_API_LEVEL)
|
||||
set(TARGET_LEVEL_OPTIONS --android-platform android-${ANDROID_NATIVE_API_LEVEL})
|
||||
endif()
|
||||
|
||||
# create a custom command that will run the androiddeployqt utility to prepare the Android package
|
||||
add_custom_target(
|
||||
${TARGET}
|
||||
ALL
|
||||
DEPENDS ${SOURCE_TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI} # it seems that recompiled libraries are not copied if we don't remove them first
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${QT_ANDROID_APP_PATH} ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI}
|
||||
COMMAND ${QT_ANDROID_QT_ROOT}/bin/androiddeployqt --verbose --output ${CMAKE_CURRENT_BINARY_DIR} --input ${CMAKE_CURRENT_BINARY_DIR}/qtdeploy.json --gradle ${TARGET_LEVEL_OPTIONS} ${INSTALL_OPTIONS} ${SIGN_OPTIONS}
|
||||
)
|
||||
|
||||
endmacro()
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
if(BUILD_TESTING)
|
||||
add_definitions(-DUNITTEST_BUILD)
|
||||
else()
|
||||
# will prevent the definition of QT_DEBUG, which enables code that uses MockLink
|
||||
add_compile_definitions(QT_NO_DEBUG)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# clang and AppleClang
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
-Wno-address-of-packed-member # ignore for mavlink
|
||||
)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# GCC
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9)
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
endif()
|
||||
|
||||
add_compile_options(
|
||||
-Wall
|
||||
-Wextra
|
||||
)
|
||||
elseif (WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES)
|
||||
add_compile_options(
|
||||
/wd4244 # warning C4244: '=': conversion from 'double' to 'float', possible loss of data
|
||||
)
|
||||
endif()
|
|
@ -0,0 +1,35 @@
|
|||
find_package(Git)
|
||||
|
||||
if(NOT GIT_FOUND OR NOT EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Update submodules as needed
|
||||
option(GIT_SUBMODULE "Check submodules during build" ON)
|
||||
if(NOT GIT_SUBMODULE)
|
||||
return()
|
||||
endif()
|
||||
|
||||
message(STATUS "Submodule update")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
RESULT_VARIABLE GIT_SUBMODULE_RESULT)
|
||||
if(NOT GIT_SUBMODULE_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, please checkout submodules")
|
||||
endif()
|
||||
|
||||
# Fetch the necessary git variables
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE APP_VERSION_STR
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE REL_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --format=%aI ${REL_VERSION}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE REL_DATE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CSResourcesFileMapped</key>
|
||||
<true/>
|
||||
<key>LSRequiresCarbon</key>
|
||||
<true/>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,74 @@
|
|||
|
||||
if(LINUX)
|
||||
# TODO: investigate https://github.com/probonopd/linuxdeployqt
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/release/package/QGroundControl.AppImage
|
||||
COMMAND ${CMAKE_SOURCE_DIR}/deploy/create_linux_appimage.sh ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/release/package;
|
||||
DEPENDS QGroundControl
|
||||
USES_TERMINAL
|
||||
)
|
||||
add_custom_target(appimage DEPENDS ${CMAKE_BINARY_DIR}/release/package/QGroundControl.AppImage)
|
||||
|
||||
elseif(APPLE)
|
||||
|
||||
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
|
||||
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
|
||||
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}")
|
||||
|
||||
add_custom_target(dmg)
|
||||
|
||||
if(GST_FOUND)
|
||||
add_custom_command(TARGET dmg
|
||||
OUTPUT
|
||||
COMMAND
|
||||
${CMAKE_SOURCE_DIR}/tools/prepare_gstreamer_framework.sh ${CMAKE_BINARY_DIR}/gstwork/ QGroundControl.app QGroundControl
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_command(TARGET dmg
|
||||
OUTPUT
|
||||
COMMAND
|
||||
${MACDEPLOYQT_EXECUTABLE} $<TARGET_FILE_DIR:QGroundControl>/../.. -appstore-compliant -qmldir=${CMAKE_SOURCE_DIR}/src
|
||||
COMMAND
|
||||
rsync -a ${CMAKE_SOURCE_DIR}/libs/Frameworks $<TARGET_FILE_DIR:QGroundControl>/../../Contents/
|
||||
COMMAND
|
||||
${CMAKE_INSTALL_NAME_TOOL} -change "@rpath/SDL2.framework/Versions/A/SDL2" "@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2" $<TARGET_FILE:QGroundControl>
|
||||
COMMAND
|
||||
mkdir -p ${CMAKE_BINARY_DIR}/package
|
||||
COMMAND
|
||||
mkdir -p ${CMAKE_BINARY_DIR}/staging
|
||||
COMMAND
|
||||
rsync -a --delete ${CMAKE_BINARY_DIR}/QGroundControl.app ${CMAKE_BINARY_DIR}/staging
|
||||
COMMAND
|
||||
hdiutil create /tmp/tmp.dmg -ov -volname "QGroundControl-$${APP_VERSION_STR}" -fs HFS+ -srcfolder "staging"
|
||||
COMMAND
|
||||
hdiutil convert /tmp/tmp.dmg -format UDBZ -o ${CMAKE_BINARY_DIR}/package/QGroundControl.dmg
|
||||
)
|
||||
|
||||
set_target_properties(QGroundControl PROPERTIES MACOSX_BUNDLE YES)
|
||||
|
||||
elseif(WIN32)
|
||||
if(MSVC) # Check if we are using the Visual Studio compiler
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
WIN32_EXECUTABLE YES
|
||||
LINK_FLAGS "/ENTRY:mainCRTStartup"
|
||||
)
|
||||
endif()
|
||||
|
||||
# deploy
|
||||
include(Windeployqt)
|
||||
windeployqt(QGroundControl "QGroundControl-installer.exe")
|
||||
|
||||
add_custom_command(TARGET QGroundControl POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E
|
||||
copy_if_different $<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:QGroundControl>
|
||||
)
|
||||
|
||||
elseif(ANDROID)
|
||||
include(AddQtAndroidApk)
|
||||
add_qt_android_apk(QGroundControl.apk QGroundControl
|
||||
PACKAGE_NAME "io.mavlink.qgroundcontrol"
|
||||
#KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
|
||||
#KEYSTORE_PASSWORD xxxxx
|
||||
)
|
||||
endif()
|
|
@ -0,0 +1,43 @@
|
|||
if(DEFINED ENV{QT_VERSION})
|
||||
set(QT_VERSION $ENV{QT_VERSION})
|
||||
endif()
|
||||
|
||||
if(NOT QT_VERSION)
|
||||
# if QT version not specified then use any available version (5.12 or 5.15 only)
|
||||
file(GLOB FOUND_QT_VERSIONS
|
||||
LIST_DIRECTORIES true
|
||||
$ENV{HOME}/Qt/5.12.*
|
||||
$ENV{HOME}/Qt/5.15.*
|
||||
)
|
||||
if(NOT FOUND_QT_VERSIONS)
|
||||
return()
|
||||
endif()
|
||||
list(SORT FOUND_QT_VERSIONS) # prefer 5.12
|
||||
list(GET FOUND_QT_VERSIONS 0 QT_VERSION_PATH)
|
||||
get_filename_component(QT_VERSION ${QT_VERSION_PATH} NAME)
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{QT_MKSPEC})
|
||||
set(QT_MKSPEC $ENV{QT_MKSPEC})
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
set(LINUX TRUE)
|
||||
endif()
|
||||
|
||||
if(NOT QT_MKSPEC)
|
||||
if(APPLE)
|
||||
set(QT_MKSPEC clang_64)
|
||||
elseif(LINUX)
|
||||
set(QT_MKSPEC gcc_64)
|
||||
elseif(WIN32)
|
||||
set(QT_MKSPEC msvc2017_64)
|
||||
#set(QT_MKSPEC winrt_x64_msvc2017)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(QT_LIBRARY_HINTS
|
||||
$ENV{HOME}/Qt/${QT_VERSION}/${QT_MKSPEC}
|
||||
$ENV{QT_PATH}/${QT_VERSION}/${QT_MKSPEC}
|
||||
C:/Qt
|
||||
)
|
|
@ -0,0 +1,102 @@
|
|||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2017 Nathan Osman
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
find_package(Qt5Core REQUIRED)
|
||||
|
||||
# Retrieve the absolute path to qmake and then use that path to find
|
||||
# the windeployqt binary
|
||||
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
|
||||
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
|
||||
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}")
|
||||
|
||||
# Running this with MSVC 2015 requires CMake 3.6+
|
||||
if((MSVC_VERSION VERSION_EQUAL 1900 OR MSVC_VERSION VERSION_GREATER 1900)
|
||||
AND CMAKE_VERSION VERSION_LESS "3.6")
|
||||
message(WARNING "Deploying with MSVC 2015+ requires CMake 3.6+")
|
||||
endif()
|
||||
|
||||
# Add commands that copy the Qt runtime to the target's output directory after
|
||||
# build and install the Qt runtime to the specified directory
|
||||
function(windeployqt target directory)
|
||||
|
||||
# Run windeployqt immediately after build
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E
|
||||
env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}"
|
||||
--verbose 0
|
||||
--no-compiler-runtime
|
||||
--no-angle
|
||||
--no-opengl-sw
|
||||
\"$<TARGET_FILE:${target}>\"
|
||||
)
|
||||
|
||||
# install(CODE ...) doesn't support generator expressions, but
|
||||
# file(GENERATE ...) does - store the path in a file
|
||||
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${target}_path"
|
||||
CONTENT "$<TARGET_FILE:${target}>"
|
||||
)
|
||||
|
||||
# Before installation, run a series of commands that copy each of the Qt
|
||||
# runtime files to the appropriate directory for installation
|
||||
install(CODE
|
||||
"
|
||||
file(READ \"${CMAKE_CURRENT_BINARY_DIR}/${target}_path\" _file)
|
||||
execute_process(
|
||||
COMMAND \"${CMAKE_COMMAND}\" -E
|
||||
env PATH=\"${_qt_bin_dir}\" \"${WINDEPLOYQT_EXECUTABLE}\"
|
||||
--dry-run
|
||||
--no-compiler-runtime
|
||||
--no-angle
|
||||
--no-opengl-sw
|
||||
--list mapping
|
||||
\${_file}
|
||||
OUTPUT_VARIABLE _output
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
separate_arguments(_files WINDOWS_COMMAND \${_output})
|
||||
while(_files)
|
||||
list(GET _files 0 _src)
|
||||
list(GET _files 1 _dest)
|
||||
execute_process(
|
||||
COMMAND \"${CMAKE_COMMAND}\" -E
|
||||
copy \${_src} \"\${CMAKE_INSTALL_PREFIX}/${directory}/\${_dest}\"
|
||||
)
|
||||
list(REMOVE_AT _files 0 1)
|
||||
endwhile()
|
||||
"
|
||||
)
|
||||
|
||||
# windeployqt doesn't work correctly with the system runtime libraries,
|
||||
# so we fall back to one of CMake's own modules for copying them over
|
||||
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
|
||||
include(InstallRequiredSystemLibraries)
|
||||
foreach(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS})
|
||||
get_filename_component(filename "${lib}" NAME)
|
||||
add_custom_command(TARGET ${target} POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E
|
||||
copy_if_different "${lib}" \"$<TARGET_FILE_DIR:${target}>\"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
endfunction()
|
||||
|
||||
mark_as_advanced(WINDEPLOYQT_EXECUTABLE)
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"description": "This file is to be read by androiddeployqt",
|
||||
"qt": "@QT_ANDROID_QT_ROOT@",
|
||||
"sdk": "@QT_ANDROID_SDK_ROOT@",
|
||||
"ndk": "@QT_ANDROID_NDK_ROOT@",
|
||||
"sdkBuildToolsRevision": "@QT_ANDROID_SDK_BUILDTOOLS_REVISION@",
|
||||
"toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
|
||||
"tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
|
||||
"toolchain-version": "@ANDROID_COMPILER_VERSION@",
|
||||
"ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@",
|
||||
"target-architecture": "@ANDROID_ABI@",
|
||||
"application-binary": "@QT_ANDROID_APP_PATH@",
|
||||
"android-package": "@QT_ANDROID_APP_PACKAGE_NAME@",
|
||||
"android-app-name": "@QT_ANDROID_APP_NAME@",
|
||||
@QT_ANDROID_APP_EXTRA_LIBS@
|
||||
"android-package-source-directory": "@QT_ANDROID_APP_PACKAGE_SOURCE_ROOT@"
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#include "winver.h"
|
||||
|
||||
IDI_ICON1 ICON DISCARDABLE "@ICON_FILE@"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
|
||||
PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
|
||||
FILEFLAGS 0x0L
|
||||
FILEFLAGSMASK 0x3fL
|
||||
FILEOS 0x00040004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "000004b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "@COMPANY@"
|
||||
VALUE "FileDescription", "@PROJECT_NAME@"
|
||||
VALUE "FileVersion", "@PROJECT_VERSION@"
|
||||
VALUE "LegalCopyright", "@COPYRIGHT@"
|
||||
VALUE "InternalName", "@PROJECT_NAME@"
|
||||
VALUE "OriginalFilename", "@PROJECT_NAME@.exe"
|
||||
VALUE "ProductName", "@PROJECT_NAME@"
|
||||
VALUE "ProductVersion", "@PROJECT_VERSION@"
|
||||
END
|
||||
END
|
||||
END
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
files:
|
||||
- source: /translations/qgc.ts
|
||||
translation: /translations/qgc_source_%locale_with_underscore%.ts
|
||||
- source: /translations/qgc-json.ts
|
||||
translation: /translations/qgc_json_%locale_with_underscore%.ts
|
|
@ -0,0 +1,301 @@
|
|||
<RCC>
|
||||
<qresource prefix="/InstrumentValueIcons">
|
||||
<file alias="stethoscope.svg">../resources/InstrumentValueIcons/stethoscope.svg</file>
|
||||
<file alias="inbox-check.svg">../resources/InstrumentValueIcons/inbox-check.svg</file>
|
||||
<file alias="cheveron-up.svg">../resources/InstrumentValueIcons/cheveron-up.svg</file>
|
||||
<file alias="location-shopping.svg">../resources/InstrumentValueIcons/location-shopping.svg</file>
|
||||
<file alias="cheveron-right.svg">../resources/InstrumentValueIcons/cheveron-right.svg</file>
|
||||
<file alias="bookmark.svg">../resources/InstrumentValueIcons/bookmark.svg</file>
|
||||
<file alias="travel-case.svg">../resources/InstrumentValueIcons/travel-case.svg</file>
|
||||
<file alias="user-solid-square.svg">../resources/InstrumentValueIcons/user-solid-square.svg</file>
|
||||
<file alias="arrow-simple-up.svg">../resources/InstrumentValueIcons/arrow-simple-up.svg</file>
|
||||
<file alias="list.svg">../resources/InstrumentValueIcons/list.svg</file>
|
||||
<file alias="shuffle.svg">../resources/InstrumentValueIcons/shuffle.svg</file>
|
||||
<file alias="travel-walk.svg">../resources/InstrumentValueIcons/travel-walk.svg</file>
|
||||
<file alias="checkmark.svg">../resources/InstrumentValueIcons/checkmark.svg</file>
|
||||
<file alias="trophy.svg">../resources/InstrumentValueIcons/trophy.svg</file>
|
||||
<file alias="mood-happy-solid.svg">../resources/InstrumentValueIcons/mood-happy-solid.svg</file>
|
||||
<file alias="cog.svg">../resources/InstrumentValueIcons/cog.svg</file>
|
||||
<file alias="text-box.svg">../resources/InstrumentValueIcons/text-box.svg</file>
|
||||
<file alias="video-camera.svg">../resources/InstrumentValueIcons/video-camera.svg</file>
|
||||
<file alias="backward-step.svg">../resources/InstrumentValueIcons/backward-step.svg</file>
|
||||
<file alias="view-column.svg">../resources/InstrumentValueIcons/view-column.svg</file>
|
||||
<file alias="add-outline.svg">../resources/InstrumentValueIcons/add-outline.svg</file>
|
||||
<file alias="battery-low.svg">../resources/InstrumentValueIcons/battery-low.svg</file>
|
||||
<file alias="bookmark copy 2.svg">../resources/InstrumentValueIcons/bookmark copy 2.svg</file>
|
||||
<file alias="volume-down.svg">../resources/InstrumentValueIcons/volume-down.svg</file>
|
||||
<file alias="travel-taxi-cab.svg">../resources/InstrumentValueIcons/travel-taxi-cab.svg</file>
|
||||
<file alias="camera.svg">../resources/InstrumentValueIcons/camera.svg</file>
|
||||
<file alias="apparel.svg">../resources/InstrumentValueIcons/apparel.svg</file>
|
||||
<file alias="checkmark-outline.svg">../resources/InstrumentValueIcons/checkmark-outline.svg</file>
|
||||
<file alias="envelope.svg">../resources/InstrumentValueIcons/envelope.svg</file>
|
||||
<file alias="flag.svg">../resources/InstrumentValueIcons/flag.svg</file>
|
||||
<file alias="window-new.svg">../resources/InstrumentValueIcons/window-new.svg</file>
|
||||
<file alias="arrow-simple-right.svg">../resources/InstrumentValueIcons/arrow-simple-right.svg</file>
|
||||
<file alias="filter.svg">../resources/InstrumentValueIcons/filter.svg</file>
|
||||
<file alias="minus-outline.svg">../resources/InstrumentValueIcons/minus-outline.svg</file>
|
||||
<file alias="edit-cut.svg">../resources/InstrumentValueIcons/edit-cut.svg</file>
|
||||
<file alias="volume-off.svg">../resources/InstrumentValueIcons/volume-off.svg</file>
|
||||
<file alias="zoom-in.svg">../resources/InstrumentValueIcons/zoom-in.svg</file>
|
||||
<file alias="pin.svg">../resources/InstrumentValueIcons/pin.svg</file>
|
||||
<file alias="layers.svg">../resources/InstrumentValueIcons/layers.svg</file>
|
||||
<file alias="airplane.svg">../resources/InstrumentValueIcons/airplane.svg</file>
|
||||
<file alias="view-tile.svg">../resources/InstrumentValueIcons/view-tile.svg</file>
|
||||
<file alias="location-marina.svg">../resources/InstrumentValueIcons/location-marina.svg</file>
|
||||
<file alias="border-top.svg">../resources/InstrumentValueIcons/border-top.svg</file>
|
||||
<file alias="refresh.svg">../resources/InstrumentValueIcons/refresh.svg</file>
|
||||
<file alias="travel-bus.svg">../resources/InstrumentValueIcons/travel-bus.svg</file>
|
||||
<file alias="add-solid.svg">../resources/InstrumentValueIcons/add-solid.svg</file>
|
||||
<file alias="notifications.svg">../resources/InstrumentValueIcons/notifications.svg</file>
|
||||
<file alias="indent-decrease.svg">../resources/InstrumentValueIcons/indent-decrease.svg</file>
|
||||
<file alias="badge.svg">../resources/InstrumentValueIcons/badge.svg</file>
|
||||
<file alias="align-center.svg">../resources/InstrumentValueIcons/align-center.svg</file>
|
||||
<file alias="queue.svg">../resources/InstrumentValueIcons/queue.svg</file>
|
||||
<file alias="conversation.svg">../resources/InstrumentValueIcons/conversation.svg</file>
|
||||
<file alias="inbox-download.svg">../resources/InstrumentValueIcons/inbox-download.svg</file>
|
||||
<file alias="cloud.svg">../resources/InstrumentValueIcons/cloud.svg</file>
|
||||
<file alias="text-decoration.svg">../resources/InstrumentValueIcons/text-decoration.svg</file>
|
||||
<file alias="date-add.svg">../resources/InstrumentValueIcons/date-add.svg</file>
|
||||
<file alias="network.svg">../resources/InstrumentValueIcons/network.svg</file>
|
||||
<file alias="list-add.svg">../resources/InstrumentValueIcons/list-add.svg</file>
|
||||
<file alias="film.svg">../resources/InstrumentValueIcons/film.svg</file>
|
||||
<file alias="book-reference.svg">../resources/InstrumentValueIcons/book-reference.svg</file>
|
||||
<file alias="star-full.svg">../resources/InstrumentValueIcons/star-full.svg</file>
|
||||
<file alias="information-outline.svg">../resources/InstrumentValueIcons/information-outline.svg</file>
|
||||
<file alias="user-group.svg">../resources/InstrumentValueIcons/user-group.svg</file>
|
||||
<file alias="hard-drive.svg">../resources/InstrumentValueIcons/hard-drive.svg</file>
|
||||
<file alias="chart-bar.svg">../resources/InstrumentValueIcons/chart-bar.svg</file>
|
||||
<file alias="box.svg">../resources/InstrumentValueIcons/box.svg</file>
|
||||
<file alias="music-notes.svg">../resources/InstrumentValueIcons/music-notes.svg</file>
|
||||
<file alias="bookmark copy 3.svg">../resources/InstrumentValueIcons/bookmark copy 3.svg</file>
|
||||
<file alias="anchor.svg">../resources/InstrumentValueIcons/anchor.svg</file>
|
||||
<file alias="mood-sad-solid.svg">../resources/InstrumentValueIcons/mood-sad-solid.svg</file>
|
||||
<file alias="coffee.svg">../resources/InstrumentValueIcons/coffee.svg</file>
|
||||
<file alias="mood-neutral-outline.svg">../resources/InstrumentValueIcons/mood-neutral-outline.svg</file>
|
||||
<file alias="menu.svg">../resources/InstrumentValueIcons/menu.svg</file>
|
||||
<file alias="fast-rewind.svg">../resources/InstrumentValueIcons/fast-rewind.svg</file>
|
||||
<file alias="user-add.svg">../resources/InstrumentValueIcons/user-add.svg</file>
|
||||
<file alias="mobile-devices.svg">../resources/InstrumentValueIcons/mobile-devices.svg</file>
|
||||
<file alias="time.svg">../resources/InstrumentValueIcons/time.svg</file>
|
||||
<file alias="subdirectory-left.svg">../resources/InstrumentValueIcons/subdirectory-left.svg</file>
|
||||
<file alias="window.svg">../resources/InstrumentValueIcons/window.svg</file>
|
||||
<file alias="hand-stop.svg">../resources/InstrumentValueIcons/hand-stop.svg</file>
|
||||
<file alias="beverage.svg">../resources/InstrumentValueIcons/beverage.svg</file>
|
||||
<file alias="volume-mute.svg">../resources/InstrumentValueIcons/volume-mute.svg</file>
|
||||
<file alias="reply-all.svg">../resources/InstrumentValueIcons/reply-all.svg</file>
|
||||
<file alias="location-food.svg">../resources/InstrumentValueIcons/location-food.svg</file>
|
||||
<file alias="arrow-thin-left.svg">../resources/InstrumentValueIcons/arrow-thin-left.svg</file>
|
||||
<file alias="folder-outline.svg">../resources/InstrumentValueIcons/folder-outline.svg</file>
|
||||
<file alias="dial-pad.svg">../resources/InstrumentValueIcons/dial-pad.svg</file>
|
||||
<file alias="battery-half.svg">../resources/InstrumentValueIcons/battery-half.svg</file>
|
||||
<file alias="trash.svg">../resources/InstrumentValueIcons/trash.svg</file>
|
||||
<file alias="notification.svg">../resources/InstrumentValueIcons/notification.svg</file>
|
||||
<file alias="send.svg">../resources/InstrumentValueIcons/send.svg</file>
|
||||
<file alias="station.svg">../resources/InstrumentValueIcons/station.svg</file>
|
||||
<file alias="pen-tool.svg">../resources/InstrumentValueIcons/pen-tool.svg</file>
|
||||
<file alias="gift.svg">../resources/InstrumentValueIcons/gift.svg</file>
|
||||
<file alias="arrow-outline-down.svg">../resources/InstrumentValueIcons/arrow-outline-down.svg</file>
|
||||
<file alias="ticket.svg">../resources/InstrumentValueIcons/ticket.svg</file>
|
||||
<file alias="border-none.svg">../resources/InstrumentValueIcons/border-none.svg</file>
|
||||
<file alias="format-italic.svg">../resources/InstrumentValueIcons/format-italic.svg</file>
|
||||
<file alias="user-solid-circle.svg">../resources/InstrumentValueIcons/user-solid-circle.svg</file>
|
||||
<file alias="edit-pencil.svg">../resources/InstrumentValueIcons/edit-pencil.svg</file>
|
||||
<file alias="step-forward.svg">../resources/InstrumentValueIcons/step-forward.svg</file>
|
||||
<file alias="edit-copy.svg">../resources/InstrumentValueIcons/edit-copy.svg</file>
|
||||
<file alias="globe.svg">../resources/InstrumentValueIcons/globe.svg</file>
|
||||
<file alias="arrow-thin-right.svg">../resources/InstrumentValueIcons/arrow-thin-right.svg</file>
|
||||
<file alias="inbox-full.svg">../resources/InstrumentValueIcons/inbox-full.svg</file>
|
||||
<file alias="mood-sad-outline.svg">../resources/InstrumentValueIcons/mood-sad-outline.svg</file>
|
||||
<file alias="bug.svg">../resources/InstrumentValueIcons/bug.svg</file>
|
||||
<file alias="question.svg">../resources/InstrumentValueIcons/question.svg</file>
|
||||
<file alias="dots-horizontal-double.svg">../resources/InstrumentValueIcons/dots-horizontal-double.svg</file>
|
||||
<file alias="format-bold.svg">../resources/InstrumentValueIcons/format-bold.svg</file>
|
||||
<file alias="close-outline.svg">../resources/InstrumentValueIcons/close-outline.svg</file>
|
||||
<file alias="dots-horizontal-triple.svg">../resources/InstrumentValueIcons/dots-horizontal-triple.svg</file>
|
||||
<file alias="format-text-size.svg">../resources/InstrumentValueIcons/format-text-size.svg</file>
|
||||
<file alias="computer-laptop.svg">../resources/InstrumentValueIcons/computer-laptop.svg</file>
|
||||
<file alias="arrow-thick-down.svg">../resources/InstrumentValueIcons/arrow-thick-down.svg</file>
|
||||
<file alias="cheveron-outline-down.svg">../resources/InstrumentValueIcons/cheveron-outline-down.svg</file>
|
||||
<file alias="travel.svg">../resources/InstrumentValueIcons/travel.svg</file>
|
||||
<file alias="usb.svg">../resources/InstrumentValueIcons/usb.svg</file>
|
||||
<file alias="cheveron-down.svg">../resources/InstrumentValueIcons/cheveron-down.svg</file>
|
||||
<file alias="key.svg">../resources/InstrumentValueIcons/key.svg</file>
|
||||
<file alias="tools copy.svg">../resources/InstrumentValueIcons/tools copy.svg</file>
|
||||
<file alias="album.svg">../resources/InstrumentValueIcons/album.svg</file>
|
||||
<file alias="arrow-base-down.svg">../resources/InstrumentValueIcons/arrow-base-down.svg</file>
|
||||
<file alias="copy.svg">../resources/InstrumentValueIcons/copy.svg</file>
|
||||
<file alias="align-left.svg">../resources/InstrumentValueIcons/align-left.svg</file>
|
||||
<file alias="explore.svg">../resources/InstrumentValueIcons/explore.svg</file>
|
||||
<file alias="watch.svg">../resources/InstrumentValueIcons/watch.svg</file>
|
||||
<file alias="playlist.svg">../resources/InstrumentValueIcons/playlist.svg</file>
|
||||
<file alias="pause-outline.svg">../resources/InstrumentValueIcons/pause-outline.svg</file>
|
||||
<file alias="location-current.svg">../resources/InstrumentValueIcons/location-current.svg</file>
|
||||
<file alias="home.svg">../resources/InstrumentValueIcons/home.svg</file>
|
||||
<file alias="battery-full.svg">../resources/InstrumentValueIcons/battery-full.svg</file>
|
||||
<file alias="format-font-size.svg">../resources/InstrumentValueIcons/format-font-size.svg</file>
|
||||
<file alias="exclamation-solid.svg">../resources/InstrumentValueIcons/exclamation-solid.svg</file>
|
||||
<file alias="music-artist.svg">../resources/InstrumentValueIcons/music-artist.svg</file>
|
||||
<file alias="music-album.svg">../resources/InstrumentValueIcons/music-album.svg</file>
|
||||
<file alias="chart-pie.svg">../resources/InstrumentValueIcons/chart-pie.svg</file>
|
||||
<file alias="photo.svg">../resources/InstrumentValueIcons/photo.svg</file>
|
||||
<file alias="lock-open.svg">../resources/InstrumentValueIcons/lock-open.svg</file>
|
||||
<file alias="inbox.svg">../resources/InstrumentValueIcons/inbox.svg</file>
|
||||
<file alias="hot.svg">../resources/InstrumentValueIcons/hot.svg</file>
|
||||
<file alias="browser-window-new.svg">../resources/InstrumentValueIcons/browser-window-new.svg</file>
|
||||
<file alias="zoom-out.svg">../resources/InstrumentValueIcons/zoom-out.svg</file>
|
||||
<file alias="search.svg">../resources/InstrumentValueIcons/search.svg</file>
|
||||
<file alias="backward.svg">../resources/InstrumentValueIcons/backward.svg</file>
|
||||
<file alias="store-front.svg">../resources/InstrumentValueIcons/store-front.svg</file>
|
||||
<file alias="user.svg">../resources/InstrumentValueIcons/user.svg</file>
|
||||
<file alias="lock-closed.svg">../resources/InstrumentValueIcons/lock-closed.svg</file>
|
||||
<file alias="load-balancer.svg">../resources/InstrumentValueIcons/load-balancer.svg</file>
|
||||
<file alias="border-all.svg">../resources/InstrumentValueIcons/border-all.svg</file>
|
||||
<file alias="location-gas-station.svg">../resources/InstrumentValueIcons/location-gas-station.svg</file>
|
||||
<file alias="news-paper.svg">../resources/InstrumentValueIcons/news-paper.svg</file>
|
||||
<file alias="align-justified.svg">../resources/InstrumentValueIcons/align-justified.svg</file>
|
||||
<file alias="color-palette.svg">../resources/InstrumentValueIcons/color-palette.svg</file>
|
||||
<file alias="radio.svg">../resources/InstrumentValueIcons/radio.svg</file>
|
||||
<file alias="reply.svg">../resources/InstrumentValueIcons/reply.svg</file>
|
||||
<file alias="target.svg">../resources/InstrumentValueIcons/target.svg</file>
|
||||
<file alias="border-vertical.svg">../resources/InstrumentValueIcons/border-vertical.svg</file>
|
||||
<file alias="duplicate.svg">../resources/InstrumentValueIcons/duplicate.svg</file>
|
||||
<file alias="arrow-thick-right.svg">../resources/InstrumentValueIcons/arrow-thick-right.svg</file>
|
||||
<file alias="exclamation-outline.svg">../resources/InstrumentValueIcons/exclamation-outline.svg</file>
|
||||
<file alias="bolt.svg">../resources/InstrumentValueIcons/bolt.svg</file>
|
||||
<file alias="shopping-cart.svg">../resources/InstrumentValueIcons/shopping-cart.svg</file>
|
||||
<file alias="calendar.svg">../resources/InstrumentValueIcons/calendar.svg</file>
|
||||
<file alias="travel-train.svg">../resources/InstrumentValueIcons/travel-train.svg</file>
|
||||
<file alias="save-disk.svg">../resources/InstrumentValueIcons/save-disk.svg</file>
|
||||
<file alias="cloud-upload.svg">../resources/InstrumentValueIcons/cloud-upload.svg</file>
|
||||
<file alias="dashboard.svg">../resources/InstrumentValueIcons/dashboard.svg</file>
|
||||
<file alias="arrow-simple-down.svg">../resources/InstrumentValueIcons/arrow-simple-down.svg</file>
|
||||
<file alias="computer-desktop.svg">../resources/InstrumentValueIcons/computer-desktop.svg</file>
|
||||
<file alias="music-playlist.svg">../resources/InstrumentValueIcons/music-playlist.svg</file>
|
||||
<file alias="share-01.svg">../resources/InstrumentValueIcons/share-01.svg</file>
|
||||
<file alias="travel-car.svg">../resources/InstrumentValueIcons/travel-car.svg</file>
|
||||
<file alias="border-horizontal.svg">../resources/InstrumentValueIcons/border-horizontal.svg</file>
|
||||
<file alias="fast-forward.svg">../resources/InstrumentValueIcons/fast-forward.svg</file>
|
||||
<file alias="keyboard.svg">../resources/InstrumentValueIcons/keyboard.svg</file>
|
||||
<file alias="chat-bubble-dots.svg">../resources/InstrumentValueIcons/chat-bubble-dots.svg</file>
|
||||
<file alias="folder.svg">../resources/InstrumentValueIcons/folder.svg</file>
|
||||
<file alias="document.svg">../resources/InstrumentValueIcons/document.svg</file>
|
||||
<file alias="format-underline.svg">../resources/InstrumentValueIcons/format-underline.svg</file>
|
||||
<file alias="arrow-thin-down.svg">../resources/InstrumentValueIcons/arrow-thin-down.svg</file>
|
||||
<file alias="cheveron-outline-left.svg">../resources/InstrumentValueIcons/cheveron-outline-left.svg</file>
|
||||
<file alias="thumbs-down.svg">../resources/InstrumentValueIcons/thumbs-down.svg</file>
|
||||
<file alias="stroke-width.svg">../resources/InstrumentValueIcons/stroke-width.svg</file>
|
||||
<file alias="yin-yang.svg">../resources/InstrumentValueIcons/yin-yang.svg</file>
|
||||
<file alias="thumbs-up.svg">../resources/InstrumentValueIcons/thumbs-up.svg</file>
|
||||
<file alias="cheveron-left.svg">../resources/InstrumentValueIcons/cheveron-left.svg</file>
|
||||
<file alias="radar.svg">../resources/InstrumentValueIcons/radar.svg</file>
|
||||
<file alias="play.svg">../resources/InstrumentValueIcons/play.svg</file>
|
||||
<file alias="bluetooth.svg">../resources/InstrumentValueIcons/bluetooth.svg</file>
|
||||
<file alias="clipboard.svg">../resources/InstrumentValueIcons/clipboard.svg</file>
|
||||
<file alias="mood-happy-outline.svg">../resources/InstrumentValueIcons/mood-happy-outline.svg</file>
|
||||
<file alias="reload.svg">../resources/InstrumentValueIcons/reload.svg</file>
|
||||
<file alias="information-solid.svg">../resources/InstrumentValueIcons/information-solid.svg</file>
|
||||
<file alias="php-elephant.svg">../resources/InstrumentValueIcons/php-elephant.svg</file>
|
||||
<file alias="bookmark-outline-add.svg">../resources/InstrumentValueIcons/bookmark-outline-add.svg</file>
|
||||
<file alias="border-inner.svg">../resources/InstrumentValueIcons/border-inner.svg</file>
|
||||
<file alias="brightness-up.svg">../resources/InstrumentValueIcons/brightness-up.svg</file>
|
||||
<file alias="view-hide.svg">../resources/InstrumentValueIcons/view-hide.svg</file>
|
||||
<file alias="forward-step.svg">../resources/InstrumentValueIcons/forward-step.svg</file>
|
||||
<file alias="cheveron-outline-up.svg">../resources/InstrumentValueIcons/cheveron-outline-up.svg</file>
|
||||
<file alias="cheveron-outline-right.svg">../resources/InstrumentValueIcons/cheveron-outline-right.svg</file>
|
||||
<file alias="share.svg">../resources/InstrumentValueIcons/share.svg</file>
|
||||
<file alias="border-right.svg">../resources/InstrumentValueIcons/border-right.svg</file>
|
||||
<file alias="location-hotel.svg">../resources/InstrumentValueIcons/location-hotel.svg</file>
|
||||
<file alias="brightness-down.svg">../resources/InstrumentValueIcons/brightness-down.svg</file>
|
||||
<file alias="light-bulb.svg">../resources/InstrumentValueIcons/light-bulb.svg</file>
|
||||
<file alias="location-park.svg">../resources/InstrumentValueIcons/location-park.svg</file>
|
||||
<file alias="factory.svg">../resources/InstrumentValueIcons/factory.svg</file>
|
||||
<file alias="stand-by.svg">../resources/InstrumentValueIcons/stand-by.svg</file>
|
||||
<file alias="swap.svg">../resources/InstrumentValueIcons/swap.svg</file>
|
||||
<file alias="portfolio.svg">../resources/InstrumentValueIcons/portfolio.svg</file>
|
||||
<file alias="arrow-outline-right.svg">../resources/InstrumentValueIcons/arrow-outline-right.svg</file>
|
||||
<file alias="tuning.svg">../resources/InstrumentValueIcons/tuning.svg</file>
|
||||
<file alias="view-carousel.svg">../resources/InstrumentValueIcons/view-carousel.svg</file>
|
||||
<file alias="calculator.svg">../resources/InstrumentValueIcons/calculator.svg</file>
|
||||
<file alias="show-sidebar.svg">../resources/InstrumentValueIcons/show-sidebar.svg</file>
|
||||
<file alias="step-backward.svg">../resources/InstrumentValueIcons/step-backward.svg</file>
|
||||
<file alias="tablet.svg">../resources/InstrumentValueIcons/tablet.svg</file>
|
||||
<file alias="backspace.svg">../resources/InstrumentValueIcons/backspace.svg</file>
|
||||
<file alias="map.svg">../resources/InstrumentValueIcons/map.svg</file>
|
||||
<file alias="arrow-simple-left.svg">../resources/InstrumentValueIcons/arrow-simple-left.svg</file>
|
||||
<file alias="view-show.svg">../resources/InstrumentValueIcons/view-show.svg</file>
|
||||
<file alias="adjust.svg">../resources/InstrumentValueIcons/adjust.svg</file>
|
||||
<file alias="border-bottom.svg">../resources/InstrumentValueIcons/border-bottom.svg</file>
|
||||
<file alias="document-add.svg">../resources/InstrumentValueIcons/document-add.svg</file>
|
||||
<file alias="plugin.svg">../resources/InstrumentValueIcons/plugin.svg</file>
|
||||
<file alias="navigation-more.svg">../resources/InstrumentValueIcons/navigation-more.svg</file>
|
||||
<file alias="browser-window-open.svg">../resources/InstrumentValueIcons/browser-window-open.svg</file>
|
||||
<file alias="flashlight.svg">../resources/InstrumentValueIcons/flashlight.svg</file>
|
||||
<file alias="currency-dollar.svg">../resources/InstrumentValueIcons/currency-dollar.svg</file>
|
||||
<file alias="close-solid.svg">../resources/InstrumentValueIcons/close-solid.svg</file>
|
||||
<file alias="vector.svg">../resources/InstrumentValueIcons/vector.svg</file>
|
||||
<file alias="paste.svg">../resources/InstrumentValueIcons/paste.svg</file>
|
||||
<file alias="arrow-outline-up.svg">../resources/InstrumentValueIcons/arrow-outline-up.svg</file>
|
||||
<file alias="pause.svg">../resources/InstrumentValueIcons/pause.svg</file>
|
||||
<file alias="artist.svg">../resources/InstrumentValueIcons/artist.svg</file>
|
||||
<file alias="hour-glass.svg">../resources/InstrumentValueIcons/hour-glass.svg</file>
|
||||
<file alias="browser-window.svg">../resources/InstrumentValueIcons/browser-window.svg</file>
|
||||
<file alias="border-left.svg">../resources/InstrumentValueIcons/border-left.svg</file>
|
||||
<file alias="tag.svg">../resources/InstrumentValueIcons/tag.svg</file>
|
||||
<file alias="translate.svg">../resources/InstrumentValueIcons/translate.svg</file>
|
||||
<file alias="mood-neutral-solid.svg">../resources/InstrumentValueIcons/mood-neutral-solid.svg</file>
|
||||
<file alias="pause-solid.svg">../resources/InstrumentValueIcons/pause-solid.svg</file>
|
||||
<file alias="phone.svg">../resources/InstrumentValueIcons/phone.svg</file>
|
||||
<file alias="heart.svg">../resources/InstrumentValueIcons/heart.svg</file>
|
||||
<file alias="headphones.svg">../resources/InstrumentValueIcons/headphones.svg</file>
|
||||
<file alias="servers.svg">../resources/InstrumentValueIcons/servers.svg</file>
|
||||
<file alias="close.svg">../resources/InstrumentValueIcons/close.svg</file>
|
||||
<file alias="directions.svg">../resources/InstrumentValueIcons/directions.svg</file>
|
||||
<file alias="arrow-thick-left.svg">../resources/InstrumentValueIcons/arrow-thick-left.svg</file>
|
||||
<file alias="play-outline.svg">../resources/InstrumentValueIcons/play-outline.svg</file>
|
||||
<file alias="arrow-thick-up.svg">../resources/InstrumentValueIcons/arrow-thick-up.svg</file>
|
||||
<file alias="code.svg">../resources/InstrumentValueIcons/code.svg</file>
|
||||
<file alias="thermometer.svg">../resources/InstrumentValueIcons/thermometer.svg</file>
|
||||
<file alias="location-restroom.svg">../resources/InstrumentValueIcons/location-restroom.svg</file>
|
||||
<file alias="list-bullet.svg">../resources/InstrumentValueIcons/list-bullet.svg</file>
|
||||
<file alias="wrench.svg">../resources/InstrumentValueIcons/wrench.svg</file>
|
||||
<file alias="compose.svg">../resources/InstrumentValueIcons/compose.svg</file>
|
||||
<file alias="at-symbol.svg">../resources/InstrumentValueIcons/at-symbol.svg</file>
|
||||
<file alias="library.svg">../resources/InstrumentValueIcons/library.svg</file>
|
||||
<file alias="view-list.svg">../resources/InstrumentValueIcons/view-list.svg</file>
|
||||
<file alias="timer.svg">../resources/InstrumentValueIcons/timer.svg</file>
|
||||
<file alias="window-open.svg">../resources/InstrumentValueIcons/window-open.svg</file>
|
||||
<file alias="mouse.svg">../resources/InstrumentValueIcons/mouse.svg</file>
|
||||
<file alias="buoy.svg">../resources/InstrumentValueIcons/buoy.svg</file>
|
||||
<file alias="subdirectory-right.svg">../resources/InstrumentValueIcons/subdirectory-right.svg</file>
|
||||
<file alias="volume-up.svg">../resources/InstrumentValueIcons/volume-up.svg</file>
|
||||
<file alias="edit-crop.svg">../resources/InstrumentValueIcons/edit-crop.svg</file>
|
||||
<file alias="screen-full.svg">../resources/InstrumentValueIcons/screen-full.svg</file>
|
||||
<file alias="forward.svg">../resources/InstrumentValueIcons/forward.svg</file>
|
||||
<file alias="pylon.svg">../resources/InstrumentValueIcons/pylon.svg</file>
|
||||
<file alias="align-right.svg">../resources/InstrumentValueIcons/align-right.svg</file>
|
||||
<file alias="border-outer.svg">../resources/InstrumentValueIcons/border-outer.svg</file>
|
||||
<file alias="arrow-thin-up.svg">../resources/InstrumentValueIcons/arrow-thin-up.svg</file>
|
||||
<file alias="printer.svg">../resources/InstrumentValueIcons/printer.svg</file>
|
||||
<file alias="wallet.svg">../resources/InstrumentValueIcons/wallet.svg</file>
|
||||
<file alias="arrow-outline-left.svg">../resources/InstrumentValueIcons/arrow-outline-left.svg</file>
|
||||
<file alias="credit-card.svg">../resources/InstrumentValueIcons/credit-card.svg</file>
|
||||
<file alias="repost.svg">../resources/InstrumentValueIcons/repost.svg</file>
|
||||
<file alias="notifications-outline.svg">../resources/InstrumentValueIcons/notifications-outline.svg</file>
|
||||
<file alias="bookmark-outline.svg">../resources/InstrumentValueIcons/bookmark-outline.svg</file>
|
||||
<file alias="radar copy 2.svg">../resources/InstrumentValueIcons/radar copy 2.svg</file>
|
||||
<file alias="share-alt.svg">../resources/InstrumentValueIcons/share-alt.svg</file>
|
||||
<file alias="link.svg">../resources/InstrumentValueIcons/link.svg</file>
|
||||
<file alias="attachment.svg">../resources/InstrumentValueIcons/attachment.svg</file>
|
||||
<file alias="chart.svg">../resources/InstrumentValueIcons/chart.svg</file>
|
||||
<file alias="shield.svg">../resources/InstrumentValueIcons/shield.svg</file>
|
||||
<file alias="block.svg">../resources/InstrumentValueIcons/block.svg</file>
|
||||
<file alias="indent-increase.svg">../resources/InstrumentValueIcons/indent-increase.svg</file>
|
||||
<file alias="arrow-base-up.svg">../resources/InstrumentValueIcons/arrow-base-up.svg</file>
|
||||
<file alias="minus-solid.svg">../resources/InstrumentValueIcons/minus-solid.svg</file>
|
||||
<file alias="folder-outline-add.svg">../resources/InstrumentValueIcons/folder-outline-add.svg</file>
|
||||
<file alias="location.svg">../resources/InstrumentValueIcons/location.svg</file>
|
||||
<file alias="mic.svg">../resources/InstrumentValueIcons/mic.svg</file>
|
||||
<file alias="education.svg">../resources/InstrumentValueIcons/education.svg</file>
|
||||
<file alias="announcement.svg">../resources/InstrumentValueIcons/announcement.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
After Width: | Height: | Size: 170 KiB |
|
@ -0,0 +1,27 @@
|
|||
# QGroundControl Ground Control Station
|
||||
|
||||
## Custom Build Example
|
||||
|
||||
To build this sample custom version:
|
||||
|
||||
* Clean you build directory of any previous build
|
||||
* Rename the directory from `custom-example` to `custom`
|
||||
* Change to the `custom` directory
|
||||
* Run `python updateqrc.py`
|
||||
* Build QGC
|
||||
|
||||
![Custom Build Screenshot](README.jpg)
|
||||
|
||||
More details on what a custom build is and how to create your own can be found in the [QGC Dev Guide](https://dev.qgroundcontrol.com/en/custom_build/custom_build.html).
|
||||
|
||||
The main features of this example:
|
||||
|
||||
* Assumes an "Off The Shelf" purchased commercial vehicle. This means most vehicle setup is hidden from the user since they should mostly never need to adjust those things. They would be set up correctly by the vehicle producing company prior to sale.
|
||||
* The above assumption cause the QGC UI to adjust and not show various things. Providing an even simpler experience to the user.
|
||||
* The full experience continues to be available in "Advanced Mode".
|
||||
* Brands the build with various custom images and custom color palette which matches corporate branding of the theoretical commercial company this build is for.
|
||||
* Customizes portions of the interface such as you can see in the above screenshot which shows a custom instrument widget replacing the standard QGC ui.
|
||||
* It also overrides various QGC Application settings to hide some settings the users shouldn't modify as well as adjusting defaults for others.
|
||||
* The source code is fully commented to explain what and why it is doing things.
|
||||
|
||||
> Important Note: This custom build is not automatically built each time regular QGC code changes. This can mean that it may fall out of date with the latest changes in QGC code. This can show up as the `python updateqrc.py` steps failing due to upstream resource changes. Or possibly fail to compile because the plugin mechanism for custom builds has changed. If this happens please notify the QGC devs and they will bring it up to date. Or even better, submit a pull for the fix yourself!
|
After Width: | Height: | Size: 5.5 KiB |