实现了双屏摄像头显示,添加了部分代码

This commit is contained in:
zxj
2025-07-31 11:40:56 +08:00
parent 56e0a85a4c
commit de4c5f1c75
86 changed files with 3446 additions and 809 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation("com.licheedev:android-serialport:2.1.5")
// implementation("com.licheedev:android-serialport:2.1.5")
val roomVersion = "2.2.5"
implementation("androidx.room:room-runtime:$roomVersion")
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,148 @@
package com.sw.plate.sdk
import android.util.Log
import com.sw.plate.utils.ThreadUtils
import com.sw.plate.utils.ToastUtils
import com.wabon.wbintelligenthardwaresdk.api.SensorScale
import com.wabon.wbintelligenthardwaresdk.api.SensorScale.OnScaleResult
import kotlinx.coroutines.delay
typealias Callback = (Double) -> Unit
private const val TAG = "SensorScaleUtils"
/**
* 称 传感器计算
*/
object SensorScaleUtils {
const val serialPort = "/dev/ttyS7"
const val baudRate = 115200
private var mSensorScale: SensorScale? = null
private var isOpened: Boolean = false
private var callback: Callback? = {}
private fun init() {
mSensorScale = SensorScale(object : OnScaleResult {
/**
* 读取重量
*/
override fun readWeight(state: Int, value: Double) {
val stateStr = when (state) {
SensorScale.STATE_STABLE -> "稳定"
SensorScale.STATE_UNSTABLE -> "不稳定"
SensorScale.STATE_OVER_WEIGHT -> "量程溢出"
else -> "未知"
}
// Log.d(TAG, "readWeight state = ${stateStr}, weight = $value")
// 只使用稳定值
if (state == SensorScale.STATE_STABLE) {
callback?.invoke(value)
}
}
/**
* 读取鉴别率
*/
override fun readIdentify(rate: Int) {
Log.e(TAG, "readIdentify rate = $rate")
}
override fun fail(errCode: Int) {
Log.e(TAG, "fail code = $errCode")
}
})
}
/**
* 开启称重
* @param autoScale 是否开启自动读取
*/
fun startScale(autoScale: Boolean = true, callback: Callback?) {
if (isOpened) {
startContinuousRead(callback = callback)
return
}
this.callback = callback
init()
mSensorScale?.openScale(serialPort, baudRate) { open ->
isOpened = open
Log.d(TAG, "isOpened = $isOpened")
if (open) {
if (autoScale) {
ThreadUtils.launchOnIo {
delay(1000)
// 打开后需要等待后才能调用,否则会 1001 SDK未初始化
mSensorScale?.startContinuousRead()
}
}
}
}
}
/**
* 开启自动读取重量
*/
fun startContinuousRead(callback: Callback?) {
this.callback = callback
Log.d(TAG, "开启自动读取 = $isOpened")
if (!isOpened) {
return
}
mSensorScale?.startContinuousRead()
}
/**
* 手动读取重量
*/
fun readWeight(callback: Callback?) {
this.callback = callback
Log.d(TAG, "isOpened = $isOpened")
if (!isOpened) {
return
}
mSensorScale?.readWeight()
}
/**
* 零位标定
*/
fun zero() {
if (!isOpened) return
mSensorScale?.zero {
Log.d(TAG, "零位标定操作成功")
ToastUtils.showToast("零位标定操作成功")
}
}
/**
* 去皮置零
*/
fun tare() {
if (!isOpened) return
mSensorScale?.tare {
Log.d(TAG, "去皮置零操作成功")
ToastUtils.showToast("去皮置零操作成功")
}
}
/**
* 停止自动读取重量
*/
fun stopContinuousRead() {
Log.d(TAG, "isOpened = $isOpened")
if (!isOpened) {
return
}
mSensorScale?.stopContinuousRead()
}
/**
* 关闭称重
*/
fun closeScale() {
isOpened = false
mSensorScale?.closeScale()
mSensorScale == null
}
}
@@ -0,0 +1,124 @@
package com.sw.plate.utils
import android.os.Handler
import android.os.Looper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.coroutines.CoroutineContext
/**
* 多功能线程工具类
* 结合协程、Handler和线程池实现线程切换
*/
object ThreadUtils : CoroutineScope {
// 主线程Handler
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
// 后台线程池(IO密集型任务)
private val ioThreadPool: ExecutorService by lazy {
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2)
}
// CPU密集型线程池
private val cpuThreadPool: ExecutorService by lazy {
Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
}
// 协程Job管理
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
// ========== Handler相关方法 ==========
/**
* 在主线程执行任务
* @param delayMillis 延迟时间(毫秒)
*/
fun runOnUiThread(delayMillis: Long = 0, block: () -> Unit) {
if (delayMillis > 0) {
mainHandler.postDelayed(block, delayMillis)
} else {
if (isOnMainThread()) {
block()
} else {
mainHandler.post(block)
}
}
}
/**
* 移除主线程任务
*/
fun removeUiThreadTask(block: () -> Unit) {
mainHandler.removeCallbacks(block)
}
// ========== 线程池相关方法 ==========
/**
* 在IO线程执行任务
*/
fun runOnIoThread(block: () -> Unit) {
ioThreadPool.execute(block)
}
/**
* 在CPU计算线程执行任务
*/
fun runOnCpuThread(block: () -> Unit) {
cpuThreadPool.execute(block)
}
// ========== 协程相关方法 ==========
/**
* 启动协程(默认在主线程)
*/
fun launch(block: suspend CoroutineScope.() -> Unit): Job {
return launch(coroutineContext, block = block)
}
/**
* 在IO线程启动协程
*/
fun launchOnIo(block: suspend CoroutineScope.() -> Unit): Job {
return launch(Dispatchers.IO, block = block)
}
/**
* 切换到主线程(协程环境)
*/
suspend fun <T> switchToMain(block: suspend CoroutineScope.() -> T): T {
return withContext(Dispatchers.Main, block)
}
/**
* 切换到IO线程(协程环境)
*/
suspend fun <T> switchToIo(block: suspend CoroutineScope.() -> T): T {
return withContext(Dispatchers.IO, block)
}
/**
* 是否在主线程
*/
fun isOnMainThread(): Boolean {
return Looper.myLooper() == Looper.getMainLooper()
}
/**
* 释放资源
*/
fun release() {
job.cancel()
ioThreadPool.shutdown()
cpuThreadPool.shutdown()
}
}
@@ -268,16 +268,19 @@ public class RecognizeViewModel extends ViewModel implements RecognizeCallback {
/**
* 初始化引擎
*/
public void init() {
public void init(PreviewConfig previewConfig1) {
Context context = App.getContext();
boolean switchCamera = ConfigUtil.isSwitchCamera(context);
previewConfig = new PreviewConfig(
switchCamera ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK,
switchCamera ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT,
Integer.parseInt(ConfigUtil.getRgbCameraAdditionalRotation(context)),
Integer.parseInt(ConfigUtil.getIrCameraAdditionalRotation(context))
);
if (previewConfig1 != null) {
previewConfig = previewConfig1;
} else {
boolean switchCamera = ConfigUtil.isSwitchCamera(context);
previewConfig = new PreviewConfig(
switchCamera ? Camera.CameraInfo.CAMERA_FACING_FRONT : Camera.CameraInfo.CAMERA_FACING_BACK,
switchCamera ? Camera.CameraInfo.CAMERA_FACING_BACK : Camera.CameraInfo.CAMERA_FACING_FRONT,
Integer.parseInt(ConfigUtil.getRgbCameraAdditionalRotation(context)),
Integer.parseInt(ConfigUtil.getIrCameraAdditionalRotation(context))
);
}
// 填入在设置界面设置好的配置信息
boolean enableLive = !ConfigUtil.getLivenessDetectType(context).equals(context.getString(R.string.value_liveness_type_disable));
boolean enableFaceQualityDetect = ConfigUtil.isEnableImageQualityDetect(context);
@@ -1,39 +0,0 @@
package com.sw.plate.utils.comn;
/**
* 串口设备
*/
public class Device {
private String path;
private String baudrate;
public Device() {
}
public Device(String path, String baudrate) {
this.path = path;
this.baudrate = baudrate;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getBaudrate() {
return baudrate;
}
public void setBaudrate(String baudrate) {
this.baudrate = baudrate;
}
@Override
public String toString() {
return "Device{" + "path='" + path + '\'' + ", baudrate='" + baudrate + '\'' + '}';
}
}
@@ -1,37 +0,0 @@
package com.sw.plate.utils.comn;
import static com.sw.plate.utils.CabinetLockCommand.generateOpenCommand;
import android.serialport.SerialPort;
public class SerialApi {
private static String path = "/dev/ttyS2";
private static int speed = 19200;
private static SerialPortManager serialPortManager;
private static SerialPort serialPort;
public static void init() {
serialPortManager = SerialPortManager.instance();
serialPort = serialPortManager.open(new Device(path, String.valueOf(speed)));
// if (serialPort == null) {
// ToastUtils.showToast("打开串口失败");
// }
}
/**
* 开柜
*
* @param boxNumber
* @param callback
*/
public static void openPlate(int boxNumber, SerialPortManager.SendCallback callback) {
if (serialPort == null) {
init();
}
if (serialPort == null) {
callback.onFail(new Exception("打开串口失败"));
return;
}
serialPortManager.sendCommand(generateOpenCommand(boxNumber), callback);
}
}
@@ -1,192 +0,0 @@
package com.sw.plate.utils.comn;
import android.os.HandlerThread;
import android.serialport.SerialPort;
import com.sw.plate.utils.ByteUtil;
import com.sw.plate.utils.L;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
/**
* Created by Administrator on 2017/3/28 0028.
*/
public class SerialPortManager {
private static final String TAG = "SerialPortManager";
private SerialReadThread mReadThread;
private OutputStream mOutputStream;
private HandlerThread mWriteThread;
private Scheduler mSendScheduler;
private static class InstanceHolder {
public static SerialPortManager sManager = new SerialPortManager();
}
public static SerialPortManager instance() {
return InstanceHolder.sManager;
}
private SerialPort mSerialPort;
private SerialPortManager() {
}
/**
* 打开串口
*
* @param device
* @return
*/
public SerialPort open(Device device) {
return open(device.getPath(), device.getBaudrate());
}
/**
* 打开串口
*
* @param devicePath
* @param baudrateString
* @return
*/
public SerialPort open(String devicePath, String baudrateString) {
if (mSerialPort != null) {
close();
}
try {
File device = new File(devicePath);
int baurate = Integer.parseInt(baudrateString);
mSerialPort = new SerialPort(device, baurate);
mReadThread = new SerialReadThread(mSerialPort.getInputStream());
mReadThread.start();
mOutputStream = mSerialPort.getOutputStream();
mWriteThread = new HandlerThread("write-thread");
mWriteThread.start();
mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());
L.e("串口打开成功");
return mSerialPort;
} catch (Throwable tr) {
L.e("打开串口失败" + tr);
close();
return null;
}
}
/**
* 关闭串口
*/
public void close() {
if (mReadThread != null) {
mReadThread.close();
}
if (mOutputStream != null) {
try {
mOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (mWriteThread != null) {
mWriteThread.quit();
}
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
}
/**
* 发送数据
*
* @param datas
* @return
*/
private void sendData(byte[] datas) throws Exception {
mOutputStream.write(datas);
}
/**
* (rx包裹)发送数据
*
* @param datas
* @return
*/
private Observable<Object> rxSendData(final byte[] datas) {
return Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
try {
sendData(datas);
emitter.onNext(new Object());
} catch (Exception e) {
L.e("发送:" + ByteUtil.bytes2HexStr(datas) + " 失败===" + e);
if (!emitter.isDisposed()) {
emitter.onError(e);
return;
}
}
emitter.onComplete();
}
});
}
/**
* 发送命令包
*/
public void sendCommand(final String command, SendCallback callback) {
// TODO: 2018/3/22
L.e("发送命令:" + command);
byte[] bytes = ByteUtil.hexStr2bytes(command);
rxSendData(bytes).subscribeOn(mSendScheduler).subscribe(new Observer<Object>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object o) {
// LogManager.instance().post(new SendMessage(command));
callback.onSuccess();
}
@Override
public void onError(Throwable e) {
L.e("发送失败" + e);
callback.onFail(new Exception(e));
}
@Override
public void onComplete() {
}
});
}
public interface SendCallback {
void onSuccess();
void onFail(Exception e);
}
}
@@ -1,96 +0,0 @@
package com.sw.plate.utils.comn;
import static com.sw.plate.utils.CabinetLockCommand.parseBoxStatus;
import android.os.SystemClock;
import com.sw.plate.utils.ByteUtil;
import com.sw.plate.utils.L;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* 读串口线程
*/
public class SerialReadThread extends Thread {
private static final String TAG = "SerialReadThread";
private BufferedInputStream mInputStream;
public SerialReadThread(InputStream is) {
mInputStream = new BufferedInputStream(is);
}
@Override
public void run() {
byte[] received = new byte[1024];
int size;
L.e("开始读线程");
while (true) {
if (Thread.currentThread().isInterrupted()) {
break;
}
try {
int available = mInputStream.available();
if (available > 0) {
size = mInputStream.read(received);
if (size > 0) {
onDataReceive(received, size);
}
} else {
// 暂停一点时间,免得一直循环造成CPU占用率过高
SystemClock.sleep(1);
}
} catch (IOException e) {
L.e("读取数据失败" + e);
}
//Thread.yield();
}
L.e("结束读进程");
}
/**
* 处理获取到的数据
*
* @param received
* @param size
*/
private void onDataReceive(byte[] received, int size) {
// TODO: 2018/3/22 解决粘包、分包等
String hexStr = ByteUtil.bytes2HexStr(received, 0, size);
// LogManager.instance().post(new RecvMessage(hexStr));
L.e("接收数据:" + hexStr);
if (hexStr.startsWith("5AA2")) {
Map<Integer, Boolean> statusMap = parseBoxStatus(hexStr);
for (Map.Entry<Integer, Boolean> entry : statusMap.entrySet()) {
System.out.println("箱门" + entry.getKey() + ": " +
(entry.getValue() ? "" : ""));
}
}
}
/**
* 停止读线程
*/
public void close() {
try {
mInputStream.close();
} catch (IOException e) {
L.e("异常" + e);
} finally {
super.interrupt();
}
}
}
@@ -1,21 +0,0 @@
package com.sw.plate.utils.comn.message;
/**
* 日志消息数据接口
*/
public interface IMessage {
/**
* 消息文本
*
* @return
*/
String getMessage();
/**
* 是否发送的消息
*
* @return
*/
boolean isToSend();
}
@@ -1,26 +0,0 @@
package com.sw.plate.utils.comn.message;
/**
* 收到的日志
*/
public class RecvMessage implements IMessage {
private String command;
private String message;
public RecvMessage(String command) {
this.command = command;
this.message = " 收到命令:" + command;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isToSend() {
return false;
}
}
@@ -1,26 +0,0 @@
package com.sw.plate.utils.comn.message;
/**
* 发送的日志
*/
public class SendMessage implements IMessage {
private String command;
private String message;
public SendMessage(String command) {
this.command = command;
this.message = " 发送命令:" + command;
}
@Override
public String getMessage() {
return message;
}
@Override
public boolean isToSend() {
return true;
}
}