之前的实例一,通过点击按钮实现起飞。而我这边最终的需求是通过点击网页上的按钮来控制无人机的起飞降落。

Web ----> Android ----> 遥控器 ----> 无人机

在之前的文章第一个Android程序提到了NanoHTTPD这个库,当时是用Java来写的,现在要换到Kotlin来写。

首先将MainActivity中的飞行控制逻辑剥离到一个服务中FlightService方便其他类调用

class FlightService : Service() {

    private val binder = FlightBinder()

    inner class FlightBinder : Binder() {
        fun getService(): FlightService = this@FlightService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }

    private fun getKeyManager(): KeyManager {
        return KeyManager.getInstance()
    }

    fun takeoff() {
        getKeyManager().performAction(KeyTools.createKey(FlightControllerKey.KeyStartTakeoff),
            object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg> {
                override fun onSuccess(msg: EmptyMsg) {
                    showToast("Takeoff successful $msg")
                }

                override fun onFailure(p0: IDJIError) {
                    showToast("Takeoff failed $p0")
                }
            })
    }

    fun landing() {
        getKeyManager().performAction(KeyTools.createKey(FlightControllerKey.KeyStartAutoLanding),
            object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg> {
                override fun onSuccess(msg: EmptyMsg) {
                    showToast("Landing successful $msg")
                }

                override fun onFailure(p0: IDJIError) {
                    showToast("Landing failed $p0")
                }
            })
    }

    fun goHome() {
        getKeyManager().performAction(
            KeyTools.createKey(FlightControllerKey.KeyStartGoHome),
            object : CommonCallbacks.CompletionCallbackWithParam<EmptyMsg> {
                override fun onSuccess(msg: EmptyMsg) {
                    showToast("Go Home successful $msg")
                }

                override fun onFailure(p0: IDJIError) {
                    showToast("Go Home failed $p0")
                }
            })
    }

    private fun showToast(message: String) {
        Toast.makeText(applicationContext, message, Toast.LENGTH_LONG).show()
    }
}

添加依赖

dependencies {
    ...    
    implementation 'org.nanohttpd:nanohttpd:2.3.1'
    ...
}

创建一个FlightProcessing接口

interface FlightProcessing {
    fun takeOff()

    fun landing()

    fun goHome()
}

创建一个FlightProcessing实现类Mavic3FlightProcessing

class Mavic3FlightProcessing(private val context: Context): FlightProcessing {

    private var flightService: FlightService? = null
    private var isBound = false

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val binder = service as FlightService.FlightBinder
            flightService = binder.getService()
            isBound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            isBound = false
        }
    }

    init {
        val intent = Intent(context, FlightService::class.java)
        context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
    }

    override fun takeOff() {
        flightService?.takeoff()
    }

    override fun landing() {
        flightService?.landing()
    }

    override fun goHome() {
        flightService?.goHome()
    }
}

Mavic3FlightProcessing里调用FlightService来执行控制逻辑。

创建NanoHTTPD的实例App

class App(private val flightProcessing: FlightProcessing): NanoHTTPD(10010) {
    init {
        start(SOCKET_READ_TIMEOUT, false)
    }

    override fun serve(session: IHTTPSession?): Response {

        if (session?.method == Method.GET) {
            // TODO
        }

        if(session?.method == Method.POST){
            val contentType = session.headers["content-type"]?.split(";")?.get(0)
            if(contentType == "application/x-www-form-urlencoded") {
                try {
                    session.parseBody(HashMap())
                    val commandName = session.parameters["commandName"]?.firstOrNull() ?: ""

                    if (commandName.isNotEmpty()) {
                        when (commandName) {
                            "[takeOff]" -> {
                                flightProcessing.takeOff()
                                return newFixedLengthResponse("Succeeded")
                            }
                            "[landing]" -> {
                                flightProcessing.landing()
                                return newFixedLengthResponse("Succeeded")
                            }
                            "[goHome]" -> {
                                flightProcessing.goHome()
                                return newFixedLengthResponse("Succeeded")
                            }
                        }
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    return newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Internal Server Error")
                }
            }
        }
        return newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "Not Found")
    }
}

修改FlightActivity来启动App实例

class FlightActivity : AppCompatActivity(), View.OnClickListener {
    // Other
    
    private lateinit var flightProcessing: FlightProcessing
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_flight)

        flightProcessing = Mavic3FlightProcessing(this)
        
        try {
            App(flightProcessing)
            Toast.makeText(this, "NanoHTTP started", Toast.LENGTH_SHORT).show()
        } catch (e: IOException) {
            e.printStackTrace()
        }
        
        initUI()
        initListener()
    
    }
    
    override fun onDestroy() {
        super.onDestroy()
    }
    
    private fun initUI() {
        // Other    
    }
    
    private fun initListener() {
        // Other    
    }
    
    override fun onClick(v: View) {
        // Other    
    }
    
    // Other
}

打包运行程序,启动App实例,Web Server 会在 10010 端口运行。

测试

创建一个Python脚本来测试

import requests

url = 'http://target_ip:10010/'
data = {'commandName': '[takeOff]'}  # Replace with '[landing]' or '[goHome]' as needed

response = requests.post(url, data=data)
print(response.text)

基本实现功能。

标签: none

评论已关闭