Эх сурвалжийг харах

1. 设备演示上锁隔离和解除隔离作业完成

bjb 1 сар өмнө
parent
commit
6b493a9745

+ 19 - 0
app/src/main/java/com/iscs/bozzys/api/Ticket.kt

@@ -0,0 +1,19 @@
+package com.iscs.bozzys.api
+
+import kotlinx.serialization.Serializable
+
+@Serializable
+data class Ticket(val cardNo: String = "",
+                  val password: String = "",
+    val data: List<TicketTask> = emptyList())
+
+@Serializable
+data class TicketTask(val taskCode: String = "",val dataList:List<Point> = emptyList())
+
+@Serializable
+data class Point(
+    val equipRfidNo: String = "",
+    val infoRfidNo: String = "",
+    val closed: Int = 0,
+    val target: Int = 0
+)

+ 12 - 3
app/src/main/java/com/iscs/bozzys/ui/common/Loading.kt

@@ -5,6 +5,7 @@ import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
@@ -20,6 +21,7 @@ import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import androidx.compose.ui.window.Dialog
@@ -71,12 +73,19 @@ fun LoadingCompose(vm: VMLoading = viewModel()) {
                 Modifier
                     .sizeIn(90.dp, 90.dp, 150.dp, 150.dp)
                     .clip(RoundedCornerShape(8.dp))
-                    .background(Color.Black.copy(alpha = 0.6f)),
+                    .background(Color.Black.copy(alpha = 0.75f))
+                    .padding(16.dp),
                 verticalArrangement = Arrangement.Center,
                 horizontalAlignment = Alignment.CenterHorizontally
             ) {
-                CircularProgressIndicator(Modifier.size(30.dp))
-                Text("${vm.state.content}", Modifier.padding(top = 5.dp), color = Color.White, fontSize = 12.sp)
+                CircularProgressIndicator(Modifier.size(46.dp), strokeWidth = 6.dp)
+                Text(
+                    "${vm.state.content}",
+                    Modifier.padding(top = 5.dp),
+                    color = Color.White,
+                    fontSize = 14.sp,
+                    textAlign = TextAlign.Center
+                )
             }
         }
     }

+ 344 - 16
app/src/main/java/com/iscs/bozzys/ui/pages/detail/task/PageDetailTask.kt

@@ -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
+                            )
                         }
                     }
                 }

+ 0 - 73
app/src/main/java/com/iscs/bozzys/ui/pages/home/HomeCompose.kt

@@ -1,6 +1,5 @@
 package com.iscs.bozzys.ui.pages.home
 
-import android.app.Application
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -57,20 +56,6 @@ import com.iscs.bozzys.ui.pages.return_device.openPageReturnDevice
 import com.iscs.bozzys.ui.pages.vm.VMHome
 import com.iscs.bozzys.ui.theme.Text
 import com.iscs.bozzys.utils.DateUtil.getShowDateOrTime
-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.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.getToken
-import com.iscs.bozzys.utils.ble.BleManager
-import com.iscs.bozzys.utils.ble.BleRunMode
-import com.iscs.bozzys.utils.byteArrayToHexString
 import com.iscs.bozzys.utils.getRoleName
 
 @OptIn(ExperimentalMaterial3Api::class)
@@ -224,64 +209,6 @@ private fun TopToolBar(pv: PaddingValues, vm: VMHome) {
     }
 }
 
