Browse Source

add Gpio 485 test

dev
赵明涛 2 days ago
parent
commit
8c2cb30312
  1. 57
      app/src/main/java/com/ouxuan/oxface/DebugActivity.java
  2. 511
      app/src/main/java/com/ouxuan/oxface/device/OxGpio.java
  3. 7
      app/src/main/res/layout/activity_debug.xml

57
app/src/main/java/com/ouxuan/oxface/DebugActivity.java

@ -23,6 +23,7 @@ import com.baidu.idl.face.main.finance.listener.SdkInitListener;
import com.baidu.idl.face.main.finance.manager.FaceSDKManager;
import com.ouxuan.oxface.device.DeviceUtils;
import com.ouxuan.oxface.device.HuaWeiScanManager;
import com.ouxuan.oxface.device.OxGpio;
import com.ouxuan.oxface.device.RelayController;
import com.ouxuan.oxface.network.utils.NetworkUtils;
import com.ouxuan.oxface.utils.AutoStartManager;
@ -41,6 +42,7 @@ public class DebugActivity extends Activity {
private ScrollView logScrollView;
private AutoStartManager autoStartManager;
private RelayController relayController;
private OxGpio oxGpio;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -63,6 +65,8 @@ public class DebugActivity extends Activity {
private void initManagers() {
autoStartManager = AutoStartManager.getInstance(this);
relayController = RelayController.getInstance();
oxGpio = OxGpio.getInstance();
oxGpio.initialize(this);
}
private void setupClickListeners() {
@ -238,6 +242,15 @@ public class DebugActivity extends Activity {
testRelayAutoClose();
}
});
// 485人数测试按钮
Button btnTest485PeopleNum = findViewById(R.id.btnTest485PeopleNum);
btnTest485PeopleNum.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test485PeopleNum();
}
});
}
/**
@ -700,4 +713,48 @@ public class DebugActivity extends Activity {
logMessage("未处理扫码结果,继续处理其他ActivityResult");
}
}
/**
* 测试485人数获取功能
*/
private void test485PeopleNum() {
logMessage("开始485人数测试...");
if (oxGpio == null) {
logMessage("OxGpio未初始化");
showToast("OxGpio未初始化");
return;
}
try {
// 设置485模式开关为启用状态
oxGpio.setGateCamera485OxOn(true);
logMessage("已启用485模式");
// 调用sendHex485ForPeopleNum方法
oxGpio.sendHex485ForPeopleNum(new OxGpio.PeopleNumCallback() {
@Override
public void onSuccess(int peopleNum) {
logMessage("485人数获取成功: " + peopleNum + " 人");
Log.i(TAG, "485人数获取成功: " + peopleNum + " 人");
showToast("485人数获取成功: " + peopleNum + " 人");
}
@Override
public void onError(String errorMessage) {
logMessage("485人数获取失败: " + errorMessage);
Log.e(TAG, "485人数获取失败: " + errorMessage);
showToast("485人数获取失败: " + errorMessage);
}
});
logMessage("已发送485人数查询请求,等待响应...");
showToast("正在查询485人数...");
} catch (Exception e) {
Log.e(TAG, "485人数测试失败", e);
logMessage("485人数测试失败: " + e.getMessage());
showToast("485人数测试失败");
}
}
}

511
app/src/main/java/com/ouxuan/oxface/device/OxGpio.java

