//
// Simple example to demonstrate how takeoff and land using MAVSDK.
//
# include <mavsdk/mavsdk.h>
# include <mavsdk/plugins/action/action.h>
# include <mavsdk/plugins/telemetry/telemetry.h>
# include <mavsdk/plugins/mavlink_passthrough/mavlink_passthrough.h>
# include <cstdint>
# include <iostream>
# include <fstream>
# include <sstream>
# include <iomanip>
# include <future>
# include <thread>
# include <chrono>
# include <vector>
# include <mutex>
# include <map>
# include <memory>
# include <mysql_driver.h>
# include <mysql_connection.h>
# include <cppconn/resultset.h>
# include <cppconn/statement.h>
using namespace mavsdk ;
using std : : chrono : : seconds ;
using std : : chrono : : milliseconds ;
using std : : this_thread : : sleep_for ;
struct MavInitParameter {
std : : string connectPort ;
std : : string connectPort2 ;
float C ;
std : : string mysqlHost ;
std : : string mysqlPort ;
std : : string mysqlUser ;
std : : string mysqlPW ;
std : : string mysqlDatabase ;
} ;
enum class systemHandlerState {
Init , // 剛剛交給 systemHandler 去處理
Prologue , // 建立了 mysql row 並取得 serialNo
Ready // 收到第一筆並放到 telemetryInfo 後
} ;
struct systemHandlerInfo {
int systemID ;
std : : thread systemThread ;
systemHandlerState handlerState ;
int mysqlSN ;
} ;
// 我無法解決跨執行緒的 promise 變數生命週期問題
// 用最智障的全域變數 (砍掉 promise 結構簡單好多)
std : : map < int , std : : map < std : : string , std : : string > > gTelemetryInfo ;
std : : mutex gTeleMtx ;
bool reset ;
int initializeParameters ( const std : : string & filePath , MavInitParameter & initSetting ) {
std : : ifstream configFile ( filePath ) ;
if ( ! configFile ) {
return 1 ;
}
std : : vector < std : : string > parameters ; // Use a vector to store the parameters
std : : string line ;
while ( std : : getline ( configFile , line ) ) {
if ( line = = " EOC " ) {
break ; // Stop reading when "EOC" is encountered
}
// Split the line into key and value
std : : size_t delimiterPos = line . find ( ' = ' ) ;
if ( delimiterPos ! = std : : string : : npos ) {
std : : string key = line . substr ( 0 , delimiterPos ) ;
std : : string value = line . substr ( delimiterPos + 1 ) ;
// Check if the key matches any field in MavInitParameter
if ( key = = " connectPort " ) {
initSetting . connectPort = value ;
} else if ( key = = " connectPort2 " ) {
initSetting . connectPort2 = value ;
} else if ( key = = " mysqlHost " ) {
initSetting . mysqlHost = value ;
} else if ( key = = " mysqlPort " ) {
initSetting . mysqlPort = value ;
} else if ( key = = " mysqlUser " ) {
initSetting . mysqlUser = value ;
} else if ( key = = " mysqlPW " ) {
initSetting . mysqlPW = value ;
} else if ( key = = " mysqlDatabase " ) {
initSetting . mysqlDatabase = value ;
} else if ( key = = " C " ) {
initSetting . C = std : : stof ( value ) ;
}
}
parameters . push_back ( line ) ; // Add the parameter to the vector
}
configFile . close ( ) ;
std : : cout < < " Parameters read successfully! " < < std : : endl ;
// Print all the parameters
for ( const auto & parameter : parameters ) {
std : : cout < < " Line: " < < parameter < < std : : endl ;
}
return 0 ;
}
/*
* Important multi - threaded functions ,
* handling system initialization , receiving messages and sending messages .
*/
void systemHandler ( System & system ) {
// Initialization
auto telemetry = Telemetry { system } ;
auto action = Action { system } ;
std : : cout < < " System " < < static_cast < int > ( system . get_system_id ( ) ) < < " get in Thread. (debug) " < < std : : endl ; // debug
// Store telemetry information
std : : map < std : : string , std : : string > telemetryInfo ;
// Put all the subscriber init in this section
telemetry . subscribe_position ( [ & system , & telemetryInfo ] ( Telemetry : : Position position ) {
// 轉換數字到字串用的暫存變數
std : : ostringstream num_str_ss ;
// std::cout << "System ID: " << static_cast<int>(system.get_system_id()) << " Altitude: " << position.relative_altitude_m << " m\n"; // debug
// Store telemetry information
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < position . relative_altitude_m ;
telemetryInfo [ " drone_alt " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
num_str_ss < < std : : fixed < < std : : setprecision ( 8 ) < < position . latitude_deg ;
telemetryInfo [ " vehicle_lat " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
num_str_ss < < std : : fixed < < std : : setprecision ( 8 ) < < position . longitude_deg ;
telemetryInfo [ " vehicle_lon " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
} ) ;
telemetry . subscribe_raw_gps ( [ & system , & telemetryInfo ] ( Telemetry : : RawGps rawGps ) {
// 轉換數字到字串用的暫存變數
std : : ostringstream num_str_ss ;
// Store telemetry information
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < rawGps . velocity_m_s ;
telemetryInfo [ " vehicle_speed " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
} ) ;
telemetry . subscribe_attitude_euler ( [ & system , & telemetryInfo ] ( Telemetry : : EulerAngle eulerAngle ) {
// 轉換數字到字串用的暫存變數
std : : ostringstream num_str_ss ;
// Store telemetry information
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < eulerAngle . roll_deg ;
telemetryInfo [ " drone_roll " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < eulerAngle . pitch_deg ;
telemetryInfo [ " drone_pitch " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < eulerAngle . yaw_deg ;
telemetryInfo [ " drone_yaw " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
} ) ;
telemetry . subscribe_heading ( [ & system , & telemetryInfo ] ( Telemetry : : Heading heading ) {
// 轉換數字到字串用的暫存變數
std : : ostringstream num_str_ss ;
// Store telemetry information
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < heading . heading_deg ;
telemetryInfo [ " vehicle_head " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
} ) ;
telemetry . subscribe_battery ( [ & system , & telemetryInfo ] ( Telemetry : : Battery battery ) {
// 轉換數字到字串用的暫存變數
std : : ostringstream num_str_ss ;
// Store telemetry information
num_str_ss < < std : : fixed < < std : : setprecision ( 4 ) < < battery . remaining_percent ;
telemetryInfo [ " vehicle_bat " ] = num_str_ss . str ( ) ;
num_str_ss . str ( " " ) ;
} ) ;
telemetry . subscribe_in_air ( [ & system , & telemetryInfo ] ( bool in_air ) {
if ( in_air ) {
telemetryInfo [ " drone_air " ] = " 1 " ;
} else {
telemetryInfo [ " drone_air " ] = " 0 " ;
}
} ) ;
telemetry . subscribe_armed ( [ & system , & telemetryInfo ] ( bool is_armed ) {
if ( is_armed ) {
telemetryInfo [ " drone_arm " ] = " 1 " ;
} else {
telemetryInfo [ " drone_arm " ] = " 0 " ;
}
} ) ;
// Wait Until telemetryInfo get something.
while ( telemetryInfo . empty ( ) ) {
sleep_for ( seconds ( 1 ) ) ;
}
// Loop
while ( system . is_connected ( ) & & ! reset ) {
// Send Telemetry Data
gTeleMtx . lock ( ) ;
gTelemetryInfo [ static_cast < int > ( system . get_system_id ( ) ) ] = telemetryInfo ;
gTeleMtx . unlock ( ) ;
// Deal Command
// for purpose
sleep_for ( milliseconds ( 100 ) ) ;
}
// Destroy
// Thread Terminate
std : : cout < < " Thread Out " < < static_cast < int > ( system . get_system_id ( ) ) < < std : : endl ; //debug
}
int main ( int argc , char * * argv )
{
reset = false ;
if ( argc ! = 2 ) {
std : : cout < < " Config File Needed! ex. config.txt " < < std : : endl ;
return 1 ;
}
// Program Initial Read Setting from config file
struct MavInitParameter initSetting ;
auto i = initializeParameters ( argv [ 1 ] , initSetting ) ;
// auto i = initializeParameters("config.txt", initSetting);
if ( i = = 1 ) {
std : : cerr < < " Failed to open config file: " < < argv [ 1 ] < < std : : endl ;
}
// Start MAVSDK Server and connect to it.
Mavsdk mavsdk ;
ConnectionResult connection_result = mavsdk . add_any_connection ( initSetting . connectPort ) ;
if ( connection_result ! = ConnectionResult : : Success ) {
std : : cerr < < " Connection failed: " < < connection_result < < ' \n ' ;
return 1 ;
}
// For store Discover systems in a list
std : : vector < systemHandlerInfo > systemHandlerInfos ;
// Subscribe to new system discovery
mavsdk . subscribe_on_new_system ( [ & mavsdk , & systemHandlerInfos ] ( ) {
// Get the last subscribed system
auto comingSystems = mavsdk . systems ( ) ;
std : : shared_ptr < System > sys = comingSystems . back ( ) ;
// Avoid Duplicate System ID (Component ID is careless, maybe determind by UUID will be better?)
for ( int i = 0 ; i < systemHandlerInfos . size ( ) ; i + + ) {
if ( sys - > get_system_id ( ) = = systemHandlerInfos [ i ] . systemID ) {
comingSystems . pop_back ( ) ;
if ( comingSystems . size ( ) ! = 0 ) {
sys = comingSystems . back ( ) ;
} else {
std : : cout < < " Duplicate System ID Collision : " < < static_cast < int > ( sys - > get_system_id ( ) ) < < std : : endl ;
return ;
}
}
}
// make std::shared_ptr<MAVSDK::System> back to MAVSDK::System
System & new_system = * sys ;
// std::cout << "system detect : " << static_cast<int>(new_system.get_system_id()) << "(Debug)"<< std::endl; // debug
// Let handler progrem deal with System
std : : thread systemHandleThread ( [ & new_system ] ( ) { systemHandler ( new_system ) ; } ) ;
// Need some extra time fpr complete init
sleep_for ( milliseconds ( 500 ) ) ;
systemHandlerInfos . push_back ( systemHandlerInfo {
static_cast < int > ( new_system . get_system_id ( ) ) ,
std : : move ( systemHandleThread ) ,
systemHandlerState : : Init ,
- 1
} ) ;
} ) ;
// MySQL connection is here
sql : : mysql : : MySQL_Driver * driver ;
sql : : Connection * con ;
sql : : Statement * stmt ;
sql : : ResultSet * res ;
try {
driver = sql : : mysql : : get_mysql_driver_instance ( ) ;
con = driver - > connect ( " tcp:// " + initSetting . mysqlHost + " : " + initSetting . mysqlPort , initSetting . mysqlUser , initSetting . mysqlPW ) ;
con - > setSchema ( initSetting . mysqlDatabase ) ;
stmt = con - > createStatement ( ) ;
} catch ( sql : : SQLException & e ) {
std : : cout < < " MySQL Connection Error: " < < e . what ( ) < < std : : endl ;
return 1 ;
}
// Main Loop is here
// -- 將這個服務目前的狀態 更新到 mysql 中
// -- 從 systemHandlerInfos 中 輪巡所有 system 檢查並處理是否有需要丟到 mysql 的資料
// -- 從 mysql 抓到指令 丟到 systemHandlerInfo
int randomValue ;
std : : string sqlString ;
std : : map < std : : string , std : : string > aTelemetryInfo ;
while ( ! reset ) {
sleep_for ( seconds ( 1 ) ) ; // debug
// 每個 System 輪詢過去
for ( auto & handlerInfo : systemHandlerInfos ) {
switch ( handlerInfo . handlerState ) {
case systemHandlerState : : Init :
std : : cout < < " SystemHandler: " < < std : : to_string ( handlerInfo . systemID ) < < " at Init " < < std : : endl ; //debug
// 用系統時間生成的隨機數 標記目前欄位
std : : srand ( std : : time ( 0 ) ) ;
randomValue = std : : rand ( ) % 10000 ;
sqlString = " INSERT INTO NodeRed_one (system_id, connect_state) VALUES ( " + std : : to_string ( handlerInfo . systemID ) + " , " + std : : to_string ( randomValue ) + " ); " ;
// std::cout << "MySQL Insert Query: " << sqlString << std::endl; //debug
stmt - > execute ( sqlString ) ;
// 取得對應的欄位序號 SerialNO
sqlString = " SELECT SerialNO FROM NodeRed_one WHERE system_id = " + std : : to_string ( handlerInfo . systemID ) + " AND connect_state = " + std : : to_string ( randomValue ) + " ; " ;
// std::cout << "MySQL SELECT Query: " << sqlString << std::endl; //debug
res = stmt - > executeQuery ( sqlString ) ;
if ( res - > next ( ) ) {
// std::cout << "Query Reselt: " << res->getString(1) << std::endl; //debug
handlerInfo . mysqlSN = std : : stof ( res - > getString ( 1 ) ) ;
}
// 切換到下一個狀態
handlerInfo . handlerState = systemHandlerState : : Prologue ;
break ;
case systemHandlerState : : Prologue :
std : : cout < < " SystemHandler: " < < std : : to_string ( handlerInfo . systemID ) < < " at Prologue " < < std : : endl ; //debug
// 確認之前的 handlerInfo.mysqlSN 有正常被取得 若沒有整個程式即將關閉
if ( handlerInfo . mysqlSN = = - 1 ) {
reset = true ;
std : : cout < < " handlerInfo.mysqlSN not aquired. Something goes wrong. Shutdown all service. " < < std : : endl ;
}
// MAVSDK 還沒收到第一筆 telemetry 就持續在這個狀態
aTelemetryInfo = gTelemetryInfo [ handlerInfo . systemID ] ;
if ( aTelemetryInfo . size ( ) = = 0 ) {
break ;
}
// 切換到下一個狀態
handlerInfo . handlerState = systemHandlerState : : Ready ;
case systemHandlerState : : Ready :
std : : cout < < " SystemHandler: " < < std : : to_string ( handlerInfo . systemID ) < < " at Ready " < < std : : endl ; //debug
// 把 MAVSDK 從無人機接到的 telemetry 放到 mysql
aTelemetryInfo = gTelemetryInfo [ handlerInfo . systemID ] ;
if ( aTelemetryInfo . size ( ) ! = 0 ) {
sqlString = " UPDATE NodeRed_one SET " ;
for ( const auto & pair : aTelemetryInfo ) {
// std::cout << "Key: " << pair.first << ", Value: " << pair.second << std::endl; //debug
sqlString + = pair . first + " = ' " + pair . second + " ', " ;
}
sqlString . pop_back ( ) ;
sqlString + = " WHERE SerialNO = " + std : : to_string ( handlerInfo . mysqlSN ) + " ; " ;
// std::cout << "MySQL UPDATE Query: " << sqlString << std::endl; //debug
stmt - > execute ( sqlString ) ;
gTelemetryInfo [ handlerInfo . systemID ] = { } ;
}
// TODO: 把 mysql 的指令放給 MAVSDK 準備傳到無人機
// reset = true; //debug
break ;
}
}
}
// // dev del S
// sleep_for(seconds(3));
// std::cout << "connect system number: " << gTelemetryInfo.size() << std::endl;
// for(int t=0;t<10;t++){
// for(int i = 0; i < gTelemetryInfo.size()-1 ; i++){
// auto& info = gTelemetryInfo[i];
// std::cout << "Sysid : " << i+1 << " ,Alt : " << info["drone_alt"] << " ,Lon : " << info["vehicle_lon"] << " ,Lat : " << info["vehicle_lat"] << std::endl;
// }
// sleep_for(seconds(1));
// }
// // dev del E
// When Progrem Terminate
reset = true ;
// make all thread join
for ( auto & handlerInfo : systemHandlerInfos ) {
handlerInfo . systemThread . join ( ) ;
}
// MySQL Indicator
delete res ;
delete stmt ;
delete con ;
return 0 ;
}
// ===================================================
// 1.
// std::cout << "Mark A" << std::endl;
// sleep_for(seconds(3));
// System& s0 = *mavsdk.systems()[0];
// System& s1 = *mavsdk.systems()[1];
// std::cout << static_cast<int>(s0.get_system_id()) << std::endl;
// auto t0 = Telemetry{s0};
// auto t1 = Telemetry{s1};
// t0.subscribe_position([](Telemetry::Position position) {
// std::cout << "System ID: " << "0" << " Altitude: " << position.relative_altitude_m << " m\n";
// });
// t1.subscribe_position([](Telemetry::Position position) {
// std::cout << "System ID: " << "1" << " Altitude: " << position.relative_altitude_m << " m\n";
// });