-val testJobJson =
-    "{\"cardNo\":\"D2931A25\",\"data\":[{\"codeId\":1,\"dataList\":[{\"dataId\":87,\"equipName\":\"E_29\",\"equipRfidNo\":\"1B9105AF\",\"target\":0},{\"dataId\":88,\"equipName\":\"E_30\",\"equipRfidNo\":\"FB9091E5\",\"target\":0}],\"taskCode\":\"165\"}],\"lockList\":[{\"lockId\":1,\"rfid\":\"C097D395\"},{\"lockId\":2,\"rfid\":\"A04AD495\"}],\"password\":\"123456\"}"
-
-
-private suspend fun bleKeyTest(application: Application, mac: String) {
-    // 测试蓝牙扫描
-    val bm = BleManager(application, mac = mac)
-    val result = bm.connect()
-    if (result.connected) {
-        // 获取设备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")
-        // 获取当前设备运行模式
-        val runMode = bm.writeByResponse(token.buildBLEGetStatusCMD()).getRunMode()
-        LogUtil.i("xiaoming $mac", "当前工作模式:$runMode")
-        // 切换当前设备运行模式
-//                val switch = bm.writeByResponse(token.buildBLESwitchRunModeCMD(RunMode.STBY)).getSwitchRunModeResult()
-//                LogUtil.i("xiaoming $mac", "切换工作模式:$switch")
-        // 下发作业票
-        val tickets = token.buildBLETicketDataCMDList(testJobJson)
-        var ticketSendOk = true
-        tickets.forEach {
-            val ticket = bm.writeByResponse(it).getSendTicketResult()
-            LogUtil.i("xiaoming $mac", "下发作业票:分包${it.data[4].toInt()}发送结果:$ticket")
-            if (ticket != 0) {
-                ticketSendOk = false
-                return@forEach
-            }
-        }
-        LogUtil.i("xiaoming $mac", "下发作业票:$ticketSendOk")
-        // 作业票下发成功,修改设备运行模式为工作模式
-        val switch = bm.writeByResponse(token.buildBLESwitchRunModeCMD(BleRunMode.WORK))
-            .getSwitchRunModeResult()
-        LogUtil.i("xiaoming $mac", "切换工作模式:$switch")
-        // 读取作业票信息
-//            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 }
-//            LogUtil.i("xiaoming $mac", "读取作业票:${String(datas)}")
-    } else {
-        // 进行重连,这里可以封装尝试次数
-        bleKeyTest(application, mac)
-    }
-    bm.disconnect()
-}
-
 /**
  * 待办任务
  */

+ 129 - 3
app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMDetailTask.kt

@@ -6,13 +6,16 @@ import com.iscs.bozzys.api.ApiRequest.getResponse
 import com.iscs.bozzys.api.Attachment
 import com.iscs.bozzys.api.FormField
 import com.iscs.bozzys.api.Node
+import com.iscs.bozzys.api.Point
 import com.iscs.bozzys.api.Task
 import com.iscs.bozzys.api.TaskFormInfo
+import com.iscs.bozzys.api.Ticket
 import com.iscs.bozzys.event.RefreshEvent
 import com.iscs.bozzys.event.RefreshEventBus
 import com.iscs.bozzys.ui.common.VMBase
 import com.iscs.bozzys.ui.pages.compose.checkCanCommitReturnTips
 import com.iscs.bozzys.ui.pages.compose.getFormListByJsonList
+import com.iscs.bozzys.utils.LogUtil
 import com.iscs.bozzys.utils.SystemUtil
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -81,14 +84,20 @@ class VMDetailTask : VMBase() {
                     if (!taskInfo.checkCanOptionByPhone()) {
                         formInfo = TaskFormInfo()
                     }
-                    _state.value = _state.value.copy(forms = formInfo.fields?.getFormListByJsonList() ?: emptyList(), node = taskInfo)
+                    _state.value = _state.value.copy(
+                        forms = formInfo.fields?.getFormListByJsonList() ?: emptyList(),
+                        node = taskInfo
+                    )
                     delay(500)
                     loading.emit(StateLoading())
                 } else {
                     ApiRequest.getTaskFormInfoByFormId(it.data?.formId ?: 0).onSuccess { formInfo ->
                         this@VMDetailTask.formInfo = formInfo.data ?: TaskFormInfo()
                         _state.value =
-                            _state.value.copy(forms = this@VMDetailTask.formInfo.fields?.getFormListByJsonList() ?: emptyList(), node = taskInfo)
+                            _state.value.copy(
+                                forms = this@VMDetailTask.formInfo.fields?.getFormListByJsonList() ?: emptyList(),
+                                node = taskInfo
+                            )
                         delay(500)
                         loading.emit(StateLoading())
                     }.onFailure { err ->
@@ -154,6 +163,113 @@ class VMDetailTask : VMBase() {
             }
         }
     }