@ -0,0 +1,511 @@
package com.ouxuan.oxface.device;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import com.ouxuan.oxface.utils.LogManager;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* OxGpio GPIO控制和485通信模块
* 基于原GpioUtils重写并集成485串口通信功能
*
* @author AI Assistant
* @version 1.0
* @date 2024/09/11
*/
public class OxGpio {
private static final String TAG = "OxGpio";
// GPIO引脚定义 - RK3399
public static final int IO1_3399 = 1066;
public static final int IO2_3399 = 1067;
public static final int IO3_3399 = 1072;
public static final int IO4_3399 = 1071;
// GPIO引脚定义 - RK3368
public static final int IO1_3368 = 91;
public static final int IO2_3368 = 90;
public static final int IO3_3368 = 111;
public static final int IO4_3368 = 109;
// 485通信配置
private static final String SERIAL_PORT_PATH = "/dev/ttyS6";
private static final int BAUD_RATE = 9600;
private static final int STOP_BITS = 1;
private static final String HEX_COMMAND_GET_PEOPLE_NUM = "0F030001000294E5";
private static final long TIMEOUT_485_MILLIS = 10000; // 10秒超时
// 单例实例
private static volatile OxGpio instance;
// 485通信相关
private SerialPort485 serialPort485;
private Handler mainHandler;
private boolean gateCamera485OxOn = false;
// GPIO读取失败计数
private static long mTime = 0;
private static int mFailTimes = 0;
/**
* 485通信回调接口
*/
public interface PeopleNumCallback {
void onSuccess(int peopleNum);
void onError(String errorMessage);
}
private OxGpio() {
mainHandler = new Handler(Looper.getMainLooper());
// 初始化SerialPort485实例
serialPort485 = SerialPort485.getInstance();
}
/**
* 获取OxGpio单例实例
* @return OxGpio实例
*/
public static OxGpio getInstance() {
if (instance == null) {
synchronized (OxGpio.class) {
if (instance == null) {
instance = new OxGpio();
}
}
}
return instance;
}
/**
* 初始化OxGpio模块
* @param context 上下文
*/
public void initialize(android.content.Context context) {
// 初始化SerialPort485
serialPort485.initialize(context);
LogManager.logInfo(TAG, "OxGpio模块初始化完成");
}
// ==================== GPIO操作方法 ====================
/**
* 给export申请权限
*/
public static boolean upgradeRootPermissionForExport() {
LogManager.logInfo(TAG, "申请GPIO export权限");
return upgradeRootPermission("/sys/class/gpio/export");
}
/**
* 配置一个gpio路径
* @param gpio GPIO引脚号
* @return 是否成功
*/
public static boolean exportGpio(int gpio) {
LogManager.logInfo(TAG, "导出GPIO: " + gpio);
return writeNode("/sys/class/gpio/export", String.valueOf(gpio));
}
/**
* 给获取io口的状态的路径申请权限该方法需要在exportGpio后调用
* @param gpio GPIO引脚号
*/
public static boolean upgradeRootPermissionForGpio(int gpio) {
LogManager.logInfo(TAG, "申请GPIO权限: " + gpio);
boolean directionResult = upgradeRootPermission("/sys/class/gpio/gpio" + gpio + "/direction");
boolean valueResult = upgradeRootPermission("/sys/class/gpio/gpio" + gpio + "/value");
return directionResult && valueResult;
}
/**
* 设置io口为输入或输出
* @param gpio GPIO引脚号
* @param direction 0=输出(out), 1=输入(in)
* @return 是否成功
*/
public static boolean setGpioDirection(int gpio, int direction) {
String gpioDirection;
if (direction == 0) {
gpioDirection = "out";
} else if (direction == 1) {
gpioDirection = "in";
} else {
LogManager.logError(TAG, "无效的GPIO方向: " + direction);
return false;
}
LogManager.logInfo(TAG, "设置GPIO方向 - 引脚: " + gpio + ", 方向: " + gpioDirection);
return writeNode("/sys/class/gpio/gpio" + gpio + "/direction", gpioDirection);
}
/**
* 获取io口的状态为输出还是输入
* @param gpio GPIO引脚号
* @return "in" "out"
*/
public static String getGpioDirection(int gpio) {
String direction = readNode("/sys/class/gpio/gpio" + gpio + "/direction");
LogManager.logDebug(TAG, "获取GPIO方向 - 引脚: " + gpio + ", 方向: " + direction);
return direction;
}
/**
* 给输出io口写值高电平或低电平
* @param gpio GPIO引脚号
* @param value "1"=高电平, "0"=低电平
* @return 是否成功
*/
public static boolean writeGpioValue(int gpio, String value) {
LogManager.logInfo(TAG, "写入GPIO值 - 引脚: " + gpio + ", 值: " + value);
return writeNode("/sys/class/gpio/gpio" + gpio + "/value", value);
}
/**
* 获取当前gpio是高还是低
* @param gpio GPIO引脚号
* @return "1"=高电平, "0"=低电平
*/
public static String getGpioValue(int gpio) {
String value = readNode("/sys/class/gpio/gpio" + gpio + "/value");
LogManager.logDebug(TAG, "获取GPIO值 - 引脚: " + gpio + ", 值: " + value);
return value;
}
// ==================== 485通信方法转换自uniapp ====================
/**
* 设置485模式开关
* @param enabled 是否启用485模式
*/
public void setGateCamera485OxOn(boolean enabled) {
this.gateCamera485OxOn = enabled;
serialPort485.setGateCamera485OxOn(enabled);
LogManager.logInfo(TAG, "设置485模式开关: " + enabled);
}
/**
* 获取485模式开关状态
* @return true表示485模式已启用
*/
public boolean isGateCamera485OxOn() {
return gateCamera485OxOn;
}
/**
* 通过485获取摄像头人数
* 对应uniapp中的sendHex485ForPeopleNum方法
* @param callback 结果回调
*/
public void sendHex485ForPeopleNum(PeopleNumCallback callback) {
LogManager.logInfo(TAG, "开始通过485获取摄像头人数");
if (!gateCamera485OxOn) {
String errorMsg = "485模式未开启";
LogManager.logError(TAG, errorMsg);
if (callback != null) {
mainHandler.post(() -> callback.onError(errorMsg));
}
return;
}
// 使用SerialPort485进行通信
serialPort485.sendHex485ForPeopleNum(new SerialPort485.SerialPort485Callback() {
@Override
public void onSuccess(int peopleNum) {
LogManager.logInfo(TAG, "485获取人数成功: " + peopleNum);
if (callback != null) {
mainHandler.post(() -> callback.onSuccess(peopleNum));
}
}
@Override
public void onError(String errorMessage) {
LogManager.logError(TAG, "485获取人数失败: " + errorMessage);
if (callback != null) {
mainHandler.post(() -> callback.onError(errorMessage));
}
}
});
}
/**
* 异步获取摄像头人数使用CompletableFuture
* @return CompletableFuture<Integer> 人数结果
*/
public CompletableFuture<Integer> sendHex485ForPeopleNumAsync() {
LogManager.logInfo(TAG, "异步获取摄像头人数");
CompletableFuture<Integer> future = new CompletableFuture<>();
sendHex485ForPeopleNum(new PeopleNumCallback() {
@Override
public void onSuccess(int peopleNum) {
future.complete(peopleNum);
}
@Override
public void onError(String errorMessage) {
future.completeExceptionally(new RuntimeException(errorMessage));
}
});
return future;
}
/**
* 检查是否需要打开485串口
*/
public void checkIsNeedOpen485() {
LogManager.logInfo(TAG, "检查是否需要打开485串口");
serialPort485.checkIsNeedOpen485();
}
/**
* 发送自定义485 HEX命令
* @param hexCommand HEX命令字符串
*/
public void send485HexCommand(String hexCommand) {
LogManager.logInfo(TAG, "发送485 HEX命令: " + hexCommand);
serialPort485.sendHex(hexCommand);
}
/**
* 获取485串口状态
* @return 串口状态描述
*/
public String get485Status() {
return serialPort485.getSerialPortStatus();
}
/**
* 关闭485串口
*/
public void close485SerialPort() {
LogManager.logInfo(TAG, "关闭485串口");
serialPort485.closeSerialPort();
}
// ==================== 内部辅助方法 ====================
/**
* 申请root权限
* @param path 路径
* @return 是否成功
*/
private static boolean upgradeRootPermission(String path) {
LogManager.logDebug(TAG, "申请root权限: " + path);
Process process = null;
DataOutputStream os = null;
try {
String cmd = "chmod 777 " + path;
process = Runtime.getRuntime().exec("su"); // 切换到root账号
os = new DataOutputStream(process.getOutputStream());
os.writeBytes(cmd + "\n");
os.writeBytes("exit\n");
os.flush();
process.waitFor();
} catch (Exception e) {
LogManager.logError(TAG, "申请root权限异常: " + path, e);
return false;
} finally {
try {
if (os != null) {
os.close();
}
if (process != null) {
process.destroy();
}
} catch (Exception e) {
LogManager.logError(TAG, "关闭进程异常", e);
}
}
try {
boolean result = process != null && process.waitFor() == 0;
LogManager.logInfo(TAG, "申请root权限结果: " + result + " - " + path);
return result;
} catch (InterruptedException e) {
LogManager.logError(TAG, "等待进程中断", e);
return false;
}
}
/**
* 写入节点
* @param path 节点路径
* @param value 写入值
* @return 是否成功
*/
private static boolean writeNode(String path, String value) {
LogManager.logDebug(TAG, "写入节点 - 路径: " + path + ", 值: " + value);
if (path == null || value == null) {
LogManager.logError(TAG, "写入节点参数错误 - 路径或值为空");
return false;
}
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(path);
fileWriter.write(value);
LogManager.logDebug(TAG, "节点写入成功: " + path);
return true;
} catch (Exception e) {
LogManager.logError(TAG, "写入节点异常 - 路径: " + path + ", 值: " + value, e);
return false;
} finally {
try {
if (fileWriter != null) {
fileWriter.close();
}
} catch (IOException e) {
LogManager.logError(TAG, "关闭FileWriter异常", e);
}
}
}
/**
* 读取节点
* @param path 节点路径
* @return 读取的值
*/
private static String readNode(String path) {
LogManager.logDebug(TAG, "读取节点: " + path);
String result = "";
FileReader fread = null;
BufferedReader buffer = null;
try {
fread = new FileReader(path);
buffer = new BufferedReader(fread);
String str;
while ((str = buffer.readLine()) != null) {
result = str;
break;
}
mFailTimes = 0;
LogManager.logDebug(TAG, "节点读取成功: " + path + " = " + result);
} catch (IOException e) {
LogManager.logError(TAG, "读取节点IO异常: " + path, e);
if (mTime == 0 || SystemClock.uptimeMillis() - mTime < 1000) {
mFailTimes++;
}
if (mFailTimes >= 3) {
LogManager.logWarning(TAG, "读取节点连续失败3次,可能需要检查权限: " + path);
}
} finally {
try {
if (buffer != null) {
buffer.close();
}
if (fread != null) {
fread.close();
}
} catch (IOException e) {
LogManager.logError(TAG, "关闭文件流异常", e);
}
}
return result;
}
// ==================== 便捷方法 ====================
/**
* 初始化GPIO引脚包含导出申请权限设置方向
* @param gpio GPIO引脚号
* @param direction 0=输出, 1=输入
* @return 是否成功
*/
public static boolean initializeGpio(int gpio, int direction) {
LogManager.logInfo(TAG, "初始化GPIO - 引脚: " + gpio + ", 方向: " + direction);
// 1. 申请export权限
if (!upgradeRootPermissionForExport()) {
LogManager.logError(TAG, "申请export权限失败");
return false;
}
// 2. 导出GPIO
if (!exportGpio(gpio)) {
LogManager.logError(TAG, "导出GPIO失败: " + gpio);
return false;
}
// 3. 申请GPIO权限
if (!upgradeRootPermissionForGpio(gpio)) {
LogManager.logError(TAG, "申请GPIO权限失败: " + gpio);
return false;
}
// 4. 设置GPIO方向
if (!setGpioDirection(gpio, direction)) {
LogManager.logError(TAG, "设置GPIO方向失败: " + gpio);
return false;
}
LogManager.logInfo(TAG, "GPIO初始化成功: " + gpio);
return true;
}
/**
* 设置GPIO输出高电平
* @param gpio GPIO引脚号
* @return 是否成功
*/
public static boolean setGpioHigh(int gpio) {
LogManager.logInfo(TAG, "设置GPIO高电平: " + gpio);
return writeGpioValue(gpio, "1");
}
/**
* 设置GPIO输出低电平
* @param gpio GPIO引脚号
* @return 是否成功
*/
public static boolean setGpioLow(int gpio) {
LogManager.logInfo(TAG, "设置GPIO低电平: " + gpio);
return writeGpioValue(gpio, "0");
}
/**
* 检查GPIO是否为高电平
* @param gpio GPIO引脚号
* @return true表示高电平
*/
public static boolean isGpioHigh(int gpio) {
String value = getGpioValue(gpio);
return "1".equals(value);
}
/**
* 检查GPIO是否为低电平
* @param gpio GPIO引脚号
* @return true表示低电平
*/
public static boolean isGpioLow(int gpio) {
String value = getGpioValue(gpio);
return "0".equals(value);
}
/**
* 释放资源
*/
public void release() {
LogManager.logInfo(TAG, "释放OxGpio资源");
if (serialPort485 != null) {
serialPort485.release();
}
}
}

7
app/src/main/res/layout/activity_debug.xml

@ -229,14 +229,15 @@
android:layout_marginEnd="4dp"
android:textSize="12sp" />
<!-- 占位按钮,保持布局对称 -->
<Button
android:id="@+id/btnTest485PeopleNum"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:visibility="invisible"
android:text="485人数测试"
android:layout_marginStart="4dp"
android:layout_marginEnd="4dp" />
android:layout_marginEnd="4dp"
android:textSize="12sp" />
<!-- 占位按钮,保持布局对称 -->
<Button

Loading…
Cancel
Save