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