+
+    fun updateTicket(ticket: Ticket) {
+        viewModelScope.launch {
+            val task = ticket.data.getOrNull(0) ?: return@launch
+            val locks = _state.value.locks
+            var step = _state.value.step
+            var pointsF = _state.value.points.first
+            var pointsS = _state.value.points.second
+            // 校验是上锁过程还是解锁过程
+            if (_state.value.step == 1) {
+                task.dataList.forEach {
+                    if (it.equipRfidNo == "1B9105AF") {
+                        pointsF = it
+                    } else if (it.equipRfidNo == "3B9F26AF") {
+                        pointsS = it
+                    }
+                }
+                // 上锁钥匙返回,校验是否还需要设备,根据上锁点位是否都完成隔离判断当前流程
+                val useLocks = mutableListOf<String>()
+                // 查找未完成的点位
+                repeat(task.dataList.filter { it.closed == 0 }.size) {
+                    val canUse = locks.filter {
+                        !task.dataList.filter { p -> p.closed == 1 }.map { p -> p.infoRfidNo }.contains(it)
+                                && !useLocks.contains(it)
+                    }
+                    if (canUse.isNotEmpty()) useLocks += canUse[0]
+                }
+                // 所需挂锁不为空,这里需要回滚步骤
+                if (useLocks.isNotEmpty()) {
+                    step--
+                    loading.emit(StateLoading())
+                    toast.emit("点位隔离任务未完成,请继续隔离任务")
+                } else {
+                    step++
+                    // 修改点位target状态
+                    pointsF = pointsF.copy(target = 1, closed = 0)
+                    pointsS = pointsS.copy(target = 1, closed = 0)
+                }
+                LogUtil.d("xiaoming", "当前步骤 $step")
+                _state.value = _state.value.copy(step = step, useLocks = useLocks)
+            } else if (step == 3) {
+                task.dataList.forEach {
+                    if (it.equipRfidNo == "1B9105AF") {
+                        pointsF = it
+                    } else if (it.equipRfidNo == "3B9F26AF") {
+                        pointsS = it
+                    }
+                }
+                if (task.dataList.all { it.closed == 1 }) {
+                    step++
+                } else {
+                    loading.emit(StateLoading())
+                    toast.emit("点位解除隔离未完成,请继续未解除隔离作业")
+                    step--
+                }
+                _state.value = _state.value.copy(step = step, useLocks = mutableListOf())
+            }
+            _state.value = _state.value.copy(points = pointsF to pointsS)
+        }
+    }
+
+    fun updateTicketStatus(step: Int = -1, status: String, delay: Long = 0) {
+        viewModelScope.launch {
+            delay(delay)
+            if (step >= 0) _state.value = _state.value.copy(step = step)
+            loading.emit(StateLoading(show = status.isNotEmpty(), content = status))
+        }
+    }
+
+    // 构建作业票
+    fun createTicketJson(): String {
+        val sb = StringBuilder("{\"cardNo\":\"\",\"data\":[{\"codeId\":1,\"dataList\":[")
+        val step = _state.value.step
+        val points = _state.value.points
+        val useLocks = _state.value.useLocks
+        if (step == 0) {
+            var spl = ""
+            if (points.first.closed == 0) {
+                sb.append("{\"dataId\":87,\"equipName\":\"E_29\",\"equipRfidNo\":\"1B9105AF\",\"infoRfidNo\":\"\",\"target\":0}")
+                spl = ","
+            }
+            if (points.second.closed == 0) {
+                sb.append("$spl{\"dataId\":88,\"equipName\":\"E_39\",\"equipRfidNo\":\"3B9F26AF\",\"infoRfidNo\":\"\",\"target\":0}")
+            }
+        } else if (step == 2) {
+            var spl = ""
+            if ((points.first.closed == 1 && points.first.target == 0) || (points.first.closed == 0 && points.first.target == 1)) {
+                sb.append("{\"dataId\":87,\"equipName\":\"E_29\",\"equipRfidNo\":\"1B9105AF\",\"infoRfidNo\":\"${points.first.infoRfidNo}\",\"target\":1}")
+                spl = ","
+            }
+            if ((points.second.closed == 1 && points.second.target == 0) || (points.second.closed == 0 && points.second.target == 1)) {
+                sb.append("$spl{\"dataId\":88,\"equipName\":\"E_39\",\"equipRfidNo\":\"3B9F26AF\",\"infoRfidNo\":\"${points.second.infoRfidNo}\",\"target\":1}")
+            }
+        }
+        // 所需挂锁
+        sb.append("],\"taskCode\":\"165\"}],\"lockList\":[")
+        // 挂锁
+        var idx = 1
+        useLocks.forEach {
+            if (idx == 1) sb.append("{\"lockId\":$idx,\"rfid\":\"$it\"}")
+            else sb.append(",{\"lockId\":$idx,\"rfid\":\"$it\"}")
+            idx++
+        }
+        sb.append("],\"password\":\"123456\"}")
+        LogUtil.d("xiaoming", "创建的作业票:$sb")
+        return "$sb"
+    }
 }
 
 /**
@@ -162,9 +278,19 @@ class VMDetailTask : VMBase() {
  * @param forms     自定义表单数据
  * @param mbForms   盲板表单数据
  * @param node      当前节点数据
+ * @param step      0-去上锁 1-完成上锁 2-去解锁 3-完成解锁 4-任务完成
+ * @param points    点位列表
+ * @param keys      钥匙列表
+ * @param locks     挂锁列表
+ * @param useLocks  当前上锁所需要的设备
  */
 data class StateDetailTask(
     val forms: List<FormField> = listOf(),
     val mbForms: List<FormField> = listOf(),
-    val node: Node = Node()
+    val node: Node = Node(),
+    val step: Int = 0,
+    val points: Pair<Point, Point> = Point(equipRfidNo = "1B9105AF") to Point(equipRfidNo = "3B9F26AF"),
+    val keys: MutableList<String> = mutableListOf("6308C5E4"),
+    val locks: MutableList<String> = mutableListOf("33AFAFE4", "0353B1E4"),
+    val useLocks: MutableList<String> = mutableListOf("33AFAFE4", "0353B1E4")
 )

