Maa WS接口回调实现

This commit is contained in:
guigui
2022-05-28 19:38:15 +08:00
parent 80fe9dfe58
commit 7638056261
9 changed files with 248 additions and 76 deletions

View File

@@ -1,4 +1,4 @@
val ktor_version = "2.0.1"
val ktor_version = "2.0.2"
val kotlin_version = "1.6.10"
val logback_version = "1.2.11"

View File

@@ -0,0 +1,19 @@
package com.iguigui.maaj.dto
/* Global Info */
val INTERNAL_ERROR = 0 // 内部错误
val INIT_FAILED = 1 // 初始化失败
val CONNECTION_INFO = 2 // 连接相关信息
val ALL_TASKS_COMPLETED = 3 // 全部任务完成
/* TaskChain Info */
val TASK_CHAIN_ERROR = 10000 // 任务链执行/识别错误
val TASK_CHAIN_START = 10001 // 任务链开始
val TASK_CHAIN_COMPLETED = 10002 // 任务链完成
val TASK_CHAIN_EXTRA_INFO = 10003 // 任务链额外信息
/* SubTask Info */
val SUB_TASK_ERROR = 20000 // 原子任务执行/识别错误
val SUB_TASK_START = 20001 // 原子任务开始
val SUB_TASK_COMPLETED = 20002 // 原子任务完成
val SUB_TASK_EXTRA_INFO = 20003 // 原子任务额外信息

View File

