diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 5dff823..513db5e 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -151,14 +151,13 @@ fenceTextView.setOnClickListener { BottomActionSheet.Builder() .setContext(this) - .setActionItemTitle(arrayListOf("区域规划", "监控角度", "云台角度")) + .setActionItemTitle(arrayListOf("监控区域", "云台角度")) .setItemTextColor(R.color.mainThemeColor.convertColor(this)) .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { override fun onActionItemClick(position: Int) { when (position) { - 0 -> "" - 1 -> navigatePageTo() - 2 -> navigatePageTo() + 0 -> navigatePageTo() + 1 -> navigatePageTo() } } }).build().show() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 5dff823..513db5e 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -151,14 +151,13 @@ fenceTextView.setOnClickListener { BottomActionSheet.Builder() .setContext(this) - .setActionItemTitle(arrayListOf("区域规划", "监控角度", "云台角度")) + .setActionItemTitle(arrayListOf("监控区域", "云台角度")) .setItemTextColor(R.color.mainThemeColor.convertColor(this)) .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { override fun onActionItemClick(position: Int) { when (position) { - 0 -> "" - 1 -> navigatePageTo() - 2 -> navigatePageTo() + 0 -> navigatePageTo() + 1 -> navigatePageTo() } } }).build().show() diff --git a/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt new file mode 100644 index 0000000..d3a8181 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt @@ -0,0 +1,39 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class RegionViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val postResult = MutableLiveData() + + fun postRegion(code: String, color: String, position: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.postRegion(code, color, position) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + postResult.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + it.cause.toString().show(BaseApplication.get()) + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 5dff823..513db5e 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -151,14 +151,13 @@ fenceTextView.setOnClickListener { BottomActionSheet.Builder() .setContext(this) - .setActionItemTitle(arrayListOf("区域规划", "监控角度", "云台角度")) + .setActionItemTitle(arrayListOf("监控区域", "云台角度")) .setItemTextColor(R.color.mainThemeColor.convertColor(this)) .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { override fun onActionItemClick(position: Int) { when (position) { - 0 -> "" - 1 -> navigatePageTo() - 2 -> navigatePageTo() + 0 -> navigatePageTo() + 1 -> navigatePageTo() } } }).build().show() diff --git a/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt new file mode 100644 index 0000000..d3a8181 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt @@ -0,0 +1,39 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class RegionViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val postResult = MutableLiveData() + + fun postRegion(code: String, color: String, position: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.postRegion(code, color, position) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + postResult.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + it.cause.toString().show(BaseApplication.get()) + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt new file mode 100644 index 0000000..72b7d5e --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt @@ -0,0 +1,136 @@ +package com.casic.br.operationsite.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.operationsite.model.Point +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth + +class VideoRegionView(private val ctx: Context, attrs: AttributeSet) : View(ctx, attrs) { + + private val routePath: Path = Path() + private val rectPath: Path = Path() + private val routePaint: Paint = Paint() + private val borderPaint: Paint = Paint() + private var routes = ArrayList() + + init { + routePaint.isAntiAlias = true + routePaint.color = Color.RED + routePaint.style = Paint.Style.STROKE + routePaint.strokeWidth = 7f //设置线宽 + routePaint.isAntiAlias = true + + borderPaint.isAntiAlias = true + borderPaint.color = Color.BLUE + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = 7f //设置线宽 + borderPaint.isAntiAlias = true + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawPath(routePath, routePaint) + + canvas.drawPath(rectPath, borderPaint) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + + val x = event.x + val y = event.y + routes.add(Point(x, y)) + + when (event.action) { + MotionEvent.ACTION_DOWN -> routePath.moveTo(x, y) + MotionEvent.ACTION_MOVE -> routePath.lineTo(x, y) + MotionEvent.ACTION_UP -> { + routePath.lineTo(x, y) + + /** + * 找出最大的(x1,y1)和最小的(x2,y2) + * + * 左上(x2,y2) + * 右上(x1,y2) + * 左下(x2,y1) + * 右下(x1,y1) + * */ + val sortedX = routes.sortedBy { point -> point.x } + val sortedY = routes.sortedBy { point -> point.y } + val xMaxPoint = sortedX.last() + val xMinPoint = sortedX.first() + + val yMaxPoint = sortedY.last() + val yMinPoint = sortedY.first() + + /** + * 画出外接矩形 + * */ + val leftTop = Point(xMinPoint.x, yMinPoint.y) + val rightTop = Point(xMaxPoint.x, yMinPoint.y) + val leftBottom = Point(xMinPoint.x, yMaxPoint.y) + val rightBottom = Point(xMaxPoint.x, yMaxPoint.y) + rectPath.moveTo(leftTop.x, leftTop.y) + rectPath.lineTo(rightTop.x, rightTop.y) + rectPath.lineTo(rightBottom.x, rightBottom.y) + rectPath.lineTo(leftBottom.x, leftBottom.y) + rectPath.lineTo(leftTop.x, leftTop.y) + + /** + * 计算出点的相对位置返回给一体机计算 + * */ + val width = ctx.getScreenWidth() - 14f.dp2px(ctx) + val height = 855 + + /** + * 区域 + * */ + if (region.isNotEmpty()) { + region.clear() + } + region.add(Point(leftTop.x / width, leftTop.y / height)) + region.add(Point(rightTop.x / width, rightTop.y / height)) + region.add(Point(leftBottom.x / width, leftBottom.y / height)) + region.add(Point(rightBottom.x / width, rightBottom.y / height)) + + /** + * 点集合 + * */ + if (points.isNotEmpty()) { + points.clear() + } + points.add(floatArrayOf(leftTop.x / width, leftTop.y / height)) + points.add(floatArrayOf(rightTop.x / width, rightTop.y / height)) + points.add(floatArrayOf(leftBottom.x / width, leftBottom.y / height)) + points.add(floatArrayOf(rightBottom.x / width, rightBottom.y / height)) + } + } + invalidate() + return true + } + + fun clearRoutePath() { + routePath.reset() + rectPath.reset() + routes.clear() + invalidate() + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var region = ArrayList() + + fun getConfirmedRegion(): ArrayList = region + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var points = ArrayList() + + fun getConfirmedPoints(): ArrayList = points +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 5dff823..513db5e 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -151,14 +151,13 @@ fenceTextView.setOnClickListener { BottomActionSheet.Builder() .setContext(this) - .setActionItemTitle(arrayListOf("区域规划", "监控角度", "云台角度")) + .setActionItemTitle(arrayListOf("监控区域", "云台角度")) .setItemTextColor(R.color.mainThemeColor.convertColor(this)) .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { override fun onActionItemClick(position: Int) { when (position) { - 0 -> "" - 1 -> navigatePageTo() - 2 -> navigatePageTo() + 0 -> navigatePageTo() + 1 -> navigatePageTo() } } }).build().show() diff --git a/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt new file mode 100644 index 0000000..d3a8181 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt @@ -0,0 +1,39 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class RegionViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val postResult = MutableLiveData() + + fun postRegion(code: String, color: String, position: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.postRegion(code, color, position) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + postResult.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + it.cause.toString().show(BaseApplication.get()) + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt new file mode 100644 index 0000000..72b7d5e --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt @@ -0,0 +1,136 @@ +package com.casic.br.operationsite.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.operationsite.model.Point +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth + +class VideoRegionView(private val ctx: Context, attrs: AttributeSet) : View(ctx, attrs) { + + private val routePath: Path = Path() + private val rectPath: Path = Path() + private val routePaint: Paint = Paint() + private val borderPaint: Paint = Paint() + private var routes = ArrayList() + + init { + routePaint.isAntiAlias = true + routePaint.color = Color.RED + routePaint.style = Paint.Style.STROKE + routePaint.strokeWidth = 7f //设置线宽 + routePaint.isAntiAlias = true + + borderPaint.isAntiAlias = true + borderPaint.color = Color.BLUE + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = 7f //设置线宽 + borderPaint.isAntiAlias = true + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawPath(routePath, routePaint) + + canvas.drawPath(rectPath, borderPaint) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + + val x = event.x + val y = event.y + routes.add(Point(x, y)) + + when (event.action) { + MotionEvent.ACTION_DOWN -> routePath.moveTo(x, y) + MotionEvent.ACTION_MOVE -> routePath.lineTo(x, y) + MotionEvent.ACTION_UP -> { + routePath.lineTo(x, y) + + /** + * 找出最大的(x1,y1)和最小的(x2,y2) + * + * 左上(x2,y2) + * 右上(x1,y2) + * 左下(x2,y1) + * 右下(x1,y1) + * */ + val sortedX = routes.sortedBy { point -> point.x } + val sortedY = routes.sortedBy { point -> point.y } + val xMaxPoint = sortedX.last() + val xMinPoint = sortedX.first() + + val yMaxPoint = sortedY.last() + val yMinPoint = sortedY.first() + + /** + * 画出外接矩形 + * */ + val leftTop = Point(xMinPoint.x, yMinPoint.y) + val rightTop = Point(xMaxPoint.x, yMinPoint.y) + val leftBottom = Point(xMinPoint.x, yMaxPoint.y) + val rightBottom = Point(xMaxPoint.x, yMaxPoint.y) + rectPath.moveTo(leftTop.x, leftTop.y) + rectPath.lineTo(rightTop.x, rightTop.y) + rectPath.lineTo(rightBottom.x, rightBottom.y) + rectPath.lineTo(leftBottom.x, leftBottom.y) + rectPath.lineTo(leftTop.x, leftTop.y) + + /** + * 计算出点的相对位置返回给一体机计算 + * */ + val width = ctx.getScreenWidth() - 14f.dp2px(ctx) + val height = 855 + + /** + * 区域 + * */ + if (region.isNotEmpty()) { + region.clear() + } + region.add(Point(leftTop.x / width, leftTop.y / height)) + region.add(Point(rightTop.x / width, rightTop.y / height)) + region.add(Point(leftBottom.x / width, leftBottom.y / height)) + region.add(Point(rightBottom.x / width, rightBottom.y / height)) + + /** + * 点集合 + * */ + if (points.isNotEmpty()) { + points.clear() + } + points.add(floatArrayOf(leftTop.x / width, leftTop.y / height)) + points.add(floatArrayOf(rightTop.x / width, rightTop.y / height)) + points.add(floatArrayOf(leftBottom.x / width, leftBottom.y / height)) + points.add(floatArrayOf(rightBottom.x / width, rightBottom.y / height)) + } + } + invalidate() + return true + } + + fun clearRoutePath() { + routePath.reset() + rectPath.reset() + routes.clear() + invalidate() + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var region = ArrayList() + + fun getConfirmedRegion(): ArrayList = region + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var points = ArrayList() + + fun getConfirmedPoints(): ArrayList = points +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml new file mode 100644 index 0000000..e5aafca --- /dev/null +++ b/app/src/main/res/drawable/ic_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 5dff823..513db5e 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -151,14 +151,13 @@ fenceTextView.setOnClickListener { BottomActionSheet.Builder() .setContext(this) - .setActionItemTitle(arrayListOf("区域规划", "监控角度", "云台角度")) + .setActionItemTitle(arrayListOf("监控区域", "云台角度")) .setItemTextColor(R.color.mainThemeColor.convertColor(this)) .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { override fun onActionItemClick(position: Int) { when (position) { - 0 -> "" - 1 -> navigatePageTo() - 2 -> navigatePageTo() + 0 -> navigatePageTo() + 1 -> navigatePageTo() } } }).build().show() diff --git a/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt new file mode 100644 index 0000000..d3a8181 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt @@ -0,0 +1,39 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class RegionViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val postResult = MutableLiveData() + + fun postRegion(code: String, color: String, position: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.postRegion(code, color, position) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + postResult.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + it.cause.toString().show(BaseApplication.get()) + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt new file mode 100644 index 0000000..72b7d5e --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt @@ -0,0 +1,136 @@ +package com.casic.br.operationsite.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.operationsite.model.Point +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth + +class VideoRegionView(private val ctx: Context, attrs: AttributeSet) : View(ctx, attrs) { + + private val routePath: Path = Path() + private val rectPath: Path = Path() + private val routePaint: Paint = Paint() + private val borderPaint: Paint = Paint() + private var routes = ArrayList() + + init { + routePaint.isAntiAlias = true + routePaint.color = Color.RED + routePaint.style = Paint.Style.STROKE + routePaint.strokeWidth = 7f //设置线宽 + routePaint.isAntiAlias = true + + borderPaint.isAntiAlias = true + borderPaint.color = Color.BLUE + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = 7f //设置线宽 + borderPaint.isAntiAlias = true + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawPath(routePath, routePaint) + + canvas.drawPath(rectPath, borderPaint) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + + val x = event.x + val y = event.y + routes.add(Point(x, y)) + + when (event.action) { + MotionEvent.ACTION_DOWN -> routePath.moveTo(x, y) + MotionEvent.ACTION_MOVE -> routePath.lineTo(x, y) + MotionEvent.ACTION_UP -> { + routePath.lineTo(x, y) + + /** + * 找出最大的(x1,y1)和最小的(x2,y2) + * + * 左上(x2,y2) + * 右上(x1,y2) + * 左下(x2,y1) + * 右下(x1,y1) + * */ + val sortedX = routes.sortedBy { point -> point.x } + val sortedY = routes.sortedBy { point -> point.y } + val xMaxPoint = sortedX.last() + val xMinPoint = sortedX.first() + + val yMaxPoint = sortedY.last() + val yMinPoint = sortedY.first() + + /** + * 画出外接矩形 + * */ + val leftTop = Point(xMinPoint.x, yMinPoint.y) + val rightTop = Point(xMaxPoint.x, yMinPoint.y) + val leftBottom = Point(xMinPoint.x, yMaxPoint.y) + val rightBottom = Point(xMaxPoint.x, yMaxPoint.y) + rectPath.moveTo(leftTop.x, leftTop.y) + rectPath.lineTo(rightTop.x, rightTop.y) + rectPath.lineTo(rightBottom.x, rightBottom.y) + rectPath.lineTo(leftBottom.x, leftBottom.y) + rectPath.lineTo(leftTop.x, leftTop.y) + + /** + * 计算出点的相对位置返回给一体机计算 + * */ + val width = ctx.getScreenWidth() - 14f.dp2px(ctx) + val height = 855 + + /** + * 区域 + * */ + if (region.isNotEmpty()) { + region.clear() + } + region.add(Point(leftTop.x / width, leftTop.y / height)) + region.add(Point(rightTop.x / width, rightTop.y / height)) + region.add(Point(leftBottom.x / width, leftBottom.y / height)) + region.add(Point(rightBottom.x / width, rightBottom.y / height)) + + /** + * 点集合 + * */ + if (points.isNotEmpty()) { + points.clear() + } + points.add(floatArrayOf(leftTop.x / width, leftTop.y / height)) + points.add(floatArrayOf(rightTop.x / width, rightTop.y / height)) + points.add(floatArrayOf(leftBottom.x / width, leftBottom.y / height)) + points.add(floatArrayOf(rightBottom.x / width, rightBottom.y / height)) + } + } + invalidate() + return true + } + + fun clearRoutePath() { + routePath.reset() + rectPath.reset() + routes.clear() + invalidate() + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var region = ArrayList() + + fun getConfirmedRegion(): ArrayList = region + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var points = ArrayList() + + fun getConfirmedPoints(): ArrayList = points +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml new file mode 100644 index 0000000..e5aafca --- /dev/null +++ b/app/src/main/res/drawable/ic_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_hikvision.xml b/app/src/main/res/layout/activity_hikvision.xml index 752d62c..8b4f9fc 100644 --- a/app/src/main/res/layout/activity_hikvision.xml +++ b/app/src/main/res/layout/activity_hikvision.xml @@ -20,10 +20,10 @@ android:layout_marginHorizontal="@dimen/dp_7" android:layout_marginTop="@dimen/dp_7" android:background="@drawable/bg_solid_layout_white_radius_10" - android:orientation="vertical"> + android:orientation="horizontal"> + android:orientation="horizontal"> + + + + + + + diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a966f73..84ac96a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -63,6 +63,7 @@ + .reformatFloatArray(): String { + if (this.isEmpty()) return "" + val builder = StringBuilder() + //循环遍历元素,同时得到元素index(下标) + this.forEachIndexed { index, it -> + if (index == this.size - 1) { + builder.append(it.toJson()) + } else { + builder.append(it.toJson()).append(",") + } + } + return builder.toString() +} + +fun addAll(vararg args: String): ArrayList { + val result = ArrayList() + args.forEach { + result.add(it) + } + return result } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/model/Point.kt b/app/src/main/java/com/casic/br/operationsite/model/Point.kt new file mode 100644 index 0000000..fae29c7 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/model/Point.kt @@ -0,0 +1,3 @@ +package com.casic.br.operationsite.model + +data class Point(val x: Float, val y: Float) diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt index a6bcfb4..54093a7 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitService.kt @@ -90,4 +90,10 @@ */ @POST("/worker/add") suspend fun enter(@Header("token") token: String, @Body requestBody: RequestBody): String + + /** + * 提交算法区域 + */ + @POST("/set_position") + suspend fun postRegion(@Body requestBody: RequestBody): String } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt index 7582a1f..eb6a592 100644 --- a/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt +++ b/app/src/main/java/com/casic/br/operationsite/retrofit/RetrofitServiceManager.kt @@ -17,13 +17,19 @@ object RetrofitServiceManager { + private val gson by lazy { Gson() } + private val api by lazy { val httpConfig = SaveKeyValues.getValue( LocaleConstant.DEFAULT_SERVER_CONFIG, LocaleConstant.SERVER_BASE_URL ) as String RetrofitFactory.createRetrofit(httpConfig) } - private val gson by lazy { Gson() } + + private val regionApi by lazy { + val httpConfig = "http://192.168.10.104:5000" + RetrofitFactory.createRetrofit(httpConfig) + } /** * 验证PublicKey @@ -171,4 +177,16 @@ ) return api.enter(AuthenticationHelper.token!!, requestBody) } + + suspend fun postRegion(code: String, color: String, position: String): String { + val param = JsonObject() + param.addProperty("code", code) + param.addProperty("color", color) + param.addProperty("position", position) + + val requestBody = param.toString().toRequestBody( + "application/json;charset=UTF-8".toMediaType() + ) + return regionApi.postRegion(requestBody) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt index f40c29f..9fc0a85 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/HikVisionActivity.kt @@ -13,6 +13,7 @@ import android.util.Log import android.view.SurfaceHolder import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.addAll import com.casic.br.operationsite.extensions.getChannel import com.casic.br.operationsite.extensions.initLayoutImmersionBar import com.casic.br.operationsite.model.DeviceConfigModel @@ -26,6 +27,7 @@ import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.convertColor import com.pengxh.kt.lite.extensions.getSystemService +import com.pengxh.kt.lite.extensions.navigatePageTo import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelController import com.pengxh.kt.lite.widget.dialog.BottomActionSheet @@ -208,6 +210,10 @@ } } }) + + videoRegionLayout.setOnClickListener { + navigatePageTo(addAll(hostModel.host, hostModel.port.toString())) + } } private val networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt new file mode 100644 index 0000000..33f6970 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/view/VideoRegionActivity.kt @@ -0,0 +1,203 @@ +package com.casic.br.operationsite.view + +import android.graphics.PixelFormat +import android.util.Log +import android.view.SurfaceHolder +import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.R +import com.casic.br.operationsite.extensions.getChannel +import com.casic.br.operationsite.extensions.initLayoutImmersionBar +import com.casic.br.operationsite.extensions.reformatFloatArray +import com.casic.br.operationsite.utils.LocaleConstant +import com.casic.br.operationsite.utils.hk.MessageCodeHub +import com.casic.br.operationsite.utils.hk.SDKGuider +import com.casic.br.operationsite.vm.RegionViewModel +import com.gyf.immersionbar.ImmersionBar +import com.hikvision.netsdk.NET_DVR_PREVIEWINFO +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import kotlinx.android.synthetic.main.activity_video_region.* +import kotlinx.android.synthetic.main.include_base_title.leftBackView +import kotlinx.android.synthetic.main.include_base_title.titleView +import kotlinx.android.synthetic.main.include_option_title.* + +class VideoRegionActivity : KotlinBaseActivity(), SurfaceHolder.Callback { + + private val kTag = "VideoRegionActivity" + private val context = this@VideoRegionActivity + private var previewHandle = -1 + private var selectChannel = -1 + private var returnUserID = -1 + private var aChannelNum = 0 + private var startAChannel = 0 + private var dChannelNum = 0 + private var startDChannel = 0 + private var isPreviewSuccess = false + private lateinit var params: ArrayList + private lateinit var regionViewModel: RegionViewModel + + override fun initData() { + params = intent.getStringArrayListExtra(Constant.INTENT_PARAM)!! + + regionViewModel = ViewModelProvider(this)[RegionViewModel::class.java] + regionViewModel.postResult.observe(this) { + if (it.code == 200) { + "区域配置成功".show(this) + finish() + } + } + } + + override fun initEvent() { + openCameraButton.setOnClickListener { + openHikVisionCamera(params[0], params[1]) + } + + closeCameraButton.setOnClickListener { + if (!SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle)) { + return@setOnClickListener + } + previewHandle = -1 + isPreviewSuccess = false + + regionView.clearRoutePath() + } + + configButton.setOnClickListener { + val region = regionView.getConfirmedPoints() + val data = region.reformatFloatArray() + + //发送数据的时候需要断开视频 + regionViewModel.postRegion("11,12", "#FF0000", data) + } + } + + override fun initLayoutView(): Int = R.layout.activity_video_region + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(false) + .statusBarColor(R.color.mainThemeColor).init() + initLayoutImmersionBar(rootView) + + leftBackView.setOnClickListener { finish() } + titleView.text = "区域规划" + rightOptionView.text = "重画" + rightOptionView.setOnClickListener { + regionView.clearRoutePath() + } + } + + private fun openHikVisionCamera(host: String, port: String) { + val deviceItem = SDKGuider.g_sdkGuider.m_comDMGuider.DeviceItem() + deviceItem.m_szDevName = "" + deviceItem.m_struNetInfo = SDKGuider.g_sdkGuider.m_comDMGuider.DevNetInfo( + host, port, LocaleConstant.HK_NET_USERNAME, LocaleConstant.HK_NET_PASSWORD + ) + if (deviceItem.m_szDevName.isEmpty()) { + deviceItem.m_szDevName = deviceItem.m_struNetInfo.m_szIp + } + + val loginV40Jna = SDKGuider.g_sdkGuider.m_comDMGuider.login_v40_jna( + deviceItem.m_szDevName, deviceItem.m_struNetInfo + ) + if (loginV40Jna) { + //配置设备通道 + try { + val deviceInfo = SDKGuider.g_sdkGuider.m_comDMGuider.devList[0] + returnUserID = deviceInfo.m_lUserID + + aChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byChanNum.toInt() + startAChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + dChannelNum = deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byIPChanNum + + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byHighDChanNum * 256 + startDChannel = + deviceInfo.m_struDeviceInfoV40_jna.struDeviceV30.byStartChan.toInt() + + var iAnalogStartChan = startAChannel + var iDigitalStartChan = startDChannel + + val channelList = ArrayList() + + for (i in 0 until aChannelNum) { + channelList.add("ACamera_$iAnalogStartChan") + iAnalogStartChan++ + } + + for (i in 0 until dChannelNum) { + channelList.add("DCamera_$iDigitalStartChan") + iDigitalStartChan++ + } + selectChannel = Integer.valueOf(channelList[0].getChannel()) + + val streamList = ArrayList() + streamList.add("main_stream") + streamList.add("sub_stream") + streamList.add("third_stream") + + //开始预览 + if (previewHandle != -1) { + SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_Stop_jni(previewHandle) + } + val strutPlayInfo = NET_DVR_PREVIEWINFO() + strutPlayInfo.lChannel = selectChannel + strutPlayInfo.dwStreamType = 1 + strutPlayInfo.bBlocked = 1 + strutPlayInfo.hHwnd = videoSurfaceView.holder + previewHandle = SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlay_V40_jni( + returnUserID, strutPlayInfo, null + ) + if (previewHandle < 0) { + Log.d( + kTag, + "configDevice: NET_DVR_RealPlay_V40 fail, Err:${MessageCodeHub.getErrorCode()}" + ) + return + } + isPreviewSuccess = true + } catch (e: IndexOutOfBoundsException) { + e.printStackTrace() + } + } + } + + override fun surfaceCreated(holder: SurfaceHolder) { + videoSurfaceView.holder.setFormat(PixelFormat.TRANSLUCENT) + if (-1 == previewHandle) { + return + } + val surface = holder.surface + if (surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, holder + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } + + override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { + + } + + override fun surfaceDestroyed(holder: SurfaceHolder) { + if (-1 == previewHandle) { + return + } + if (holder.surface.isValid) { + if (-1 == SDKGuider.g_sdkGuider.m_comPreviewGuider.RealPlaySurfaceChanged_jni( + previewHandle, 0, null + ) + ) { + Log.d(kTag, "surfaceCreated: ${MessageCodeHub.getErrorCode()}") + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt index 5dff823..513db5e 100644 --- a/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt +++ b/app/src/main/java/com/casic/br/operationsite/view/WorkSiteTabActivity.kt @@ -151,14 +151,13 @@ fenceTextView.setOnClickListener { BottomActionSheet.Builder() .setContext(this) - .setActionItemTitle(arrayListOf("区域规划", "监控角度", "云台角度")) + .setActionItemTitle(arrayListOf("监控区域", "云台角度")) .setItemTextColor(R.color.mainThemeColor.convertColor(this)) .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { override fun onActionItemClick(position: Int) { when (position) { - 0 -> "" - 1 -> navigatePageTo() - 2 -> navigatePageTo() + 0 -> navigatePageTo() + 1 -> navigatePageTo() } } }).build().show() diff --git a/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt new file mode 100644 index 0000000..d3a8181 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/vm/RegionViewModel.kt @@ -0,0 +1,39 @@ +package com.casic.br.operationsite.vm + +import androidx.lifecycle.MutableLiveData +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.extensions.separateResponseCode +import com.casic.br.operationsite.extensions.toErrorMessage +import com.casic.br.operationsite.model.CommonResultModel +import com.casic.br.operationsite.retrofit.RetrofitServiceManager +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.pengxh.kt.lite.extensions.launch +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.vm.BaseViewModel +import com.pengxh.kt.lite.vm.LoadState + +class RegionViewModel : BaseViewModel() { + + private val gson by lazy { Gson() } + val postResult = MutableLiveData() + + fun postRegion(code: String, color: String, position: String) = launch({ + loadState.value = LoadState.Loading + val response = RetrofitServiceManager.postRegion(code, color, position) + val responseCode = response.separateResponseCode() + if (responseCode == 200) { + loadState.value = LoadState.Success + postResult.value = gson.fromJson( + response, object : TypeToken() {}.type + ) + } else { + loadState.value = LoadState.Fail + response.toErrorMessage().show(BaseApplication.get()) + } + }, { + loadState.value = LoadState.Fail + it.cause.toString().show(BaseApplication.get()) + it.printStackTrace() + }) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt new file mode 100644 index 0000000..72b7d5e --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/VideoRegionView.kt @@ -0,0 +1,136 @@ +package com.casic.br.operationsite.widgets + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path +import android.util.AttributeSet +import android.view.MotionEvent +import android.view.View +import com.casic.br.operationsite.model.Point +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.extensions.getScreenWidth + +class VideoRegionView(private val ctx: Context, attrs: AttributeSet) : View(ctx, attrs) { + + private val routePath: Path = Path() + private val rectPath: Path = Path() + private val routePaint: Paint = Paint() + private val borderPaint: Paint = Paint() + private var routes = ArrayList() + + init { + routePaint.isAntiAlias = true + routePaint.color = Color.RED + routePaint.style = Paint.Style.STROKE + routePaint.strokeWidth = 7f //设置线宽 + routePaint.isAntiAlias = true + + borderPaint.isAntiAlias = true + borderPaint.color = Color.BLUE + borderPaint.style = Paint.Style.STROKE + borderPaint.strokeWidth = 7f //设置线宽 + borderPaint.isAntiAlias = true + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawPath(routePath, routePaint) + + canvas.drawPath(rectPath, borderPaint) + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + + val x = event.x + val y = event.y + routes.add(Point(x, y)) + + when (event.action) { + MotionEvent.ACTION_DOWN -> routePath.moveTo(x, y) + MotionEvent.ACTION_MOVE -> routePath.lineTo(x, y) + MotionEvent.ACTION_UP -> { + routePath.lineTo(x, y) + + /** + * 找出最大的(x1,y1)和最小的(x2,y2) + * + * 左上(x2,y2) + * 右上(x1,y2) + * 左下(x2,y1) + * 右下(x1,y1) + * */ + val sortedX = routes.sortedBy { point -> point.x } + val sortedY = routes.sortedBy { point -> point.y } + val xMaxPoint = sortedX.last() + val xMinPoint = sortedX.first() + + val yMaxPoint = sortedY.last() + val yMinPoint = sortedY.first() + + /** + * 画出外接矩形 + * */ + val leftTop = Point(xMinPoint.x, yMinPoint.y) + val rightTop = Point(xMaxPoint.x, yMinPoint.y) + val leftBottom = Point(xMinPoint.x, yMaxPoint.y) + val rightBottom = Point(xMaxPoint.x, yMaxPoint.y) + rectPath.moveTo(leftTop.x, leftTop.y) + rectPath.lineTo(rightTop.x, rightTop.y) + rectPath.lineTo(rightBottom.x, rightBottom.y) + rectPath.lineTo(leftBottom.x, leftBottom.y) + rectPath.lineTo(leftTop.x, leftTop.y) + + /** + * 计算出点的相对位置返回给一体机计算 + * */ + val width = ctx.getScreenWidth() - 14f.dp2px(ctx) + val height = 855 + + /** + * 区域 + * */ + if (region.isNotEmpty()) { + region.clear() + } + region.add(Point(leftTop.x / width, leftTop.y / height)) + region.add(Point(rightTop.x / width, rightTop.y / height)) + region.add(Point(leftBottom.x / width, leftBottom.y / height)) + region.add(Point(rightBottom.x / width, rightBottom.y / height)) + + /** + * 点集合 + * */ + if (points.isNotEmpty()) { + points.clear() + } + points.add(floatArrayOf(leftTop.x / width, leftTop.y / height)) + points.add(floatArrayOf(rightTop.x / width, rightTop.y / height)) + points.add(floatArrayOf(leftBottom.x / width, leftBottom.y / height)) + points.add(floatArrayOf(rightBottom.x / width, rightBottom.y / height)) + } + } + invalidate() + return true + } + + fun clearRoutePath() { + routePath.reset() + rectPath.reset() + routes.clear() + invalidate() + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var region = ArrayList() + + fun getConfirmedRegion(): ArrayList = region + + /////////////////////////////////////////////////////////////////////////////////////////////// + + private var points = ArrayList() + + fun getConfirmedPoints(): ArrayList = points +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_right.xml b/app/src/main/res/drawable/ic_right.xml new file mode 100644 index 0000000..e5aafca --- /dev/null +++ b/app/src/main/res/drawable/ic_right.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_hikvision.xml b/app/src/main/res/layout/activity_hikvision.xml index 752d62c..8b4f9fc 100644 --- a/app/src/main/res/layout/activity_hikvision.xml +++ b/app/src/main/res/layout/activity_hikvision.xml @@ -20,10 +20,10 @@ android:layout_marginHorizontal="@dimen/dp_7" android:layout_marginTop="@dimen/dp_7" android:background="@drawable/bg_solid_layout_white_radius_10" - android:orientation="vertical"> + android:orientation="horizontal"> + android:orientation="horizontal"> + + + + + + + diff --git a/app/src/main/res/layout/activity_video_region.xml b/app/src/main/res/layout/activity_video_region.xml new file mode 100644 index 0000000..35a7996 --- /dev/null +++ b/app/src/main/res/layout/activity_video_region.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + +