1 changed files with 0 additions and 514 deletions
@ -1,514 +0,0 @@ |
|||||
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 Ox485 ox485; |
|
||||
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()); |
|
||||
// 初始化Ox485实例 |
|
||||
ox485 = Ox485.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) { |
|
||||
// 初始化Ox485 |
|
||||
ox485.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; |
|
||||
ox485.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; |
|
||||
} |
|
||||
|
|
||||
// 使用Ox485进行通信 |
|
||||
ox485.sendHex485ForPeopleNum(new Ox485.PeopleNumCallback() { |
|
||||
@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串口"); |
|
||||
ox485.checkIsNeedOpen485(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 发送自定义485 HEX命令 |
|
||||
* @param hexCommand HEX命令字符串 |
|
||||
*/ |
|
||||
public void send485HexCommand(String hexCommand) { |
|
||||
LogManager.logInfo(TAG, "发送485 HEX命令: " + hexCommand); |
|
||||
// 使用Ox485的send485HexCommand方法 |
|
||||
ox485.send485HexCommand(hexCommand); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 获取485串口状态 |
|
||||
* @return 串口状态描述 |
|
||||
*/ |
|
||||
public String get485Status() { |
|
||||
// 使用Ox485的get485Status方法 |
|
||||
return ox485.get485Status(); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 关闭485串口 |
|
||||
*/ |
|
||||
public void close485SerialPort() { |
|
||||
LogManager.logInfo(TAG, "关闭485串口"); |
|
||||
// 使用Ox485的close485SerialPort方法 |
|
||||
ox485.close485SerialPort(); |
|
||||
} |
|
||||
|
|
||||
// ==================== 内部辅助方法 ==================== |
|
||||
|
|
||||
/** |
|
||||
* 申请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 (ox485 != null) { |
|
||||
ox485.release(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue