Pārlūkot izejas kodu

优化无法分配钥匙时的提示语;修正作业票工作进度页按钮显示错误;锁定站改地图临时提交

Frankensteinly 7 mēneši atpakaļ
vecāks
revīzija
896b21c76e

+ 2 - 2
app/src/main/java/com/grkj/iscs/BusinessManager.kt

@@ -463,7 +463,7 @@ object BusinessManager {
         if (isNeedKey && lockCount >= needLockCount) {
             key = ModBusController.getOneKey()
             if (key == null) {
-                val msg = MyApplication.instance!!.applicationContext.resources.getString(R.string.key_is_not_enough)
+                val msg = MyApplication.instance!!.applicationContext.resources.getString(R.string.no_available_key)
                 LogUtil.w(msg)
                 tipStr = if (tipStr.isEmpty()) {
                     msg
@@ -648,7 +648,7 @@ object BusinessManager {
 
                 override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
                     if (isNeedLoading) mEventBus.postValue(MsgEvent(MSG_EVENT_LOADING, LoadingMsg(false, CommonUtils.getStr(R.string.ble_connect_fail), false)))
-                    LogUtil.e("onConnectFail : $bleDevice - ${exception?.description}")
+                    LogUtil.e("onConnectFail : ${bleDevice?.mac} - ${exception?.description}")
                     prepareDoneCallBack?.invoke(false, null)
                 }
 

+ 1 - 0
app/src/main/java/com/grkj/iscs/modbus/PortManager.kt

@@ -101,6 +101,7 @@ class PortManager private constructor(
             try {
                 val file = File(if(usb) "/dev/ttyUSB${port}" else "/dev/ttyS${port}")
                 LogUtil.i("连接 port file")
+                // TODO 判断file是否为空,防止不接线导致黑屏
                 SerialPort(file, bps, 0).run {
                     blocked = false
                     LogUtil.i("建立 SerialPort")

+ 14 - 0
app/src/main/java/com/grkj/iscs/model/vo/ticket/LotoMapRespVO.kt

@@ -28,4 +28,18 @@ data class LotoMapRespVO(
     val pointPicture: String?,
 
     val mapImg: String?,
+
+    val x: String?,
+
+    val y: String?,
+
+    val entityId: Long?,
+
+    val entityName: String?,
+
+    val id: Long?,
+
+    val mapId: Long?,
+
+    val mapType: String?
 )

+ 45 - 0
app/src/main/java/com/grkj/iscs/util/CommonUtils.kt

@@ -10,6 +10,11 @@ import android.graphics.Bitmap
 import android.os.Build
 import android.os.Environment
 import androidx.appcompat.app.AppCompatActivity
+import com.bumptech.glide.Glide
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.engine.GlideException
+import com.bumptech.glide.request.RequestListener
+import com.bumptech.glide.request.target.Target
 import com.grkj.iscs.MyApplication
 import com.grkj.iscs.model.Constants.PERMISSION_REQUEST_CODE
 import pub.devrel.easypermissions.EasyPermissions
@@ -125,4 +130,44 @@ object CommonUtils {
         // 获取字节数组
         return stream.toByteArray()
     }
+
+    fun loadBitmapFromUrl(ctx: Context, url: String, callback: (Bitmap?) -> Unit) {
+        try {
+            Glide.with(ctx)
+                .asBitmap()
+                .load(url)
+                .listener(object : RequestListener<Bitmap> {
+                    override fun onLoadFailed(
+                        e: GlideException?,
+                        model: Any?,
+                        target: Target<Bitmap>?,
+                        isFirstResource: Boolean
+                    ): Boolean {
+                        Executor.runOnMain {
+                            callback(null)
+                        }
+                        return false
+                    }
+
+                    override fun onResourceReady(
+                        resource: Bitmap?,
+                        model: Any?,
+                        target: Target<Bitmap>?,
+                        dataSource: DataSource?,
+                        isFirstResource: Boolean
+                    ): Boolean {
+                        Executor.runOnMain {
+                            callback(resource)
+                        }
+                        return false
+                    }
+                })
+                .submit()
+        } catch (e: Exception) {
+            e.printStackTrace()
+            Executor.runOnMain {
+                callback(null)
+            }
+        }
+    }
 }

+ 1 - 1
app/src/main/java/com/grkj/iscs/util/NetApi.kt

@@ -775,7 +775,7 @@ object NetApi {
     /**
      * 获取地图参数详细信息
      *
-     * @param id 机柜固定传1,物资柜固定传4
+     * @param id 机柜固定首页传1,锁定站传2,物资柜固定传4
      */
     fun getMapInfo(id: Long, callBack: (MapInfoRespVO?) -> Unit) {
         NetHttpManager.getInstance().doRequestNet(

+ 1 - 0
app/src/main/java/com/grkj/iscs/view/fragment/JobProgressFragment.kt

@@ -177,6 +177,7 @@ class JobProgressFragment(val goBack: () -> Unit) :
     private fun handleActionBtnVisibility() {
         when (mStep) {
             4 -> {
+                mBinding?.cbAction?.setText(getString(R.string.go_locking))
                 if (!mPointList.all { it.pointStatus == "1" }) {
                     mBinding?.cbAction?.visibility = View.VISIBLE
                 }

+ 71 - 1
app/src/main/java/com/grkj/iscs/view/fragment/StepFragment.kt

@@ -1,5 +1,6 @@
 package com.grkj.iscs.view.fragment
 
+import android.graphics.PointF
 import android.view.View
 import android.widget.ImageView
 import androidx.constraintlayout.widget.ConstraintLayout
@@ -12,11 +13,15 @@ import com.grkj.iscs.model.bo.PageChangeBO
 import com.grkj.iscs.model.vo.machinery.MachineryDetailRespVO
 import com.grkj.iscs.model.vo.ticket.LotoMapRespVO
 import com.grkj.iscs.model.vo.ticket.StepDetailRespVO
+import com.grkj.iscs.util.CommonUtils
+import com.grkj.iscs.util.ToastUtils
 import com.grkj.iscs.util.log.LogUtil
 import com.grkj.iscs.view.base.BaseMvpFragment
 import com.grkj.iscs.view.dialog.TipDialog
 import com.grkj.iscs.view.iview.IStepView
 import com.grkj.iscs.view.presenter.StepPresenter
+import com.grkj.iscs.view.widget.CustomStationLayer
+import com.onlylemi.mapview.library.MapViewListener
 import com.zhy.adapter.recyclerview.CommonAdapter
 import com.zhy.adapter.recyclerview.base.ViewHolder
 import kotlin.math.ceil
@@ -34,6 +39,9 @@ class StepFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> Uni
     private var mMachineryDetail: MachineryDetailRespVO? = null
     private var mStep: Int = 0
     private lateinit var mTipDialog: TipDialog
+    private var stationLayer: CustomStationLayer? = null
+    private val mStationList = mutableListOf<CustomStationLayer.IsolationPoint>()
+    private var mMapPicWidth = 1
 
     override val viewBinding: FragmentStepBinding
         get() = FragmentStepBinding.inflate(layoutInflater)
@@ -83,7 +91,7 @@ class StepFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> Uni
                 }
             }
         }
-        
+
         mBinding?.rvLoto?.adapter = object : CommonAdapter<LotoMapRespVO>(requireContext(), R.layout.item_rv_loto, mLotoList) {
             override fun convert(holder: ViewHolder, loto: LotoMapRespVO, position: Int) {
                 val cl = holder.getView<ConstraintLayout>(R.id.cl_container)
@@ -127,6 +135,32 @@ class StepFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> Uni
                     mChangePage?.machineryId, mChangePage?.machineryName))
             }
         }
+
+        initMap()
+    }
+
+    private fun initMap() {
+        mBinding?.mapview?.isScaleAndRotateTogether = false
+        mBinding?.mapview?.setMapViewListener(object : MapViewListener {
+            override fun onMapLoadSuccess() {
+                if (stationLayer != null) {
+                    mBinding?.mapview?.currentRotateDegrees = 0f
+                    return
+                }
+                stationLayer = CustomStationLayer(mBinding?.mapview, mStationList)
+                stationLayer?.setMarkIsClickListener(object : CustomStationLayer.MarkIsClickListener {
+                    override fun markIsClick(index: Int, btnIndex: Int) {
+//                        ToastUtils.tip(mPointList[index].name + " is selected, btnIndex is " + btnIndex)
+                    }
+                })
+                mBinding?.mapview?.addLayer(stationLayer)
+                mBinding?.mapview?.refresh()
+            }
+
+            override fun onMapLoadFail() {
+                ToastUtils.tip("onMapLoadFail")
+            }
+        })
     }
 
     override fun refreshPage(pageChangeBO: PageChangeBO) {
@@ -172,6 +206,42 @@ class StepFragment(val goBack: () -> Unit, val changePage: (PageChangeBO) -> Uni
             (mBinding?.rvLoto?.layoutManager as GridLayoutManager).spanCount = spanCount
 
             mBinding?.rvLoto?.adapter?.notifyDataSetChanged()
+
+            if (mLotoList.isNotEmpty()) {
+                mLotoList[0].mapId?.let { itId ->
+                    presenter?.getMapInfo(itId) { itMapInfo ->
+                        itMapInfo?.imageUrl ?: return@getMapInfo
+                        CommonUtils.loadBitmapFromUrl(requireContext(), itMapInfo.imageUrl) {
+                            if (it == null) {
+                                LogUtil.e("Map pic is null")
+                                return@loadBitmapFromUrl
+                            }
+                            mMapPicWidth = it.width
+
+                            mStationList.clear()
+
+                            val location = IntArray(2)
+                            mBinding?.mapview?.getLocationOnScreen(location)
+
+                            itMapInfo.pointList?.forEach { itPoint ->
+                                val locationX = (itPoint.x!!.toFloat() - (itMapInfo.x!!.toFloat() / 50))  * 50 / itMapInfo.width!!.toFloat() * it.width
+                                val locationY = (itPoint.y!!.toFloat() + 1 - itMapInfo.y!!.toFloat() / 50) * 50 / itMapInfo.height!!.toFloat() * it.height
+                                
+                                mStationList.add(
+                                    CustomStationLayer.IsolationPoint(
+                                        PointF(locationX, locationY),
+                                        itPoint.entityName!!,
+                                        itPoint.entityId!!,
+                                        mLotoList.any { it.entityId == itPoint.entityId }
+                                    )
+                                )
+                            }
+
+                            mBinding?.mapview?.loadMap(it)
+                        }
+                    }
+                }
+            }
         }
     }
 

+ 8 - 5
app/src/main/java/com/grkj/iscs/view/fragment/WorkshopFragment.kt

@@ -7,6 +7,7 @@ import com.grkj.iscs.databinding.FragmentWorkshopBinding
 import com.grkj.iscs.model.Constants
 import com.grkj.iscs.model.bo.PageChangeBO
 import com.grkj.iscs.model.vo.ticket.WorkstationTicketListRespVO
+import com.grkj.iscs.util.CommonUtils
 import com.grkj.iscs.util.ToastUtils
 import com.grkj.iscs.util.log.LogUtil
 import com.grkj.iscs.view.base.BaseMvpFragment
@@ -29,6 +30,7 @@ class WorkshopFragment(val changePage: (PageChangeBO) -> Unit) :
         CustomPoint(PointF(600f, 260f), "R&R", 8, mutableListOf()),
         CustomPoint(PointF(800f, 480f), "CCO", 7, mutableListOf())
     )
+    private var mMapPicWidth = 1
 
     override val viewBinding: FragmentWorkshopBinding
         get() = FragmentWorkshopBinding.inflate(layoutInflater)
@@ -58,13 +60,14 @@ class WorkshopFragment(val changePage: (PageChangeBO) -> Unit) :
 
     fun refreshPage() {
         presenter?.getMapInfo {
-            presenter?.loadBitmapFromUrl(it?.imageUrl!!) {
+            CommonUtils.loadBitmapFromUrl(requireContext(), it?.imageUrl!!) {
                 if (it == null) {
                     LogUtil.e("Map pic is null")
                     return@loadBitmapFromUrl
                 }
+                mMapPicWidth = it.width
                 mBinding?.mapview?.loadMap(it)
-
+                
                 presenter?.getMapPointPage {
                     mPointList.clear()
                     it?.records?.forEach { itPoint ->
@@ -81,9 +84,8 @@ class WorkshopFragment(val changePage: (PageChangeBO) -> Unit) :
                             itPoint.ticketList = it.filter { it.workstationId == itPoint.workstationId }.toMutableList()
                         }
                         mBinding?.rvStatistics?.adapter?.notifyDataSetChanged()
-                        // 重新load之后,会自动复位
-//                        mBinding?.mapview?.currentRotateDegrees = 0f
-//                        mBinding?.mapview?.currentZoom = 1f
+
+
                         mBinding?.mapview?.refresh()
                     }
                 }
@@ -97,6 +99,7 @@ class WorkshopFragment(val changePage: (PageChangeBO) -> Unit) :
             override fun onMapLoadSuccess() {
                 // 要加null判断,否则loadMap的时候会调用onMapLoadSuccess导致多次创建layer
                 if (markLayer != null) {
+                    mBinding?.mapview?.currentRotateDegrees = 0f
                     return
                 }
                 markLayer = CustomMarkLayer(mBinding?.mapview, mPointList)

+ 9 - 0
app/src/main/java/com/grkj/iscs/view/presenter/StepPresenter.kt

@@ -3,6 +3,7 @@ package com.grkj.iscs.view.presenter
 import com.grkj.iscs.R
 import com.grkj.iscs.model.vo.machinery.MachineryDetailRespVO
 import com.grkj.iscs.model.vo.machinery.MachineryPageRespVO
+import com.grkj.iscs.model.vo.map.MapInfoRespVO
 import com.grkj.iscs.model.vo.ticket.LotoMapRespVO
 import com.grkj.iscs.model.vo.ticket.StepDetailRespVO
 import com.grkj.iscs.model.vo.ticket.TicketDetailRespVO
@@ -73,4 +74,12 @@ class StepPresenter : BasePresenter<IStepView>() {
             }
         }
     }
+
+    fun getMapInfo(mapId: Long, callBack: (MapInfoRespVO?) -> Unit) {
+        NetApi.getMapInfo(mapId) {
+            Executor.runOnMain {
+                callBack(it)
+            }
+        }
+    }
 }

+ 0 - 46
app/src/main/java/com/grkj/iscs/view/presenter/WorkshopPresenter.kt

@@ -1,11 +1,5 @@
 package com.grkj.iscs.view.presenter
 
-import android.graphics.Bitmap
-import com.bumptech.glide.Glide
-import com.bumptech.glide.load.DataSource
-import com.bumptech.glide.load.engine.GlideException
-import com.bumptech.glide.request.RequestListener
-import com.bumptech.glide.request.target.Target
 import com.grkj.iscs.model.vo.map.MapInfoRespVO
 import com.grkj.iscs.model.vo.map.MapPointPageRespVO
 import com.grkj.iscs.model.vo.ticket.WorkstationTicketListRespVO
@@ -39,44 +33,4 @@ class WorkshopPresenter : BasePresenter<IWorkshopView>() {
             }
         }
     }
-
-    fun loadBitmapFromUrl(url: String, callback: (Bitmap?) -> Unit) {
-        try {
-            Glide.with(mContext!!)
-                .asBitmap()
-                .load(url)
-                .listener(object : RequestListener<Bitmap> {
-                    override fun onLoadFailed(
-                        e: GlideException?,
-                        model: Any?,
-                        target: Target<Bitmap>?,
-                        isFirstResource: Boolean
-                    ): Boolean {
-                        Executor.runOnMain {
-                            callback(null)
-                        }
-                        return false
-                    }
-
-                    override fun onResourceReady(
-                        resource: Bitmap?,
-                        model: Any?,
-                        target: Target<Bitmap>?,
-                        dataSource: DataSource?,
-                        isFirstResource: Boolean
-                    ): Boolean {
-                        Executor.runOnMain {
-                            callback(resource)
-                        }
-                        return false
-                    }
-                })
-                .submit()
-        } catch (e: Exception) {
-            e.printStackTrace()
-            Executor.runOnMain {
-                callback(null)
-            }
-        }
-    }
 }

+ 189 - 0
app/src/main/java/com/grkj/iscs/view/widget/CustomStationLayer.kt

@@ -0,0 +1,189 @@
+package com.grkj.iscs.view.widget
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Matrix
+import android.graphics.Paint
+import android.graphics.PointF
+import android.util.Pair
+import android.view.MotionEvent
+import com.grkj.iscs.R
+import com.onlylemi.mapview.library.MapView
+import com.onlylemi.mapview.library.layer.MapBaseLayer
+import kotlin.math.cos
+import kotlin.math.sin
+
+
+class CustomStationLayer @JvmOverloads constructor(
+    mapView: MapView?,
+    private var pointList: List<IsolationPoint> = mutableListOf()
+) : MapBaseLayer(mapView) {
+    private var listener: MarkIsClickListener? = null
+    private var radiusMark = 0f
+    private var isClickMark: Boolean = false
+    private var num: Int
+    private lateinit var paint: Paint
+    private var btnIndex: Int
+    private var currentZoom = 0f
+    private var currentDegree = 0f
+    private lateinit var bgBitmap: Bitmap
+
+
+    init {
+        num = -1
+        btnIndex = -1
+        initLayer()
+    }
+
+    private fun initLayer() {
+        radiusMark = setValue(4.0f)
+        bgBitmap = getResizedBitmapFromMipmap(mapView.context, R.mipmap.red_stroke_bg, 24, 32)
+
+        paint = Paint()
+        paint.isAntiAlias = true
+        paint.style = Paint.Style.FILL_AND_STROKE
+    }
+
+    private fun getResizedBitmapFromMipmap(
+        context: Context,
+        resId: Int,
+        reqWidth: Int,
+        reqHeight: Int
+    ): Bitmap {
+        // First decode with inJustDecodeBounds=true to check dimensions
+        val options = BitmapFactory.Options()
+        options.inJustDecodeBounds = true
+        BitmapFactory.decodeResource(context.resources, resId, options)
+
+        // Calculate inSampleSize
+        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
+
+        // Decode bitmap with inSampleSize set
+        options.inJustDecodeBounds = false
+        val scaledBitmap = BitmapFactory.decodeResource(context.resources, resId, options)
+
+        // Resize the bitmap if necessary
+        return Bitmap.createScaledBitmap(scaledBitmap, reqWidth, reqHeight, false)
+    }
+
+    private fun calculateInSampleSize(
+        options: BitmapFactory.Options,
+        reqWidth: Int,
+        reqHeight: Int
+    ): Int {
+        // Raw height and width of image
+        val height = options.outHeight
+        val width = options.outWidth
+        var inSampleSize = 1
+
+        if (height > reqHeight || width > reqWidth) {
+            val halfHeight = height / 2
+            val halfWidth = width / 2
+
+            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+            // height and width larger than the requested height and width.
+            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
+                inSampleSize *= 2
+            }
+        }
+
+        return inSampleSize
+    }
+
+    override fun onTouch(event: MotionEvent) {
+        if (pointList.isNotEmpty()) {
+            val goal = mapView.convertMapXYToScreenXY(event.x, event.y)
+
+            for (i in pointList.indices) {
+                var continueOuterLoop = false // 双循环跳出标志变量
+
+                // 计算文字点击
+                val width = paint.measureText(pointList[i].entityName) / currentZoom
+
+
+
+                // 计算图标点击
+
+                // 跳出外层循环
+                if (continueOuterLoop) break
+
+                if (i == pointList.size - 1) {
+                    isClickMark = false
+                }
+            }
+
+            if (listener != null && isClickMark) {
+                listener!!.markIsClick(num, btnIndex)
+                mapView.refresh()
+            }
+        }
+    }
+
+    override fun draw(
+        canvas: Canvas,
+        currentMatrix: Matrix,
+        currentZoom: Float,
+        currentRotateDegrees: Float
+    ) {
+        this.currentZoom = currentZoom
+        currentDegree = 360 - currentRotateDegrees
+        if (isVisible) {
+            canvas.save()
+            if (pointList.isNotEmpty()) {
+                pointList.forEach { point ->
+                    val mark = point.pos
+                    val goal = floatArrayOf(mark.x, mark.y)
+                    currentMatrix.mapPoints(goal)
+
+                    // 文字背景
+                    val width = paint.measureText(point.entityName)
+                    paint.style = Paint.Style.FILL
+                    paint.strokeWidth = 1.0f
+
+                    paint.color = Color.parseColor("#CCFF0000")
+                    paint.textSize = radiusMark
+                    // 先画背景再画文字防止文字被盖住
+                    canvas.drawBitmap(bgBitmap, goal[0] - width / 2, goal[1] - 2 * radiusMark, paint)
+                    // 一直显示文字
+                    canvas.drawText(
+                        point.entityName,
+                        goal[0] - width / 2,
+                        goal[1] + radiusMark / 3.0f,
+                        paint
+                    )
+
+                    // 定位点,仅调试用,不要显示
+//                    canvas.drawCircle(goal[0], goal[1], 2f, paint)
+                }
+            }
+
+            canvas.restore()
+        }
+    }
+
+    private fun rotatePoint(
+        oriX: Float, oriY: Float, desX: Float, desY: Float, rotateDegrees: Float
+    ): Pair<Float, Float> {
+        // 将度数转换为弧度
+        val theta = Math.toRadians(rotateDegrees.toDouble())
+
+        // 计算旋转后的坐标
+        val newX = (oriX - desX) * cos(theta) - (oriY - desY) * sin(theta) + desX
+        val newY = (oriX - desX) * sin(theta) + ((oriY - desY) * cos(theta)) + desY
+
+        return Pair(newX.toFloat(), newY.toFloat())
+    }
+
+    fun setMarkIsClickListener(listener: MarkIsClickListener?) {
+        this.listener = listener
+    }
+
+    interface MarkIsClickListener {
+        fun markIsClick(index: Int, btnIndex: Int)
+    }
+
+    data class IsolationPoint(val pos: PointF, val entityName: String, val entityId: Long, val isSelected: Boolean)
+}

+ 9 - 1
app/src/main/res/layout/fragment_step.xml

@@ -138,7 +138,15 @@
                     android:paddingBottom="12dp"
                     app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
                     app:spanCount="3"
-                    tools:listitem="@layout/item_rv_loto" />
+                    tools:listitem="@layout/item_rv_loto"
+                    android:visibility="gone"/>
+
+                <com.onlylemi.mapview.library.MapView
+                    android:id="@+id/mapview"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:layout_marginTop="@dimen/common_spacing_small"
+                    android:layout_above="@id/rv_statistics" />
             </LinearLayout>
         </LinearLayout>