█ 【无人机学习之QGroundControl】android端 源码
█ 系列文章目录
提示:这里是收集了无人机的相关文章
- 【无人机学习】无人机基础知识
- 【无人机学习】Mission Planner(pc端)和QGroundControl(android端)
- 【无人机学习之DroidPlanner】FlightActivity的启动过程
- 【无人机学习之DroidPlanner】msg_heartbeat心跳处理(含MAVLink协议)
- 【无人机学习之DroidPlanner】msg_sys_status系统状态
- 【无人机学习之QGroundControl】android端App初解1
- 【无人机学习之QGroundControl】android端App初解2-APMPowerComponent(含QML的介绍)
- 【无人机学习之QGroundControl】android端App初解3-ParameterEditorController
█ 文章目录
█ 读前说明
- 本文通过学习别人写demo,学习一些课件,参考一些博客,学习相关知识,如有涉及侵权请告知
- 本文可能只简单罗列了一些相关的代码实现过程,复制了一些大神的高论,如内容有误请自行辨别
- 涉及到的逻辑以及说明可能只做了简单的介绍,主要当做笔记,了解过程而已,如有不同看法,欢迎下方评论
- 本文源码:https://github.com/mavlink/qgroundcontrol
- 本文UI:https://github.com/mavlink/qgroundcontrol/blob/master/src/ui
- QGC中UI设计的主要模式是用QML编写的UI页面,多次与用C ++编写的定制“控制器”进行通信。类似MVC的设计模式。
提示:QGroundControl是使用QT & c++ 编写的
█ android端
1.android\AndroidManifest.xml
此文件在qgroundcontrol-master\android\AndroidManifest.xml
重点字段 |
---|
org.qtproject.qt5.android.bindings.QtApplication |
org.mavlink.qgroundcontrol.QGCActivity |
<?xml version="1.0"?>
<manifest package="org.mavlink.qgroundcontrol" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="3.0.0-243-gd759437" android:versionCode="300243" 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="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"/>
<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>
</activity>
</application>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29"/>
。。。。。。。。
</manifest>
2.android\AndroidManifest.xml.in
此文件在qgroundcontrol-master\android\AndroidManifest.xml.inl
重点字段 |
---|
org.qtproject.qt5.android.bindings.QtApplication |
org.qtproject.qt5.android.bindings.QtActivity |
<?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>
。。。。。。。。
</activity>
</application>
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="19"/>
</manifest>
3.QGCActivity.java
此文件在qgroundcontrol-master\android\src\org\mavlink\qgroundcontrol
重点字段 |
---|
public class QGCActivity extends org.qtproject.qt5.android.bindings.QtActivity |
public void onCreate(Bundle savedInstanceState) -> public native void nativeInit() |
界面初始化都是在QT中处理,包含界面
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 final String TAG = "QGC_QGCActivity";
private static final String ACTION_USB_PERMISSION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION";
public static Context m_context;
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)) {
// usb 权限处理
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
// usb 插入处理
}
}
};
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)) {
// usb 权限处理
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
// usb 插入处理
}
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);
}
}
█ C++端
1.main.cc
此文件在qgroundcontrol-master\src\main.cc
重点字段 |
---|
“nativeInit” -> gst_android_init |
public void onCreate(Bundle savedInstanceState) -> public native void nativeInit() |
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); |
QGCApplication* app = new QGCApplication(argc, argv, runUnitTests); |
app->_initCommon(); |
getQGCMapEngine()->init();//-- Initialize Cache System |
void setNativeMethods(void)
{
JNINativeMethod javaMethods[] {// reinterpret_cast运算符是用来处理无关类型之间的转换;它会产生一个新的值,这个值会有与原始参数(expression)有完全相同的比特位。
{"nativeInit", "()V", reinterpret_cast<void *>(gst_android_init)}
};
。。。。。。
}
//-----------------------------------------------------------------------------
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);//context为jni方法的参数,表示java层的类对象
_class_loader = env->NewGlobalRef (class_loader);
}
//-----------------------------------------------------------------------------
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();
#if defined(QGC_GST_STREAMING)
// Tell the androidmedia plugin about the Java VM
gst_amc_jni_set_java_vm(vm);
#endif
#if !defined(NO_SERIAL_LINK)
QSerialPort::setNativeMethods();
#endif
JoystickAndroid::setNativeMethods();
#if defined(QGC_ENABLE_PAIRING)
PairingManager::setNativeMethods();
#endif
return JNI_VERSION_1_6;
}
//-----------------------------------------------------------------------------
/**
* @brief Starts the application
* 启动应用程序
* @return exit code, 0 for normal exit and !=0 for error cases
* 0表示正常退出!=0表示错误情况
*/
int main(int argc, char *argv[])
{
//-- Record boot time
QGC::initTimer();
// install the message handler
AppMessages::installHandler();
// qRegisterMetaType 调试的警告为静音输出
qRegisterMetaType<QSerialPort::SerialPortError>();
qRegisterMetaType<QBluetoothSocket::SocketError>();
qRegisterMetaType<QBluetoothServiceInfo>();
qRegisterMetaType<QAbstractSocket::SocketError>();
qRegisterMetaType<QGCSerialPortInfo>();
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QGCApplication* app = new QGCApplication(argc, argv, runUnitTests);// 下一步需要查看的信息
Q_CHECK_PTR(app);
if(app->isErrorState()) {
app->exec();
return -1;
}
// 设置图标
QApplication::setWindowIcon(QIcon(":/res/resources/icons/qgroundcontrol.ico"));
app->_initCommon();// 下一步需要查看的信息
//-- Initialize Cache System
getQGCMapEngine()->init();// 下一步需要查看的信息
if (!app->_initForNormalAppBoot()) {
return -1;
}
。。。。。。
return exitCode;
}
2.QGCApplication:app->_initCommon()
QGCApplication.cpp:此文件在qgroundcontrol-master\src\QGCApplication.cc
重点字段 |
---|
_toolbox = new QGCToolbox(this); -> _toolbox->setChildToolboxes(); |
qmlRegisterUncreatableType (kQGCVehicle, 1, 0, “Vehicle”, kRefOnly); |
初始化,全局方法,语言设置
QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting)
: QApplication (argc, argv)
, _runningUnitTests (unitTesting)
{
_app = this;
_msecsElapsedTime.start();
。。。。。。
// Set application information
setApplicationName(applicationName);
setOrganizationName(QGC_ORG_NAME);
setOrganizationDomain(QGC_ORG_DOMAIN);
this->setApplicationVersion(QString(GIT_VERSION));
// Set settings format
QSettings::setDefaultFormat(QSettings::IniFormat);
QSettings settings;
qDebug() << "Settings location" << settings.fileName() << "Is writable?:" << settings.isWritable();
settings.setValue(_settingsVersionKey, QGC_SETTINGS_VERSION);
// We need to set language as early as possible prior to loading on JSON files.
setLanguage();
_toolbox = new QGCToolbox(this);
_toolbox->setChildToolboxes();
。。。。。。
_checkForNewVersion();
}
void QGCApplication::_initCommon()
{
static const char* kRefOnly = "Reference only";
static const char* kQGroundControl = "QGroundControl";
static const char* kQGCControllers = "QGroundControl.Controllers";
static const char* kQGCVehicle = "QGroundControl.Vehicle";
static const char* kQGCTemplates = "QGroundControl.Templates";
QSettings settings;
// Register our Qml objects
qmlRegisterType<QGCPalette> ("QGroundControl.Palette", 1, 0, "QGCPalette");
qmlRegisterType<QGCMapPalette> ("QGroundControl.Palette", 1, 0, "QGCMapPalette");
qmlRegisterUncreatableType<Vehicle> (kQGCVehicle, 1, 0, "Vehicle", kRefOnly);
qmlRegisterUncreatableType<MissionManager> (kQGCVehicle, 1, 0, "MissionManager", kRefOnly);
qmlRegisterUncreatableType<ParameterManager> (kQGCVehicle, 1, 0, "ParameterManager", kRefOnly);
。。。。。。
}
bool QGCApplication::_initForNormalAppBoot()
{
QSettings settings;
_qmlAppEngine = toolbox()->corePlugin()->createQmlApplicationEngine(this);
toolbox()->corePlugin()->createRootWindow(_qmlAppEngine);
// Image provider for PX4 Flow
QQuickImageProvider* pImgProvider = dynamic_cast<QQuickImageProvider*>(qgcApp()->toolbox()->imageProvider());
_qmlAppEngine->addImageProvider(QStringLiteral("QGCImages"), pImgProvider);
QQuickWindow* rootWindow = (QQuickWindow*)qgcApp()->mainRootWindow();
if (rootWindow) {
rootWindow->scheduleRenderJob (new FinishVideoInitialization (toolbox()->videoManager()),
QQuickWindow::BeforeSynchronizingStage);
}
// Safe to show popup error messages now that main window is created
UASMessageHandler* msgHandler = qgcApp()->toolbox()->uasMessageHandler();
if (msgHandler) {
msgHandler->showErrorsInToolbar();
}
// Now that main window is up check for lost log files
connect(this, &QGCApplication::checkForLostLogFiles, toolbox()->mavlinkProtocol(), &MAVLinkProtocol::checkForLostLogFiles);
emit checkForLostLogFiles();
// Load known link configurations
toolbox()->linkManager()->loadLinkConfigurationList();
// Probe for joysticks
toolbox()->joystickManager()->init();
if (_settingsUpgraded) {
showAppMessage(QString(tr("The format for %1 saved settings has been modified. "
"Your saved settings have been reset to defaults.")).arg(applicationName()));
}
// Connect links with flag AutoconnectLink
toolbox()->linkManager()->startAutoConnectedLinks();
if (getQGCMapEngine()->wasCacheReset()) {
showAppMessage(tr("The Offline Map Cache database has been upgraded. "
"Your old map cache sets have been reset."));
}
settings.sync();
return true;
}
3. QGCMapEngine.cpp:getQGCMapEngine()->init()
此文件在qgroundcontrol-master\src\QtLocationPlugin\QGCMapEngine.cpp
单例模式
//-----------------------------------------------------------------------------
// Singleton
static QGCMapEngine* kMapEngine = nullptr;
QGCMapEngine*
getQGCMapEngine()
{
if(!kMapEngine)
kMapEngine = new QGCMapEngine();
return kMapEngine;
}
//-----------------------------------------------------------------------------
QGCMapEngine::~QGCMapEngine()
{
_worker.quit();
_worker.wait();
delete _urlFactory;
_urlFactory = nullptr;
}
//-----------------------------------------------------------------------------
void
QGCMapEngine::init()
{
//-- Delete old style caches (if present)
_wipeOldCaches();
//-- Figure out cache path
QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QLatin1String("/QGCMapCache" CACHE_PATH_VERSION);
if(!QDir::root().mkpath(cacheDir)) {
qWarning() << "Could not create mapping disk cache directory: " << cacheDir;
cacheDir = QDir::homePath() + QStringLiteral("/.qgcmapscache/");
if(!QDir::root().mkpath(cacheDir)) {
qWarning() << "Could not create mapping disk cache directory: " << cacheDir;
cacheDir.clear();
}
}
_cachePath = cacheDir;
if(!_cachePath.isEmpty()) {
_cacheFile = kDbFileName;
_worker.setDatabaseFile(_cachePath + "/" + _cacheFile);
qDebug() << "Map Cache in:" << _cachePath << "/" << _cacheFile;
} else {
qCritical() << "Could not find suitable map cache directory.";
}
QGCMapTask* task = new QGCMapTask(QGCMapTask::taskInit);
_worker.enqueueTask(task);
}
4. QGCApplication:qmlRegisterUncreatableType()
QGCApplication.cpp:此文件在qgroundcontrol-master\src\QGCApplication.cc
qmlRegisterUncreatableType()将c++类(派生自QObject)注册到QML系统(注册为非实例化类型)。但是我们不能在qml文件(ui界面文件)中为类创建对象,我们需要在qml文件中使用属性枚举方式来调用。
█ 相关资料
提示:这里是参考的相关文章
- 2018-03-08 QGC 连接功能 底层执行逻辑_/* */-CSDN博客
- 2019-01-15 QT Qml 的qmlRegisterUncreatableType()函数_小马哔哔-CSDN博客
- 2018-03-04 QGroundControl 开发人员指南_/* */-CSDN博客_qgroundcontrol
- 2019-05-11 Qt学习笔记:多语言文件.qm的生成和使用_chase_hung的博客-CSDN博客:生成ts文件,修改ts文件,生成qm文件,加载qm语言包
█ 免责声明
博主分享的所有文章内容,部分参考网上教程,引用大神高论,部分亲身实践,记下笔录,内容可能存在诸多不实之处,还望海涵,本内容仅供学习研究使用,切勿用于商业用途,若您是部分内容的作者,不喜欢此内容被分享出来,可联系博主说明相关情况通知删除,感谢您的理解与支持! |
---|
提示:转载请注明出处:
https://blog.csdn.net/ljb568838953/article/details/112860105