@@ -6,7 +6,6 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonElement
sealed class WsRequest
@Serializable
data class ConnectRequest(
@@ -145,25 +144,33 @@ data class Copilot(
@Serializable
data class Start(
data class StartRequest(
@SerialName("id")
val id: String,
)
@Serializable
data class Stop(
data class StopRequest(
@SerialName("id")
val id: String,
)
@Serializable
data class DestroyRequest(
@SerialName("id")
val id: String,
)
@SerialName("connect")
@Serializable
data class ConnectWs(
data class WsRequest(
@SerialName("data")
val `data`: ConnectRequest,
val `data`: JsonElement,
@SerialName("command")
val command: String
) : WsRequest()
val command: String,
@SerialName("msgId")
val msgId: Int = 0
)
fun Task.toJsonString() = Json.encodeToString(this)

View File

@@ -22,12 +22,14 @@ data class HttpResponse(
data class WsResponse(
@SerialName("version")
val data: JsonElement,
@SerialName("type")
val type: String,
@SerialName("command")
val command: String,
@SerialName("msgId")
val msgId: Int = 0,
@SerialName("code")
val code: Int,
val code: Int = 0,
@SerialName("message")
val message: String
val message: String = "success"
)
@Serializable
@@ -102,6 +104,8 @@ data class StopResponse(
val result: Boolean
) : BaseData()
@Serializable
data class CallBackLog(
@SerialName("id")
@@ -122,7 +126,15 @@ fun BaseData.toJsonElement() =
Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" })
fun BaseData.wapperToResponse() =
fun BaseData.wapperToHttpResponse() =
HttpResponse(Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" }))
fun BaseData.wapperToWsResponse(command: String, msgId: Int) =
WsResponse(
Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" }),
command,
msgId
)
fun WsResponse.toJsonString() = Json.encodeToJsonElement(Json.encodeToJsonElement(this).jsonObject.filterNot { it.key == "type" }).toString()

View File

@@ -16,12 +16,12 @@ fun Application.httpRouting() {
route("/v1") {
get("/getVersion") {
val version = MaaService.getVersion()
call.respond(GetVersionResponse(version).wapperToResponse())
call.respond(GetVersionResponse(version).wapperToHttpResponse())
}
post("/connect") {
with(call.receive<ConnectRequest>()) {
val connect = MaaService.connect(adbPath, host, detailJson)
call.respond(connect.wapperToResponse())
call.respond(connect.wapperToHttpResponse())
}
}
post("/appendTask") {
@@ -31,7 +31,7 @@ fun Application.httpRouting() {
type,
params.jsonObject.toString()
)
call.respond(AppendTaskResponse(id, result).wapperToResponse())
call.respond(AppendTaskResponse(id, result).wapperToHttpResponse())
}
}
post("/setTaskParams") {
@@ -42,22 +42,27 @@ fun Application.httpRouting() {
taskId,
params.jsonObject.toString()
)
call.respond(SetTaskParamsResponse(id, result).wapperToResponse())
call.respond(SetTaskParamsResponse(id, result).wapperToHttpResponse())
}
}
post("/start") {
val start = call.receive<Start>()
val result = MaaService.start(start.id)
call.respond(StartResponse(start.id, result).wapperToResponse())
val startRequest = call.receive<StartRequest>()
val result = MaaService.start(startRequest.id)
call.respond(StartResponse(startRequest.id, result).wapperToHttpResponse())
}
post("/stop") {
val stop = call.receive<Stop>()
val result = MaaService.stop(stop.id)
call.respond(StartResponse(stop.id, result).wapperToResponse())
val stopRequest = call.receive<StopRequest>()
val result = MaaService.stop(stopRequest.id)
call.respond(StartResponse(stopRequest.id, result).wapperToHttpResponse())
}
post("/destroy") {
val destroy = call.receive<DestroyRequest>()
MaaService.destroy(destroy.id)
call.respond(EmptyBaseData.wapperToHttpResponse())
}
get("/listInstance") {
val list = MaaService.listInstance()
call.respond(ListInstanceResponse(list).wapperToResponse())
call.respond(ListInstanceResponse(list).wapperToHttpResponse())
}
}
}

View File

@@ -1,25 +1,95 @@
package com.iguigui.maaj.routing
import com.iguigui.maaj.dto.*
import io.ktor.serialization.kotlinx.json.*
import com.iguigui.maaj.service.Connection
import com.iguigui.maaj.service.MaaService
import com.iguigui.maaj.service.MaaService.addConnection
import com.iguigui.maaj.service.MaaService.appendTask
import com.iguigui.maaj.service.MaaService.removeConnection
import com.iguigui.maaj.util.Json
import io.ktor.websocket.*
import io.ktor.server.application.*
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.request.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.serialization.json.jsonObject
import java.util.*
import kotlin.collections.LinkedHashSet
fun Application.wsRouting() {
routing {
webSocket("/v1") {
send("You are connected!")
println("Adding user!")
val connection = Connection(this)
addConnection(connection)
for (frame in incoming) {
frame as? Frame.Text ?: continue
val receivedText = frame.readText()
println("接收到信息 : $receivedText")
send("You said: $receivedText")
try {
val receivedText = frame.readText()
val wsRequest = Json.decodeFromString(WsRequest.serializer(), receivedText)
var response: BaseData? = null
when (wsRequest.command) {
"connect" -> {
with(Json.decodeFromJsonElement(ConnectRequest.serializer(), wsRequest.data)) {
response = MaaService.connect(adbPath, host, detailJson)
}
}
"appendTask" -> {
with(Json.decodeFromJsonElement(AppendTaskRequest.serializer(), wsRequest.data)) {
val result = appendTask(
id,
type,
params.jsonObject.toString()
)
response = AppendTaskResponse(id, result)
}
}
"setTaskParams" -> {
with(Json.decodeFromJsonElement(SetTaskParamsRequest.serializer(), wsRequest.data)) {
val result = MaaService.setTaskParams(
id,
type,
taskId,
params.jsonObject.toString()
)
response = SetTaskParamsResponse(id, result)
}
}
"start" -> {
val startRequest =
Json.decodeFromJsonElement(StartRequest.serializer(), wsRequest.data)
val result = MaaService.start(startRequest.id)
response = StartResponse(startRequest.id, result)
}
"stop" -> {
val stopRequest =
Json.decodeFromJsonElement(StopRequest.serializer(), wsRequest.data)
val result = MaaService.stop(stopRequest.id)
response = StopResponse(stopRequest.id, result)
}
"destroy" -> {
val destroyRequest =
Json.decodeFromJsonElement(DestroyRequest.serializer(), wsRequest.data)
MaaService.stop(destroyRequest.id)
response = EmptyBaseData
}
"listInstance" -> {
val list = MaaService.listInstance()
response = ListInstanceResponse(list)
}
else -> {}
}
response?.let {
send(it.wapperToWsResponse(wsRequest.command, wsRequest.msgId).toJsonString())
}
} catch (e: Exception) {
e.printStackTrace()
println(e.message)
} finally {
println("Removing $connection!")
removeConnection(connection)
}
}
}
}

View File

@@ -0,0 +1,11 @@
package com.iguigui.maaj.service
import io.ktor.websocket.*
import java.util.concurrent.atomic.AtomicInteger
class Connection(val session: DefaultWebSocketSession) {
companion object {
var lastId = AtomicInteger(0)
}
val name = "user${lastId.getAndIncrement()}"
}

View File

@@ -1,56 +1,69 @@
package com.iguigui.maaj.service
import com.iguigui.maaj.dto.CallBackLog
import com.iguigui.maaj.dto.*
import com.iguigui.maaj.easySample.MeoAssistant
import com.iguigui.maaj.util.Json
import com.sun.jna.Pointer
import java.util.LinkedList
import java.util.Queue
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.atomic.AtomicInteger
import java.util.concurrent.atomic.AtomicLong
import kotlin.reflect.KFunction1
class MaaInstance(
private val instance: MeoAssistant,
val id: String,
val adbPath: String,
val host: String,
val detailJson: String
private val detailJson: String,
val callBackAction: KFunction1<CallBackLog, Unit>
) :
MeoAssistant.AsstApiCallback {
constructor(
instance: MeoAssistant,
id: String,
adbPath: String,
host: String,
pointer: Pointer,
detailJson: String
) : this(
instance,
id,
adbPath,
host,
detailJson
) {
this.pointer = pointer
}
// constructor(
// instance: MeoAssistant,
// id: String,
// adbPath: String,
// host: String,
// pointer: Pointer,
// detailJson: String
// ) : this(
// instance,
// id,
// adbPath,
// host,
// detailJson
// ) {
// this.pointer = pointer
// }
lateinit var pointer: Pointer
var uuid = ""
//状态码0初始化未运行 1运行中
var status = 0
var logQueue: Queue<CallBackLog> = LinkedBlockingQueue(1000)
var msgId = AtomicLong(0)
// private var logQueue: ArrayBlockingQueue<CallBackLog> = ArrayBlockingQueue(100)
//
private var msgId = AtomicLong(0)
override fun callback(msg: Int, detail_json: String, custom_arg: String) {
System.out.printf("回调msg : %s , 回调 detail_json : %s ,回调 custom_arg : %s \n", msg, detail_json, custom_arg)
logQueue.offer(CallBackLog(id, msgId.getAndIncrement(), msg, Json.parseToJsonElement(detail_json)))
val callBackLog = CallBackLog(id, msgId.getAndIncrement(), msg, Json.parseToJsonElement(detail_json))
callBackAction?.call(callBackLog)
when (msg) {
INTERNAL_ERROR,
INIT_FAILED,
ALL_TASKS_COMPLETED,
TASK_CHAIN_ERROR,
TASK_CHAIN_COMPLETED,
SUB_TASK_ERROR,
SUB_TASK_COMPLETED -> status = 0
//也不知道啥算正在运行,姑且认为任务链开始和原子任务开始肯定是在运行中吧
TASK_CHAIN_START,
SUB_TASK_START -> status = 1
else -> {}
}
}
fun connect(): Boolean = instance.AsstConnect(pointer, adbPath, host, detailJson)
@@ -59,8 +72,17 @@ class MaaInstance(
fun setTaskParams(type: String, taskId: Int, params: String) = instance.AsstSetTaskParams(pointer, taskId, params)
fun start() = instance.AsstStart(pointer)
fun start(): Boolean {
msgId.set(0)
return instance.AsstStart(pointer)
}
fun stop() = instance.AsstStop(pointer)
fun destroy() = instance.AsstDestroy(pointer)
//todo
private fun addLog(callBackLog: CallBackLog) {
}
}

View File

@@ -1,18 +1,29 @@
package com.iguigui.maaj.service
import com.iguigui.maaj.dto.CallBackLog
import com.iguigui.maaj.dto.ConnectResponse
import com.iguigui.maaj.dto.MaaInstanceInfo
import com.iguigui.maaj.dto.toJsonElement
import com.iguigui.maaj.easySample.MeoAssistant
import com.sun.jna.Native
import io.ktor.websocket.*
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.io.File
import java.security.MessageDigest
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import kotlin.collections.LinkedHashSet
object MaaService {
private var instancePool: ConcurrentHashMap<String, MaaInstance> = ConcurrentHashMap()
private val wsConnection = Collections.synchronizedSet<Connection?>(LinkedHashSet())
val meoAssistant: MeoAssistant by lazy {
val f = File(this.javaClass.getResource("")?.path ?: "")
var maaPath = f.path
@@ -26,31 +37,29 @@ object MaaService {
fun connect(adbPath: String, host: String, detailJson: String): ConnectResponse {
val id = sha1(host)
synchronized(id) {
if (instancePool.containsKey(id)) {
return ConnectResponse(id, true)
}
val maaInstance = MaaInstance(meoAssistant, host, adbPath, host, detailJson)
maaInstance.pointer = meoAssistant.AsstCreateEx(maaInstance, maaInstance.id)
val connect = maaInstance.connect()
if (!connect) {
return ConnectResponse("", false)
}
instancePool.putIfAbsent(id, maaInstance)
if (instancePool.containsKey(id)) {
return ConnectResponse(id, true)
}
val maaInstance = MaaInstance(meoAssistant, host, adbPath, host, detailJson, ::callBackLog)
maaInstance.pointer = meoAssistant.AsstCreateEx(maaInstance, maaInstance.id)
val connect = maaInstance.connect()
if (!connect) {
return ConnectResponse("", false)
}
instancePool.putIfAbsent(id, maaInstance)
return ConnectResponse(id, true)
}
fun appendTask(host: String, type: String, detailJson: String) =
instancePool[host]?.appendTask(type, detailJson) ?: 0
fun appendTask(id: String, type: String, detailJson: String) =
instancePool[id]?.appendTask(type, detailJson) ?: 0
fun setTaskParams(host: String, type: String, taskId: Int, detailJson: String) =
instancePool[host]?.setTaskParams(type, taskId, detailJson) ?: false
fun setTaskParams(id: String, type: String, taskId: Int, detailJson: String) =
instancePool[id]?.setTaskParams(type, taskId, detailJson) ?: false
fun start(host: String) = instancePool[host]?.start() ?: false
fun start(id: String) = instancePool[id]?.start() ?: false
fun stop(host: String) = instancePool[host]?.stop() ?: false
fun stop(id: String) = instancePool[id]?.stop() ?: false
fun getVersion(): String = meoAssistant.AsstGetVersion()
@@ -73,5 +82,22 @@ object MaaService {
fun listInstance(): List<MaaInstanceInfo> =
instancePool.values.map { e -> MaaInstanceInfo(e.id, e.host, e.adbPath, e.uuid, e.status) }.toList()
fun destroy(id: String) = instancePool[id]?.destroy()
@OptIn(DelicateCoroutinesApi::class)
fun callBackLog(message: CallBackLog) {
GlobalScope.launch(Dispatchers.IO) {
wsConnection.forEach { it.session.send(message.toJsonElement().toString()) }
}
}
fun addConnection(connection: Connection) {
this.wsConnection += connection
}
fun removeConnection(connection: Connection) {
this.wsConnection -= connection
}
}