+ 16 - 2
app/src/main/java/com/iscs/bozzys/utils/ble/BleFrameExt.kt

@@ -40,7 +40,18 @@ object BleFrameExt {
         } else if (runMode == BleRunMode.STBY) {
             mode = 0x02
         }
-        return BleFrame(BleProtocol.REQ_SWITCH_MODE, byteArrayOf(mode.toByte()) + getUnixTime() + this, BleProtocol.RSP_SWITCH_MODE)
+        return BleFrame(
+            BleProtocol.REQ_SWITCH_MODE,
+            byteArrayOf(mode.toByte()) + getUnixTime() + this,
+            BleProtocol.RSP_SWITCH_MODE
+        )
+    }
+
+    /**
+     * 构建用于对蓝牙设备进行关机的命令,因为设备即关机无任何响应需要处理
+     */
+    fun ByteArray.buildBLEPowerOffCMD(): BleFrame {
+        return BleFrame(BleProtocol.REQ_POWER_OFF, byteArrayOf(0x02) + getUnixTime() + this, BleProtocol.RSP_POWER_OFF)
     }
 
     /**
@@ -59,7 +70,10 @@ object BleFrameExt {
             // 将包做切片操作
             val subPkg = pkg.copyOfRange(idx * 128, endIdx)
             // 封装数据包操作
-            val data = pkgCount.toByteArray() + idx.toByteArray() + subPkg.crc16(0, subPkg.size) + subPkg.size.toByteArray() + subPkg
+            val data = pkgCount.toByteArray() + idx.toByteArray() + subPkg.crc16(
+                0,
+                subPkg.size
+            ) + subPkg.size.toByteArray() + subPkg
             // 装包处理
             cmds.add(
                 BleFrame(

+ 6 - 0
app/src/main/java/com/iscs/bozzys/utils/ble/BleProtocol.kt

@@ -38,6 +38,12 @@ object BleProtocol {
     // 工作模式切换响应
     val RSP_SWITCH_MODE = byteArrayOf(0x02, 0x02, 0x03, 0x01)
 
+    // 设备关机
+    val REQ_POWER_OFF = byteArrayOf(0x02, 0x01, 0x02, 0xEE.toByte())
+
+    // 设备关机响应
+    val RSP_POWER_OFF = byteArrayOf(0x02, 0x02, 0x03, 0xEE.toByte())
+
     // 工作票下发
     val REQ_SEND_WORK_TICKET = byteArrayOf(0x02, 0x01)
 

BIN
app/src/main/res/mipmap-xhdpi/key.png


BIN
app/src/main/res/mipmap-xhdpi/lock.png


BIN
app/src/main/res/mipmap-xhdpi/point.png