diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml new file mode 100644 index 0000000..471e378 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml new file mode 100644 index 0000000..471e378 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_text_tag_blue.xml b/app/src/main/res/drawable/bg_text_tag_blue.xml new file mode 100644 index 0000000..5878a74 --- /dev/null +++ b/app/src/main/res/drawable/bg_text_tag_blue.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml new file mode 100644 index 0000000..471e378 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_text_tag_blue.xml b/app/src/main/res/drawable/bg_text_tag_blue.xml new file mode 100644 index 0000000..5878a74 --- /dev/null +++ b/app/src/main/res/drawable/bg_text_tag_blue.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection.xml b/app/src/main/res/layout/activity_inspection.xml index 710ad8f..32f528a 100644 --- a/app/src/main/res/layout/activity_inspection.xml +++ b/app/src/main/res/layout/activity_inspection.xml @@ -77,6 +77,7 @@ android:text="设备状态" /> +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml new file mode 100644 index 0000000..471e378 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_text_tag_blue.xml b/app/src/main/res/drawable/bg_text_tag_blue.xml new file mode 100644 index 0000000..5878a74 --- /dev/null +++ b/app/src/main/res/drawable/bg_text_tag_blue.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection.xml b/app/src/main/res/layout/activity_inspection.xml index 710ad8f..32f528a 100644 --- a/app/src/main/res/layout/activity_inspection.xml +++ b/app/src/main/res/layout/activity_inspection.xml @@ -77,6 +77,7 @@ android:text="设备状态" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml new file mode 100644 index 0000000..471e378 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_text_tag_blue.xml b/app/src/main/res/drawable/bg_text_tag_blue.xml new file mode 100644 index 0000000..5878a74 --- /dev/null +++ b/app/src/main/res/drawable/bg_text_tag_blue.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection.xml b/app/src/main/res/layout/activity_inspection.xml index 710ad8f..32f528a 100644 --- a/app/src/main/res/layout/activity_inspection.xml +++ b/app/src/main/res/layout/activity_inspection.xml @@ -77,6 +77,7 @@ android:text="设备状态" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popu_line_chart_marker.xml b/app/src/main/res/layout/popu_line_chart_marker.xml new file mode 100644 index 0000000..9fd4485 --- /dev/null +++ b/app/src/main/res/layout/popu_line_chart_marker.xml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt new file mode 100644 index 0000000..627f972 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/adapter/AlarmListAdapter.kt @@ -0,0 +1,35 @@ +package com.casic.br.ktd.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.ktd.R +import com.casic.br.ktd.model.AlarmPointModel + +class AlarmListAdapter( + context: Context, private val dataRows: MutableList +) : RecyclerView.Adapter() { + + private var layoutInflater: LayoutInflater = LayoutInflater.from(context) + + override fun getItemCount(): Int = dataRows.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ItemViewHolder( + layoutInflater.inflate(R.layout.item_alarm_rv_l, parent, false) + ) + + override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { + //绑定数据 + val rowsBean = dataRows[position] + holder.alarmTimeView.text = rowsBean.alarmTime + holder.alarmValueView.text = rowsBean.alarmValue.toString() + } + + inner class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) { + var alarmTimeView: TextView = view.findViewById(R.id.alarmTimeView) + var alarmValueView: TextView = view.findViewById(R.id.alarmValueView) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt new file mode 100644 index 0000000..0e4ea5f --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/extensions/LineChart.kt @@ -0,0 +1,66 @@ +package com.casic.br.ktd.extensions + +import android.graphics.Color +import android.graphics.Paint +import com.casic.br.ktd.utils.LocaleConstant +import com.github.mikephil.charting.animation.Easing +import com.github.mikephil.charting.charts.Chart +import com.github.mikephil.charting.charts.LineChart +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.YAxis +import com.pengxh.kt.lite.extensions.dp2px +import com.pengxh.kt.lite.utils.SaveKeyValues + + +fun LineChart.init() { + this.setNoDataText("无数据,无法渲染...") + this.setNoDataTextColor(Color.RED) + this.getPaint(Chart.PAINT_INFO).textSize = 12f.dp2px(this.context).toFloat() + this.setDrawGridBackground(false) + this.setDrawBorders(false) + this.animateY(1200, Easing.EaseInOutQuad) + //设置样式 + val rightAxis: YAxis = this.axisRight + //设置图表右边的y轴禁用 + rightAxis.isEnabled = false + val leftAxis: YAxis = this.axisLeft + leftAxis.axisMinimum = 0f + leftAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + leftAxis.setDrawZeroLine(true) + //添加限制线 + val threshold = SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + val limitLine = LimitLine(threshold.toFloat(), "阈值:${threshold}ppm·m") + limitLine.lineWidth = 1f + limitLine.lineColor = Color.GREEN + //允许在虚线模式下绘制(线段长度,分隔长度,偏移量) + limitLine.enableDashedLine(10f, 10f, 0f) + //设置限制线标签的位置 + limitLine.labelPosition = LimitLine.LimitLabelPosition.RIGHT_TOP + limitLine.textSize = 10f + limitLine.textStyle = Paint.Style.FILL + //添加限制线 + leftAxis.addLimitLine(limitLine) + //只显示一条阈值线 + if (leftAxis.limitLines.size > 1) { + leftAxis.limitLines.removeAt(0) + } + //是否隐藏限制线/true_否,false_是 + limitLine.isEnabled = true + this.isScaleXEnabled = true //X轴可缩放 + this.isScaleYEnabled = false //Y轴不可缩放 + //设置x轴 + val xAxis: XAxis = this.xAxis + xAxis.textSize = 10f + xAxis.setDrawLabels(true) //绘制标签 指x轴上的对应数值 + xAxis.setDrawAxisLine(true) //是否绘制轴线 + xAxis.setDrawGridLines(true) //设置x轴上每个点对应的线 + xAxis.granularity = 1f //禁止放大后x轴标签重绘 + xAxis.position = XAxis.XAxisPosition.BOTTOM + this.extraTopOffset = 20f + this.extraBottomOffset = 10f //解决X轴显示不完全问题 + //去掉描述 + this.description.isEnabled = false + //去掉图例 + this.legend.isEnabled = false +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/extensions/String.kt b/app/src/main/java/com/casic/br/ktd/extensions/String.kt index 0a94d3e..6efd1a3 100644 --- a/app/src/main/java/com/casic/br/ktd/extensions/String.kt +++ b/app/src/main/java/com/casic/br/ktd/extensions/String.kt @@ -6,10 +6,12 @@ import com.google.gson.Gson import com.google.gson.reflect.TypeToken import com.pengxh.kt.lite.extensions.createCompressImageDir +import com.pengxh.kt.lite.extensions.dateToTimestamp import org.json.JSONObject import top.zibin.luban.Luban import top.zibin.luban.OnCompressListener import java.io.File +import java.text.SimpleDateFormat import java.util.* import java.util.regex.Pattern @@ -42,6 +44,12 @@ return m.replaceAll("").trim { it <= ' ' } } +fun String.dateToTime(): String { + val timestamp = this.dateToTimestamp() + val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.CHINA) + return dateFormat.format(Date(timestamp)) +} + fun String.compressImage(context: Context, listener: OnImageCompressListener) { Luban.with(context) .load(this) diff --git a/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java new file mode 100644 index 0000000..9f03944 --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/AlarmPointModel.java @@ -0,0 +1,106 @@ +package com.casic.br.ktd.model; + +public class AlarmPointModel { + private String uuid; + private String alarmTime; + private double alarmLat; + private double alarmLng; + /** + * ppm·m + */ + private int alarmValue; + /** + * 摄像头水平角度 + */ + private double horizontal; + /** + * 摄像头垂直角度 + */ + private double vertical; + /** + * 报警阈值 + */ + private int threshold; + private float carSpeed; + private String videoPath; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getAlarmTime() { + return alarmTime; + } + + public void setAlarmTime(String alarmTime) { + this.alarmTime = alarmTime; + } + + public double getAlarmLat() { + return alarmLat; + } + + public void setAlarmLat(double alarmLat) { + this.alarmLat = alarmLat; + } + + public double getAlarmLng() { + return alarmLng; + } + + public void setAlarmLng(double alarmLng) { + this.alarmLng = alarmLng; + } + + public int getAlarmValue() { + return alarmValue; + } + + public void setAlarmValue(int alarmValue) { + this.alarmValue = alarmValue; + } + + public double getHorizontal() { + return horizontal; + } + + public void setHorizontal(double horizontal) { + this.horizontal = horizontal; + } + + public double getVertical() { + return vertical; + } + + public void setVertical(double vertical) { + this.vertical = vertical; + } + + public int getThreshold() { + return threshold; + } + + public void setThreshold(int threshold) { + this.threshold = threshold; + } + + public float getCarSpeed() { + return carSpeed; + } + + public void setCarSpeed(float carSpeed) { + this.carSpeed = carSpeed; + } + + public String getVideoPath() { + return videoPath; + } + + public void setVideoPath(String videoPath) { + this.videoPath = videoPath; + } +} diff --git a/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt new file mode 100644 index 0000000..0a6a8fd --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/model/ChartPointModel.kt @@ -0,0 +1,3 @@ +package com.casic.br.ktd.model + +data class ChartPointModel(var dataTime: String, var dataValue: Int) diff --git a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt index 4af74cf..17daf35 100644 --- a/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt +++ b/app/src/main/java/com/casic/br/ktd/netty/SocketManager.kt @@ -1,11 +1,14 @@ package com.casic.br.ktd.netty import android.util.Log +import com.casic.br.ktd.base.BaseApplication import com.casic.br.ktd.extensions.covertAngleValue import com.casic.br.ktd.extensions.covertDataValue import com.casic.br.ktd.extensions.toUnsignedByteArray import com.casic.br.ktd.model.SensorDataModel -import java.util.* +import com.casic.br.ktd.utils.LocaleConstant +import com.pengxh.kt.lite.extensions.toJson +import com.pengxh.kt.lite.utils.BroadcastManager class SocketManager private constructor() : ISocketListener { @@ -28,7 +31,7 @@ } override fun onMessageResponse(data: ByteArray?) { - Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) +// Log.d(kTag, "onMessageResponse ===> " + Arrays.toString(data)) /** * 0xFF,0x01, * 0x01,0x37,0xE6, 甲烷浓度值(数据码1* 65536 + 数据码2 * 256 + 数据码3) @@ -82,9 +85,9 @@ dataModel.vertical = verticalBytes.covertAngleValue() //{"horizontal":26.21,"laser":45621,"methane":79846,"methaneState":"正常","vertical":13.91} -// BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( -// LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() -// ) + BroadcastManager.obtainInstance(BaseApplication.get()).sendBroadcast( + LocaleConstant.ACTION_UPDATE_DATA, dataModel.toJson() + ) } } diff --git a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt index 6579d57..0dd119e 100644 --- a/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/ktd/utils/LocaleConstant.kt @@ -50,6 +50,12 @@ const val DEV_NET_IP = "192.168.1.21" const val DEV_NET_PORT = "8000" + //BroadcastReceiver Action + const val ACTION_UPDATE_DATA = "update" + + //SharedPreferences + const val ALARM_THRESHOLD = "alarmThreshold" + /** * ============================================================================================= * ByteArray diff --git a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt index 6f2d43c..5c92e4a 100644 --- a/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt +++ b/app/src/main/java/com/casic/br/ktd/view/InspectionActivity.kt @@ -1,6 +1,10 @@ package com.casic.br.ktd.view import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.graphics.Color import android.graphics.PixelFormat import android.location.Location import android.os.Bundle @@ -15,19 +19,32 @@ import com.amap.api.maps.model.CameraPosition import com.amap.api.maps.model.LatLng import com.casic.br.ktd.R -import com.casic.br.ktd.extensions.createHorizontalCommand -import com.casic.br.ktd.extensions.createVerticalCommand -import com.casic.br.ktd.extensions.getChannel +import com.casic.br.ktd.adapter.AlarmListAdapter +import com.casic.br.ktd.extensions.* +import com.casic.br.ktd.model.AlarmPointModel +import com.casic.br.ktd.model.ChartPointModel +import com.casic.br.ktd.model.SensorDataModel import com.casic.br.ktd.netty.SocketManager import com.casic.br.ktd.utils.LocaleConstant import com.casic.br.ktd.utils.LocationHelper import com.casic.br.ktd.widgets.AlertControlDialog +import com.casic.br.ktd.widgets.LineChartMarkerView import com.casic.br.ktd.widgets.SteeringWheelView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.data.LineData +import com.github.mikephil.charting.data.LineDataSet +import com.github.mikephil.charting.interfaces.datasets.ILineDataSet +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken import com.gyf.immersionbar.ImmersionBar import com.hikvision.netsdk.* import com.pengxh.kt.lite.base.KotlinBaseActivity import com.pengxh.kt.lite.extensions.createImageFileDir import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.extensions.timestampToCompleteDate +import com.pengxh.kt.lite.utils.BroadcastManager +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler import hcnetsdk.sdkhub.MessageCodeHub import hcnetsdk.sdkhub.SDKGuider @@ -46,6 +63,7 @@ private val speedTimer by lazy { Timer() } private val tcpTimer by lazy { Timer() } private val decimalFormat by lazy { DecimalFormat("##0.0") } + private val gson by lazy { Gson() } private var isLoginSuccess = false private var previewHandle = -1 private var selectChannel = -1 @@ -68,7 +86,19 @@ //车速 private var speed: Float = 0.0f + + //趋势线起点X坐标 + private var i = 0 + private var chartBeans: MutableList = ArrayList() + private val xAxisLabels: MutableList = ArrayList() + private val strengthEntries: MutableList = ArrayList() + private var lineDataSets: MutableList = ArrayList() + + //最新报警列表 + private var dataBeans: MutableList = ArrayList() + private var isLoadMore = false private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var alarmAdapter: AlarmListAdapter private fun setDeviceConfig() { Log.d(kTag, "setDeviceConfig => 配置设备IP和端口") @@ -97,9 +127,17 @@ override fun initData(savedInstanceState: Bundle?) { weakReferenceHandler = WeakReferenceHandler(this) + /** + * 配置云台设备 + * */ setDeviceConfig() /** + * 监听浓度数据 + * */ + setDataListener() + + /** * 手机GPS定位 * */ val converter = CoordinateConverter(this) @@ -554,13 +592,121 @@ override fun onDestroy() { super.onDestroy() stopPreview() + BroadcastManager.obtainInstance(this).destroy(LocaleConstant.ACTION_UPDATE_DATA) } override fun handleMessage(msg: Message): Boolean { when (msg.what) { //停止旋转 2023071401 -> hkSDK.NET_DVR_PTZControl(previewHandle, PTZCommand.PAN_AUTO, 1) + 2023071402 -> { + if (isLoadMore) { + alarmAdapter.notifyDataSetChanged() + //每次更新数据都将RecyclerView滑到最底部 + alarmRecyclerView.scrollToPosition(alarmAdapter.itemCount - 1) + } else { + alarmAdapter = AlarmListAdapter(this, dataBeans) + alarmRecyclerView.adapter = alarmAdapter + + /** + * 折线图初始化 + * */ + methaneLineChart.init() + //添加自定义Marker + val markerView = LineChartMarkerView(this) + markerView.chartView = methaneLineChart + markerView.setXAxisDate(xAxisLabels) + methaneLineChart.marker = markerView + } + } } return true } + + private fun setDataListener() { + BroadcastManager.obtainInstance(this).addAction(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null || context == null) { + return + } + val deviceJson = intent.getStringExtra(Constant.BROADCAST_INTENT_DATA_KEY) + val dataModel = gson.fromJson( + deviceJson, object : TypeToken() {}.type + ) + sensorStateView.text = dataModel.methaneState + if (dataModel.methaneState == "正常") { + sensorStateView.setTextColor(Color.GREEN) + } else { + sensorStateView.setTextColor(Color.RED) + } + val degree = resources.getString(R.string.degree) + horizontalDegreeView.text = String.format("${dataModel.horizontal}$degree") + verticalDegreeView.text = String.format("${dataModel.vertical}$degree") + gasConcentrationView.text = String.format("${dataModel.methane}ppm·m") + + /***折线图***Start*******************************************************************/ + val chartPointModel = ChartPointModel("", 0) + chartPointModel.dataTime = System.currentTimeMillis().timestampToCompleteDate() + chartPointModel.dataValue = dataModel.methane + chartBeans.add(chartPointModel) + xAxisLabels.add(chartBeans.last().dataTime.dateToTime()) + + //浓度线 + strengthEntries.add( + Entry(i++.toFloat(), chartBeans.last().dataValue.toFloat(), "浓度") + ) + + //设置数据 + val dataSet = LineDataSet(strengthEntries, "") + dataSet.setDrawCircles(false) + //线条颜色 + dataSet.color = Color.RED + dataSet.mode = LineDataSet.Mode.CUBIC_BEZIER + lineDataSets.add(dataSet) + val lineData = LineData(lineDataSets) + lineData.setDrawValues(false) + + //解决折线点太多导致卡顿问题 + if (lineData.entryCount > 100) { + lineData.removeDataSet(0) + lineData.notifyDataChanged() + } + methaneLineChart.data = lineData + methaneLineChart.invalidate() + /***折线图***End*******************************************************************/ + //判断浓度阈值 + val threshold = + SaveKeyValues.getValue(LocaleConstant.ALARM_THRESHOLD, "1000") as String + if (dataModel.methane > threshold.toInt()) { + //记录报警 + if (latlngs.isNotEmpty()) { + val model = AlarmPointModel() + model.uuid = UUID.randomUUID().toString() + val time = System.currentTimeMillis() + model.alarmTime = time.timestampToCompleteDate() + model.alarmLat = latlngs.last().latitude + model.alarmLng = latlngs.last().longitude + model.alarmValue = dataModel.methane + model.horizontal = dataModel.horizontal + model.vertical = dataModel.vertical + model.threshold = threshold.toInt() + model.carSpeed = speed + //因为报警时间比较短,ms级别,所以存入报警数据时,视频还未采集完毕,所以,在视频采集完毕之后再去判断在这个时间范围的报警数据 + model.videoPath = "" + + dataBeans.add(model) + //设置列表固定只显示10条数据 + if (dataBeans.size > 10) { + dataBeans.removeAt(0) + } + + isLoadMore = true + weakReferenceHandler.sendEmptyMessage(2023071402) + } else { + Log.d(kTag, "onReceive => 无位置数据,报警数据无效") + } + } + } + }, LocaleConstant.ACTION_UPDATE_DATA) + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt new file mode 100644 index 0000000..5017dec --- /dev/null +++ b/app/src/main/java/com/casic/br/ktd/widgets/LineChartMarkerView.kt @@ -0,0 +1,37 @@ +package com.casic.br.ktd.widgets + +import android.content.Context +import android.widget.TextView +import com.casic.br.ktd.R +import com.github.mikephil.charting.components.MarkerView +import com.github.mikephil.charting.data.Entry +import com.github.mikephil.charting.highlight.Highlight +import com.github.mikephil.charting.utils.MPPointF + +class LineChartMarkerView(context: Context?) : + MarkerView(context, R.layout.popu_line_chart_marker) { + + private val dayView: TextView = findViewById(R.id.dayView) + private val dataView: TextView = findViewById(R.id.dataView) + private var xAxisDate: MutableList = ArrayList() + + fun setXAxisDate(date: MutableList) { + this.xAxisDate = date + } + + //每次重绘,会调用此方法刷新数据 + override fun refreshContent(e: Entry, highlight: Highlight) { + super.refreshContent(e, highlight) + try { + dataView.text = String.format("浓度:${e.y.toInt()}ppm·m") + dayView.text = xAxisDate[(e.x).toInt()] + } catch (e1: Exception) { + e1.printStackTrace() + } + super.refreshContent(e, highlight) + } + + override fun getOffset(): MPPointF { + return MPPointF((-(width shr 1)).toFloat(), (-height).toFloat()) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml new file mode 100644 index 0000000..471e378 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_blue_5.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_text_tag_blue.xml b/app/src/main/res/drawable/bg_text_tag_blue.xml new file mode 100644 index 0000000..5878a74 --- /dev/null +++ b/app/src/main/res/drawable/bg_text_tag_blue.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_inspection.xml b/app/src/main/res/layout/activity_inspection.xml index 710ad8f..32f528a 100644 --- a/app/src/main/res/layout/activity_inspection.xml +++ b/app/src/main/res/layout/activity_inspection.xml @@ -77,6 +77,7 @@ android:text="设备状态" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/popu_line_chart_marker.xml b/app/src/main/res/layout/popu_line_chart_marker.xml new file mode 100644 index 0000000..9fd4485 --- /dev/null +++ b/app/src/main/res/layout/popu_line_chart_marker.xml @@ -0,0 +1,30 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9926522..e6c5e6b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ 空天地一体化 中国航天科工集团二院二〇三所 暂无数据 + ° 中国航天科工集团二院二〇三所 版权所有 Copyright © 2023 – 2050 Casic. All rights reserved. \ No newline at end of file