fix(plc): 统一添加超时兜底机制避免调用无响应挂起

- 在 PlcHelper 中新增 withTimeout 方法,包装所有 SDK 动作和查询接口
- 通过定时器实现超时检测,超时触发 onError 回调,避免永久挂起
- 确保 onSuccess 和 onError 回调至多调用一次,防止重复回调
- 为所有 PLC 操作接口分别设置合理超时时长(3s - 10s不等)
- 修改所有内部调用使用 withTimeout 包装,保证异常场景的错误回调触发
- 更新生产环境基础 URL 地址为平台测试环境地址
This commit is contained in:
mazengfei
2026-08-10 14:02:30 +08:00
parent c9dd294f87
commit 6be9f4f1cf
2 changed files with 112 additions and 35 deletions
@@ -40,7 +40,7 @@ object GlobalData {
*/ */
const val LOCAL_BASE_URL: String = "http://192.168.10.101:24801" const val LOCAL_BASE_URL: String = "http://192.168.10.101:24801"
const val TEST_BASE_URL: String = "https://dev.yixiong-tech.com:8081" const val TEST_BASE_URL: String = "https://dev.yixiong-tech.com:8081"
const val PROD_BASE_URL: String = "https://api.dm.yixiong-tech.com:8443" const val PROD_BASE_URL: String = "https://platform-api.uat.shuziweidao.com"
/** /**
* 设备id * 设备id
@@ -4,10 +4,16 @@ import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import com.megoai.plc_aar.SDK import com.megoai.plc_aar.SDK
import java.util.concurrent.atomic.AtomicBoolean
/** /**
* PLC 操作工具类(单例) * PLC 操作工具类(单例)
* 封装 SDK 所有接口,统一回调到主线程 * 封装 SDK 所有接口,统一回调到主线程
*
* 注意:底层 PLC SDKFlutter MethodChannel 封装)的 Listener 只有 onMessage 一个回调,
* Flutter 侧 error()/notImplemented() 均为空实现,串口异常或引擎未就绪时不会回调。
* 因此本类对所有动作类、查询类接口统一加超时兜底:超时未响应则触发 onError,
* 保证上层回调链不会因“无响应”而永久挂起。
*/ */
class PlcHelper private constructor(context: Context) { class PlcHelper private constructor(context: Context) {
@@ -28,6 +34,59 @@ class PlcHelper private constructor(context: Context) {
} }
} }
// ==================== 超时兜底封装 ====================
/**
* 包装 SDK 动作/查询调用:超时未响应时主动触发 onError。
*
* 背景:底层 SDK 的 Listener 仅在 Flutter 侧 success() 时回调 onMessage
* error()/notImplemented() 为空实现,串口异常时 Android 侧收不到任何回调,
* 会导致上层回调永久挂起。本方法用定时器兜底,并保证 onSuccess/onError 至多触发一次。
*
* @param timeoutMs 超时时长(毫秒)
* @param callback 上层回调(统一在主线程回调)
* @param action 实际发起 SDK 调用的逻辑,参数为经过防重入包装的“安全回调”,
* 内部所有成功/失败出口都应通过该回调触发
* @param <T> 回调结果类型
*/
private inline fun <T> withTimeout(
timeoutMs: Long,
callback: PlcCallback<T>,
crossinline action: (safeCallback: PlcCallback<T>) -> Unit
) {
// 标记回调是否已触发,保证 onSuccess/onError 至多执行一次
val fired = AtomicBoolean(false)
// 包装后的安全回调,所有出口都经过它,避免重复回调
val safeCallback = object : PlcCallback<T> {
override fun onSuccess(result: T) {
if (fired.compareAndSet(false, true)) callback.onSuccess(result)
}
override fun onError(message: String) {
if (fired.compareAndSet(false, true)) callback.onError(message)
}
}
// 超时定时器:到期未收到 SDK 回调则触发 onError
val timeoutRunnable = Runnable {
safeCallback.onError("PLC 操作超时(${timeoutMs}ms)未响应")
}
mainHandler.postDelayed(timeoutRunnable, timeoutMs)
// 发起实际 SDK 调用,所有成功/失败都通过 safeCallback 回调
action(object : PlcCallback<T> {
override fun onSuccess(result: T) {
// 成功后取消超时定时器
mainHandler.removeCallbacks(timeoutRunnable)
safeCallback.onSuccess(result)
}
override fun onError(message: String) {
// 失败同样取消超时定时器
mainHandler.removeCallbacks(timeoutRunnable)
safeCallback.onError(message)
}
})
}
// ==================== 连接与模式 ==================== // ==================== 连接与模式 ====================
/** /**
@@ -43,8 +102,10 @@ class PlcHelper private constructor(context: Context) {
* 打开自动模式(应用启动时调用一次) * 打开自动模式(应用启动时调用一次)
*/ */
fun openAutoMode(callback: PlcCallback<String>) { fun openAutoMode(callback: PlcCallback<String>) {
sdk.actionOpenAutoMode { result -> withTimeout(5000, callback) { safe ->
mainHandler.post { callback.onSuccess(result.toString()) } sdk.actionOpenAutoMode { result ->
mainHandler.post { safe.onSuccess(result.toString()) }
}
} }
} }
@@ -52,76 +113,90 @@ class PlcHelper private constructor(context: Context) {
* 故障复位(出现 alarmCode 或补盘完成后调用) * 故障复位(出现 alarmCode 或补盘完成后调用)
*/ */
fun alarmReset(callback: PlcCallback<String>) { fun alarmReset(callback: PlcCallback<String>) {
sdk.actionAlarmReset { result -> withTimeout(5000, callback) { safe ->
mainHandler.post { callback.onSuccess(result.toString()) } sdk.actionAlarmReset { result ->
mainHandler.post { safe.onSuccess(result.toString()) }
}
} }
} }
// ==================== 出盘操作 ==================== // ==================== 出盘操作 ====================
/** /**
* 出盘 * 出盘(机械动作,超时 10s
*/ */
fun outPlate(callback: PlcCallback<String>) { fun outPlate(callback: PlcCallback<String>) {
sdk.actionOutPlate { result -> withTimeout(10000, callback) { safe ->
mainHandler.post { callback.onSuccess(result.toString()) } sdk.actionOutPlate { result ->
mainHandler.post { safe.onSuccess(result.toString()) }
}
} }
} }
// ==================== 平台控制 ==================== // ==================== 平台控制 ====================
/** /**
* 平台下降(开始补盘) * 平台下降(开始补盘,超时 8s
*/ */
fun platformDown(callback: PlcCallback<String>) { fun platformDown(callback: PlcCallback<String>) {
sdk.actionPlatformDown { result -> withTimeout(8000, callback) { safe ->
mainHandler.post { callback.onSuccess(result.toString()) } sdk.actionPlatformDown { result ->
mainHandler.post { safe.onSuccess(result.toString()) }
}
} }
} }
/** /**
* 平台上升(补盘完成) * 平台上升(补盘完成,超时 8s
*/ */
fun platformUp(callback: PlcCallback<String>) { fun platformUp(callback: PlcCallback<String>) {
sdk.actionPlatformDownStop { result -> withTimeout(8000, callback) { safe ->
mainHandler.post { callback.onSuccess(result.toString()) } sdk.actionPlatformDownStop { result ->
mainHandler.post { safe.onSuccess(result.toString()) }
}
} }
} }
/** /**
* 补盘完成返回营业(自动模式 + 故障复位) * 补盘完成返回营业(自动模式 + 故障复位,超时 5s
*/ */
fun backToBusiness(callback: PlcCallback<String>) { fun backToBusiness(callback: PlcCallback<String>) {
sdk.actionOpenAutoModeAndActionAlarmReset { result -> withTimeout(5000, callback) { safe ->
mainHandler.post { callback.onSuccess(result.toString()) } sdk.actionOpenAutoModeAndActionAlarmReset { result ->
mainHandler.post { safe.onSuccess(result.toString()) }
}
} }
} }
// ==================== 状态查询 ==================== // ==================== 状态查询 ====================
/** /**
* 获取 PLC 状态(解析为 PlcStatus 对象) * 获取 PLC 状态(解析为 PlcStatus 对象,超时 3s
*/ */
fun getStatus(callback: PlcCallback<PlcStatus>) { fun getStatus(callback: PlcCallback<PlcStatus>) {
sdk.getPlcStatus { json -> withTimeout(3000, callback) { safe ->
mainHandler.post { sdk.getPlcStatus { json ->
if (json != null) { mainHandler.post {
callback.onSuccess(PlcStatus.Companion.fromJson(json.toString())) if (json != null) {
} else { safe.onSuccess(PlcStatus.Companion.fromJson(json.toString()))
callback.onError("获取状态失败") } else {
safe.onError("获取状态失败")
}
} }
} }
} }
} }
/** /**
* 获取 PLC 状态(原始 JSON 字符串) * 获取 PLC 状态(原始 JSON 字符串,超时 3s
*/ */
fun getStatusJson(callback: PlcCallback<String>) { fun getStatusJson(callback: PlcCallback<String>) {
sdk.getPlcStatus { json -> withTimeout(3000, callback) { safe ->
mainHandler.post { sdk.getPlcStatus { json ->
if (json != null) callback.onSuccess(json.toString()) mainHandler.post {
else callback.onError("获取状态失败") if (json != null) safe.onSuccess(json.toString())
else safe.onError("获取状态失败")
}
} }
} }
} }
@@ -129,16 +204,18 @@ class PlcHelper private constructor(context: Context) {
// ==================== 参数设置 ==================== // ==================== 参数设置 ====================
/** /**
* 设置 PLC 参数 * 设置 PLC 参数(超时 3s
* @param paramType 参数类型(如 "18FD" 表示主电机运行最长时间) * @param paramType 参数类型(如 "18FD" 表示主电机运行最长时间)
* @param paramValue 参数值(int,单位 0.1s * @param paramValue 参数值(int,单位 0.1s
*/ */
fun setParams(paramType: String, paramValue: Int, callback: PlcCallback<String>) { fun setParams(paramType: String, paramValue: Int, callback: PlcCallback<String>) {
sdk.setPLCParams( withTimeout(3000, callback) { safe ->
{ result -> mainHandler.post { callback.onSuccess(result.toString()) } }, sdk.setPLCParams(
paramType, { result -> mainHandler.post { safe.onSuccess(result.toString()) } },
paramValue paramType,
) paramValue
)
}
} }
// ==================== 生命周期 ==================== // ==================== 生命周期 ====================