|
|
@@ -1,8 +1,10 @@
|
|
|
package com.iscs.bozzys.ui.pages.detail.task
|
|
|
|
|
|
+import android.app.Application
|
|
|
import android.content.Context
|
|
|
import android.content.Intent
|
|
|
import android.os.Build
|
|
|
+import androidx.compose.foundation.Image
|
|
|
import androidx.compose.foundation.background
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
@@ -10,15 +12,16 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
|
|
import androidx.compose.foundation.layout.FlowRow
|
|
|
import androidx.compose.foundation.layout.PaddingValues
|
|
|
import androidx.compose.foundation.layout.Row
|
|
|
+import androidx.compose.foundation.layout.Spacer
|
|
|
+import androidx.compose.foundation.layout.fillMaxHeight
|
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
|
import androidx.compose.foundation.layout.fillMaxWidth
|
|
|
import androidx.compose.foundation.layout.height
|
|
|
import androidx.compose.foundation.layout.offset
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
import androidx.compose.foundation.layout.size
|
|
|
-import androidx.compose.foundation.rememberScrollState
|
|
|
+import androidx.compose.foundation.layout.width
|
|
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
|
|
-import androidx.compose.foundation.verticalScroll
|
|
|
import androidx.compose.material3.Button
|
|
|
import androidx.compose.material3.ButtonDefaults
|
|
|
import androidx.compose.material3.Icon
|
|
|
@@ -31,20 +34,43 @@ import androidx.compose.runtime.getValue
|
|
|
import androidx.compose.ui.Alignment
|
|
|
import androidx.compose.ui.Modifier
|
|
|
import androidx.compose.ui.draw.clip
|
|
|
+import androidx.compose.ui.draw.scale
|
|
|
import androidx.compose.ui.graphics.Color
|
|
|
import androidx.compose.ui.res.painterResource
|
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
|
import androidx.compose.ui.unit.dp
|
|
|
import androidx.compose.ui.unit.sp
|
|
|
+import androidx.lifecycle.lifecycleScope
|
|
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
|
import com.iscs.bozzys.R
|
|
|
import com.iscs.bozzys.api.Task
|
|
|
+import com.iscs.bozzys.api.Ticket
|
|
|
import com.iscs.bozzys.ui.common.PageBase
|
|
|
import com.iscs.bozzys.ui.common.Title
|
|
|
import com.iscs.bozzys.ui.pages.compose.CardBox
|
|
|
import com.iscs.bozzys.ui.pages.compose.FormBox
|
|
|
import com.iscs.bozzys.ui.pages.vm.VMDetailTask
|
|
|
import com.iscs.bozzys.ui.theme.Text
|
|
|
+import com.iscs.bozzys.utils.LogUtil
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEGetPowerCMD
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEGetStatusCMD
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEGetTicketInfoCMD
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLEPowerOffCMD
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLESwitchRunModeCMD
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.buildBLETicketDataCMDList
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.getPower
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.getRunMode
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.getSendTicketResult
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.getSwitchRunModeResult
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.getTicketPackageInfo
|
|
|
+import com.iscs.bozzys.utils.ble.BleFrameExt.getToken
|
|
|
+import com.iscs.bozzys.utils.ble.BleManager
|
|
|
+import com.iscs.bozzys.utils.ble.BleRunMode
|
|
|
+import com.iscs.bozzys.utils.ble.BleTicketDataPackage
|
|
|
+import com.iscs.bozzys.utils.byteArrayToHexString
|
|
|
+import kotlinx.coroutines.launch
|
|
|
+import kotlinx.serialization.json.Json
|
|
|
|
|
|
/**
|
|
|
* 打开任务详情页面
|
|
|
@@ -63,6 +89,90 @@ class PageDetailTask : PageBase() {
|
|
|
// 页面携带数据对象
|
|
|
private lateinit var task: Task
|
|
|
|
|
|
+ /**
|
|
|
+ * 发送作业票到设备
|
|
|
+ */
|
|
|
+ private suspend fun sendTicket2Key(application: Application, vm: VMDetailTask, mac: String, step: Int) {
|
|
|
+ vm.updateTicketStatus(status = "正在搜索钥匙...")
|
|
|
+ // 测试蓝牙扫描
|
|
|
+ val bm = BleManager(application, mac = mac)
|
|
|
+ val result = bm.connect()
|
|
|
+ if (result.connected) {
|
|
|
+ if (step == 0 || step == 2) vm.updateTicketStatus(status = "正在下发任务...")
|
|
|
+ if (step == 1 || step == 3) vm.updateTicketStatus(status = "正在读取任务...")
|
|
|
+ // 获取设备token
|
|
|
+ val token = bm.writeByResponse(BleFrameExt.buildBLEGetTokenCMD()).getToken()
|
|
|
+ LogUtil.i("xiaoming $mac", "获取设备token ${token.byteArrayToHexString()}")
|
|
|
+ // 获取设备电量
|
|
|
+ val power = bm.writeByResponse(token.buildBLEGetPowerCMD()).getPower()
|
|
|
+ LogUtil.i("xiaoming $mac", "当前设备电量:$power")
|
|
|
+ if (power < 0) {
|
|
|
+ vm.updateTicketStatus(status = "连接失败,请稍后重试")
|
|
|
+ vm.updateTicketStatus(status = "", delay = 2000)
|
|
|
+ bm.disconnect()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 获取当前设备运行模式
|
|
|
+ val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
|
|
|
+ LogUtil.i("xiaoming $mac", "当前工作模式:$runMode")
|
|
|
+ if (step == 0 || step == 2) {
|
|
|
+ // 下发作业票
|
|
|
+ val tickets = token.buildBLETicketDataCMDList(vm.createTicketJson())
|
|
|
+ var ticketSendOk = true
|
|
|
+ tickets.forEach {
|
|
|
+ val ticket = bm.writeByResponse(it).getSendTicketResult()
|
|
|
+ if (ticket != 0) {
|
|
|
+ ticketSendOk = false
|
|
|
+ return@forEach
|
|
|
+ }
|
|
|
+ }
|
|
|
+ LogUtil.i("xiaoming $mac", "下发作业票:$ticketSendOk")
|
|
|
+ // 作业票下发成功,修改设备运行模式为工作模式
|
|
|
+ val switch = bm.writeByResponse(token.buildBLESwitchRunModeCMD(BleRunMode.WORK))
|
|
|
+ .getSwitchRunModeResult()
|
|
|
+ bm.writeByResponse(token.buildBLEPowerOffCMD())
|
|
|
+ LogUtil.i("xiaoming $mac", "切换工作模式:$switch")
|
|
|
+ if (step == 0) {
|
|
|
+ vm.updateTicketStatus(1, "任务下发成功")
|
|
|
+ vm.updateTicketStatus(1, "", 2000)
|
|
|
+ } else {
|
|
|
+ vm.updateTicketStatus(3, "任务下发成功")
|
|
|
+ vm.updateTicketStatus(3, "", 2000)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 读取作业票信息
|
|
|
+ val pkgList = ArrayList<BleTicketDataPackage>()
|
|
|
+ val ticketInfo = bm.writeByResponse(token.buildBLEGetTicketInfoCMD()).getTicketPackageInfo()
|
|
|
+ pkgList.add(ticketInfo)
|
|
|
+ LogUtil.i("xiaoming $mac", "读取作业票:首包信息:$ticketInfo")
|
|
|
+ // 校验是否有子包,如果有,继续读取子包数据
|
|
|
+ for (idx in 1 until ticketInfo.pkgTotal) {
|
|
|
+ val ticketSubPackageInfo =
|
|
|
+ bm.writeByResponse(token.buildBLEGetTicketInfoCMD(idx, ticketInfo.pkgTotal))
|
|
|
+ .getTicketPackageInfo()
|
|
|
+ pkgList.add(ticketSubPackageInfo)
|
|
|
+ LogUtil.i("xiaoming $mac", "读取作业票:子包信息:$ticketSubPackageInfo")
|
|
|
+ }
|
|
|
+ var datas = byteArrayOf()
|
|
|
+ pkgList.forEach { datas += it.pkgData }
|
|
|
+ val ticketJson = String(datas)
|
|
|
+ LogUtil.i("xiaoming $mac", "读取作业票:${ticketJson}")
|
|
|
+ bm.writeByResponse(token.buildBLESwitchRunModeCMD(BleRunMode.STBY)).getSwitchRunModeResult()
|
|
|
+ bm.writeByResponse(token.buildBLEPowerOffCMD())
|
|
|
+ val json = Json { ignoreUnknownKeys = true }
|
|
|
+ val ticket = json.decodeFromString<Ticket>(ticketJson)
|
|
|
+ vm.updateTicketStatus(status = "任务读取完成")
|
|
|
+ vm.updateTicketStatus(status = "", delay = 2000)
|
|
|
+ vm.updateTicket(ticket)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 进行重连,这里可以封装尝试次数
|
|
|
+ sendTicket2Key(application, vm, mac, step)
|
|
|
+ }
|
|
|
+ bm.disconnect()
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
/**
|
|
|
* 获取页面携带的数据
|
|
|
*/
|
|
|
@@ -91,7 +201,7 @@ class PageDetailTask : PageBase() {
|
|
|
Column(
|
|
|
modifier = Modifier
|
|
|
.weight(1f)
|
|
|
- .verticalScroll(state = rememberScrollState())
|
|
|
+ // .verticalScroll(state = rememberScrollState())
|
|
|
.padding(16.dp)
|
|
|
) {
|
|
|
TaskInfo(task)
|
|
|
@@ -104,6 +214,32 @@ class PageDetailTask : PageBase() {
|
|
|
}
|
|
|
// 底部操作功能封装
|
|
|
TaskOptions(pv, vm)
|
|
|
+ if (state.step < 4) Button(
|
|
|
+ {
|
|
|
+ lifecycleScope.launch {
|
|
|
+ sendTicket2Key(
|
|
|
+ this@PageDetailTask.application,
|
|
|
+ vm,
|
|
|
+ "CC:BA:97:21:72:0A",
|
|
|
+ state.step
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }, Modifier
|
|
|
+ .padding(bottom = 16.dp)
|
|
|
+ .fillMaxWidth(0.8f)
|
|
|
+ .height(40.dp)
|
|
|
+ .align(Alignment.CenterHorizontally)
|
|
|
+ ) {
|
|
|
+ Text(
|
|
|
+ when (state.step) {
|
|
|
+ 1 -> "完成上锁"
|
|
|
+ 2 -> "去解锁"
|
|
|
+ 3 -> "完成解锁"
|
|
|
+ else -> "去上锁"
|
|
|
+ }, fontSize = 18.sp, fontWeight = FontWeight.Bold
|
|
|
+ )
|
|
|
+
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -292,6 +428,7 @@ class PageDetailTask : PageBase() {
|
|
|
*/
|
|
|
@Composable
|
|
|
fun TaskDevice(vm: VMDetailTask) {
|
|
|
+ val state by vm.state.collectAsState()
|
|
|
CardBox(
|
|
|
Modifier
|
|
|
.padding(top = 10.dp)
|
|
|
@@ -300,17 +437,191 @@ class PageDetailTask : PageBase() {
|
|
|
Column(
|
|
|
Modifier
|
|
|
.fillMaxWidth()
|
|
|
- .height(120.dp),
|
|
|
- horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
- verticalArrangement = Arrangement.Center
|
|
|
+ .padding(10.dp),
|
|
|
+ horizontalAlignment = Alignment.Start,
|
|
|
) {
|
|
|
- Icon(
|
|
|
- painter = painterResource(R.drawable.job_warning),
|
|
|
- contentDescription = null,
|
|
|
- modifier = Modifier.size(60.dp),
|
|
|
- tint = MaterialTheme.colorScheme.primary
|
|
|
- )
|
|
|
- Text("请前往锁控柜进行取锁,取钥匙操作", fontSize = 14.sp, color = Text)
|
|
|
+// Icon(
|
|
|
+// painter = painterResource(R.drawable.job_warning),
|
|
|
+// contentDescription = null,
|
|
|
+// modifier = Modifier.size(60.dp),
|
|
|
+// tint = MaterialTheme.colorScheme.primary
|
|
|
+// )
|
|
|
+// Text("请前往锁控柜进行取锁,取钥匙操作", fontSize = 14.sp, color = Text)
|
|
|
+ Row(verticalAlignment = Alignment.CenterVertically) {
|
|
|
+ Icon(
|
|
|
+ painterResource(R.drawable.location),
|
|
|
+ contentDescription = null,
|
|
|
+ modifier = Modifier
|
|
|
+ .padding(end = 5.dp)
|
|
|
+ .size(16.dp),
|
|
|
+ tint = MaterialTheme.colorScheme.primary
|
|
|
+ )
|
|
|
+ Text("隔离点位", fontSize = 16.sp, fontWeight = FontWeight.Bold, color = Text)
|
|
|
+ }
|
|
|
+ Row(
|
|
|
+ Modifier
|
|
|
+ .padding(top = 10.dp)
|
|
|
+ .fillMaxWidth()
|
|
|
+ ) {
|
|
|
+ Row(
|
|
|
+ Modifier
|
|
|
+ .weight(1f)
|
|
|
+ .height(80.dp)
|
|
|
+ .background(Color(0xFFFFF8E6), shape = RoundedCornerShape(16.dp)),
|
|
|
+ verticalAlignment = Alignment.CenterVertically
|
|
|
+ ) {
|
|
|
+ Image(
|
|
|
+ painterResource(R.mipmap.point),
|
|
|
+ contentDescription = null,
|
|
|
+ modifier = Modifier.scale(1.2f)
|
|
|
+ )
|
|
|
+ Column {
|
|
|
+ Text(
|
|
|
+ "E-29",
|
|
|
+ fontSize = 14.sp,
|
|
|
+ lineHeight = 16.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Text
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ state.points.first.equipRfidNo,
|
|
|
+ fontSize = 12.sp,
|
|
|
+ lineHeight = 14.sp,
|
|
|
+ color = Text.copy(alpha = 0.5f)
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ if (state.step <= 1) {
|
|
|
+ if (state.points.first.closed == 1) "已隔离" else "待隔离"
|
|
|
+ } else {
|
|
|
+ if (state.points.first.closed == 1) "已解除隔离" else "待解除隔离"
|
|
|
+ },
|
|
|
+ fontSize = 12.sp,
|
|
|
+ lineHeight = 14.sp,
|
|
|
+ color = if (state.step <= 1) {
|
|
|
+ if (state.points.first.closed == 1) Color.Red else Color.Green
|
|
|
+ } else {
|
|
|
+ if (state.points.first.closed == 1) Color.Green else Color.Red
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Spacer(Modifier.width(10.dp))
|
|
|
+ Row(
|
|
|
+ Modifier
|
|
|
+ .weight(1f)
|
|
|
+ .height(80.dp)
|
|
|
+ .background(Color(0xFFFFF8E6), shape = RoundedCornerShape(16.dp)),
|
|
|
+ verticalAlignment = Alignment.CenterVertically
|
|
|
+ ) {
|
|
|
+ Image(
|
|
|
+ painterResource(R.mipmap.point),
|
|
|
+ contentDescription = null,
|
|
|
+ modifier = Modifier.scale(1.2f)
|
|
|
+ )
|
|
|
+ Column {
|
|
|
+ Text(
|
|
|
+ "E-39",
|
|
|
+ fontSize = 14.sp,
|
|
|
+ lineHeight = 16.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Text
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ state.points.second.equipRfidNo,
|
|
|
+ fontSize = 12.sp,
|
|
|
+ lineHeight = 14.sp,
|
|
|
+ color = Text.copy(alpha = 0.5f)
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ if (state.step <= 1) {
|
|
|
+ if (state.points.second.closed == 1) "已隔离" else "待隔离"
|
|
|
+ } else {
|
|
|
+ if (state.points.second.closed == 1) "已解除隔离" else "待解除隔离"
|
|
|
+ },
|
|
|
+ fontSize = 12.sp,
|
|
|
+ lineHeight = 14.sp,
|
|
|
+ color = if (state.step <= 1) {
|
|
|
+ if (state.points.second.closed == 1) Color.Red else Color.Green
|
|
|
+ } else {
|
|
|
+ if (state.points.second.closed == 1) Color.Green else Color.Red
|
|
|
+ }
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 获取设备列表
|
|
|
+ if (state.step == 0 || state.step == 2) Row(
|
|
|
+ verticalAlignment = Alignment.CenterVertically,
|
|
|
+ modifier = Modifier.padding(vertical = 16.dp)
|
|
|
+ ) {
|
|
|
+ Icon(
|
|
|
+ painterResource(R.drawable.category),
|
|
|
+ contentDescription = null,
|
|
|
+ modifier = Modifier
|
|
|
+ .padding(end = 5.dp)
|
|
|
+ .size(16.dp),
|
|
|
+ tint = MaterialTheme.colorScheme.primary
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ "${if (state.step <= 1) "上锁" else "解除"}隔离所需设备",
|
|
|
+ fontSize = 16.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Text
|
|
|
+ )
|
|
|
+ }
|
|
|
+ if (state.step == 0 || state.step == 2) Row(Modifier.height(110.dp)) {
|
|
|
+ state.keys.forEach {
|
|
|
+ Column(
|
|
|
+ Modifier
|
|
|
+ .padding(end = 10.dp)
|
|
|
+ .fillMaxHeight()
|
|
|
+ .width(90.dp)
|
|
|
+ .background(Color(0xFFFFF8E6), shape = RoundedCornerShape(16.dp)),
|
|
|
+ horizontalAlignment = Alignment.CenterHorizontally
|
|
|
+ ) {
|
|
|
+ Image(
|
|
|
+ painterResource(R.mipmap.key),
|
|
|
+ contentDescription = null,
|
|
|
+ modifier = Modifier
|
|
|
+ .size(80.dp)
|
|
|
+ .scale(0.9f)
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ it,
|
|
|
+ fontSize = 14.sp,
|
|
|
+ lineHeight = 14.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Text
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 当前使用中的挂锁
|
|
|
+ state.useLocks.forEach {
|
|
|
+ Column(
|
|
|
+ Modifier
|
|
|
+ .padding(end = 10.dp)
|
|
|
+ .fillMaxHeight()
|
|
|
+ .width(90.dp)
|
|
|
+ .background(Color(0xFFFFF8E6), shape = RoundedCornerShape(16.dp)),
|
|
|
+ horizontalAlignment = Alignment.CenterHorizontally
|
|
|
+ ) {
|
|
|
+ Image(
|
|
|
+ painterResource(R.mipmap.lock),
|
|
|
+ contentDescription = null,
|
|
|
+ modifier = Modifier
|
|
|
+ .size(80.dp)
|
|
|
+ .scale(0.9f)
|
|
|
+ )
|
|
|
+ Text(
|
|
|
+ it,
|
|
|
+ fontSize = 14.sp,
|
|
|
+ lineHeight = 14.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Text
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -334,7 +645,12 @@ class PageDetailTask : PageBase() {
|
|
|
.padding(horizontal = 16.dp)
|
|
|
.fillMaxWidth()
|
|
|
) {
|
|
|
- if (listOf("complete", "inputInfo", "confirm").contains(state.node.type) || state.node.checkCanOptionByPhone()) {
|
|
|
+ if (listOf(
|
|
|
+ "complete",
|
|
|
+ "inputInfo",
|
|
|
+ "confirm"
|
|
|
+ ).contains(state.node.type) || state.node.checkCanOptionByPhone()
|
|
|
+ ) {
|
|
|
Button(
|
|
|
{ destroy() }, modifier = Modifier
|
|
|
.padding(horizontal = 8.dp)
|
|
|
@@ -353,7 +669,13 @@ class PageDetailTask : PageBase() {
|
|
|
.size(18.dp),
|
|
|
tint = Color(0xFFFF4D4F)
|
|
|
)
|
|
|
- Text("取消", fontSize = 16.sp, lineHeight = 16.sp, fontWeight = FontWeight.Bold, color = Color(0xFFFF4D4F))
|
|
|
+ Text(
|
|
|
+ "取消",
|
|
|
+ fontSize = 16.sp,
|
|
|
+ lineHeight = 16.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Color(0xFFFF4D4F)
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
Button(
|
|
|
@@ -436,7 +758,13 @@ class PageDetailTask : PageBase() {
|
|
|
.size(18.dp),
|
|
|
tint = Color.White
|
|
|
)
|
|
|
- Text("审核通过", fontSize = 16.sp, lineHeight = 16.sp, fontWeight = FontWeight.Bold, color = Color.White)
|
|
|
+ Text(
|
|
|
+ "审核通过",
|
|
|
+ fontSize = 16.sp,
|
|
|
+ lineHeight = 16.sp,
|
|
|
+ fontWeight = FontWeight.Bold,
|
|
|
+ color = Color.White
|
|
|
+ )
|
|
|
}
|
|
|
}
|
|
|
}
|