[8.2.0][adas] 修改心跳机制:增加接收域控心跳,兼容域控老版本心跳,修改心跳超时断开连接后立即进行重连,域控连接状态新增调用时间链路日志排查问题

This commit is contained in:
xinfengkun
2025-08-01 17:34:05 +08:00
parent 4add71e4cc
commit 12f3f8b5e0
10 changed files with 119 additions and 90 deletions

View File

@@ -96,13 +96,14 @@ class MoGoAdasMsgConnectStatusListenerImpl :
/**
* 与工控机链接状态变化
*
* @param status {@link AdasConstants.IpcConnectionStatus}
* @param reason 连接信息需要判null,目前只有以下参数存在值其他全部为null
* {@link AdasConstants.IpcConnectionStatus#HEARTBEAT_TIMEOUT} 表示超时时间
* {@link AdasConstants.IpcConnectionStatus#CONNECT_EXCEPTION} 表示连接异常原因
* {@link AdasConstants.IpcConnectionStatus#SERVER_DISCONNECTED} 服务端断开如果服务端关闭时发送了原因将存在如果服务端关闭时未发送将为null
* @param callbackTime 回调时间
* @param status {@link AdasConstants.IpcConnectionStatus}
* @param reason 连接信息需要判null,目前只有以下参数存在值其他全部为null
* {@link AdasConstants.IpcConnectionStatus#HEARTBEAT_TIMEOUT} 表示超时时间
* {@link AdasConstants.IpcConnectionStatus#CONNECT_EXCEPTION} 表示连接异常原因
* {@link AdasConstants.IpcConnectionStatus#SERVER_DISCONNECTED} 服务端断开如果服务端关闭时发送了原因将存在如果服务端关闭时未发送将为null
*/
override fun onConnectionIPCStatus(status: AdasConstants.IpcConnectionStatus, reason: String?) {
override fun onConnectionIPCStatus(callbackTime: Long, status: AdasConstants.IpcConnectionStatus, reason: String?) {
val isConnected = status == AdasConstants.IpcConnectionStatus.CONNECTED
CallerAutopilotActionsListenerManager.setConnected(isConnected)
CallerParallelDrivingActionsListenerManager.setConnected(isConnected)
@@ -235,8 +236,10 @@ class MoGoAdasMsgConnectStatusListenerImpl :
}
AdasConstants.IpcConnectionStatus.HEARTBEAT_TIMEOUT -> {
connectStatusDescribe =
"心跳超时(连接域控成功后在一段时间内未收到域控任何数据),超时时间:${reason}"
connectStatusDescribe = "心跳超时"
reason?.let {
connectStatusDescribe = it
}
CallerLogger.d(
"$M_D_C$TAG",
connectStatusDescribe
@@ -270,7 +273,7 @@ class MoGoAdasMsgConnectStatusListenerImpl :
CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().connectStatusReason = reason
CallerAutoPilotStatusListenerManager.invokeAutoPilotStatus()
CallerAutoPilotStatusListenerManager.invokeAutoPilotIPCStatusChanged(status, reason)
saveIntoMsgBox(status, connectStatusDescribe)
saveIntoMsgBox(callbackTime, status, connectStatusDescribe)
}
/**
@@ -326,9 +329,10 @@ class MoGoAdasMsgConnectStatusListenerImpl :
linkChainLog = CHAIN_TYPE_SOCKET_AUTOPILOT,
linkCode = CHAIN_SOURCE_ADAS,
nodeAliasCode = CHAIN_CODE_ADAS_CONNECT_STATUS,
paramIndexes = [0,1]
paramIndexes = [0,1,2]
)
private fun saveIntoMsgBox(
callbackTime: Long,
status: AdasConstants.IpcConnectionStatus,
reason: String
) {

View File

@@ -21,7 +21,7 @@ public enum IpcConnectionStatusDescription {
SEARCH_ADDRESS(AdasConstants.IpcConnectionStatus.SEARCH_ADDRESS, "搜索中", "连接模式为PING模式时,正在查找是否有可用的域控地址"),
NOT_FOUND_ADDRESS(AdasConstants.IpcConnectionStatus.NOT_FOUND_ADDRESS, "未找到", "连接模式为PING模式时,配置的域控地址列表中的全部地址均无法PING通【不修改默认配置的情况下需要排查PAD是否连接车载路由器、工控机是否连接车载路由器、工控机是否开机等】"),
CERTIFICATION_FAILED(AdasConstants.IpcConnectionStatus.CERTIFICATION_FAILED, "认证异常", "鹰眼端或鹰眼端和域控端启用认证后,认证出现异常"),
HEARTBEAT_TIMEOUT(AdasConstants.IpcConnectionStatus.HEARTBEAT_TIMEOUT, "心跳超时", "连接域控成功后在一段时间内未收到域控任何数据默认4秒【可以配置】"),
HEARTBEAT_TIMEOUT(AdasConstants.IpcConnectionStatus.HEARTBEAT_TIMEOUT, "心跳超时", "连接域控成功后在一段时间内未收到域控心跳数据或其它任何数据默认4秒【可以配置】"),
PROTOCOL_MISMATCH(AdasConstants.IpcConnectionStatus.PROTOCOL_MISMATCH, "协议不匹配", "被连接的域控端非WebSocket协议【可能性非常低】"),
SERVER_DISCONNECTED(AdasConstants.IpcConnectionStatus.SERVER_DISCONNECTED, "域控断开", "域控主动发起断开WebSocket连接目前主要原因有1.证书认证失败2.老版本MAP不支持多连接的情况下会踢掉客户端");

View File

@@ -508,8 +508,9 @@ public class AdasChannel implements IAdasNetCommApi, FpgaSocket.IWebSocketConnec
receivedAckManager.sendReceivedAck(header.getMsgID());
}
//司机端刷新心跳
if (!adasOptions.isPassenger())
ReceiveTimeoutManager.getInstance().refreshLast(header.getTimestamp());
if (!adasOptions.isPassenger() && !ReceiveTimeoutManager.getInstance().isHaveIpcHeartbeat()) {
ReceiveTimeoutManager.getInstance().refreshLast((long) (header.getTimestamp() * 1000L));
}
// CupidLogUtils.w("--->websocket byte read header = " + messageType.toString());
MessagePad.MessageType messageType = header.getMsgType();
if (usedSsmSource == AdasConstants.SsmSource.SSM_UNKNOWN && (messageType == MessagePad.MessageType.MsgTypeStatusQueryResp || messageType == MessagePad.MessageType.MsgTypeSSMState)) {
@@ -780,9 +781,19 @@ public class AdasChannel implements IAdasNetCommApi, FpgaSocket.IWebSocketConnec
long nowTime = 0;
if (CupidLogUtils.isEnableLog())
nowTime = SystemClock.elapsedRealtime();
decoderRaw(receiveTime, bytes);
//司机屏工控机数据转发
if (!adasOptions.isPassenger() && onMultiDeviceListener != null) {
boolean isForward = true;//司机屏是否转发域控数据
//域控发送的心跳数据
if (Constants.RAW_HEARTBEAT.equals(bytes)) {
isForward = false;
if (!ReceiveTimeoutManager.getInstance().isHaveIpcHeartbeat()) {
ReceiveTimeoutManager.getInstance().setHaveIpcHeartbeat(true);
}
ReceiveTimeoutManager.getInstance().refreshLast(System.currentTimeMillis());
} else {
decoderRaw(receiveTime, bytes);
}
//司机屏工控机数据转发 域控心跳数据不做转发
if (!adasOptions.isPassenger() && onMultiDeviceListener != null && isForward) {
onMultiDeviceListener.onForwardingDriverIPCMessage(bytes.toByteArray());
}
calculateTimeConsumingOnMessage(nowTime);
@@ -900,6 +911,7 @@ public class AdasChannel implements IAdasNetCommApi, FpgaSocket.IWebSocketConnec
stopSendHeartbeat();
stopDispatchHandler();
isInitConfigure.set(false);
ReceiveTimeoutManager.getInstance().setHaveIpcHeartbeat(false);
stopCarConfigReq();
ipcConnectedIp = null;
ipcConnectedPort = Constants.DEFAULT_PORT;
@@ -911,11 +923,15 @@ public class AdasChannel implements IAdasNetCommApi, FpgaSocket.IWebSocketConnec
stopPingAddress();
}
if (args != null && args.length > 0) {
reason = String.valueOf(args[0]);
if (status == AdasConstants.IpcConnectionStatus.HEARTBEAT_TIMEOUT && args.length > 2) {
reason = ReceiveTimeoutManager.buildTimeoutStr(true, (boolean) args[0], (long) args[1], (long) args[2]);
} else {
reason = String.valueOf(args[0]);
}
}
}
if (adasConnectStatusListener != null) {
adasConnectStatusListener.onConnectionIPCStatus(status, reason);
adasConnectStatusListener.onConnectionIPCStatus(System.currentTimeMillis(), status, reason);
}
}
@@ -939,13 +955,13 @@ public class AdasChannel implements IAdasNetCommApi, FpgaSocket.IWebSocketConnec
}
}
//车辆基础信息请求
//发送心跳
private synchronized void startSendHeartbeat() {
if (heartbeatScheduler == null || heartbeatScheduler.isShutdown()) {
heartbeatScheduler = Executors.newSingleThreadScheduledExecutor();
heartbeatScheduler.scheduleWithFixedDelay(() -> {
if (mSocket != null && mSocket.isConnected()) {
mSocket.sendDataWebSocket(Constants.RAW_HEART_BEAT);
mSocket.sendDataWebSocket(Constants.RAW_HEARTBEAT);
}
}, 100, 1000, TimeUnit.MILLISECONDS);
}
@@ -1033,26 +1049,6 @@ public class AdasChannel implements IAdasNetCommApi, FpgaSocket.IWebSocketConnec
return AdasConstants.IpcConnectionStatus.DISCONNECTED;
}
// TODO 需求暂停 待讨论
// @Override
// public boolean getRoutes() {
// return sendWsMessage(Constants.QUERY_ROUTES);
// }
/**
* 接管原因
*/
/**
* log是否显示
*
* @param isEnableLog
*/
public void setEnableLog(boolean isEnableLog) {
CupidLogUtils.setEnableLog(isEnableLog);
}
//车辆基础信息请求
private void startCarConfigReq() {
if (carConfigReqTimer == null) {

View File

@@ -204,24 +204,11 @@ public class AdasManager implements IAdasNetCommApi {
return AdasConstants.IpcConnectionStatus.DISCONNECTED;
}
// TODO 需求暂停 待讨论 获取车辆轨迹文件
// @Override
// public boolean getRoutes() {
// if (mChannel!=null){
// return mChannel.getRoutes();
// }
// return false;
// }
/**
* Log是否开启打印
*/
@Override
public void setEnableLog(boolean isEnableLog) {
if (mChannel != null) {
mChannel.setEnableLog(isEnableLog);
}
CupidLogUtils.setEnableLog(isEnableLog);
}
// /**

View File

@@ -1503,9 +1503,6 @@ public interface IAdasNetCommApi {
*/
long sendImgUploadCloudStatusQuery();
// TODO 需求暂停 待讨论
// boolean getRoutes();
/**
* 向工控机发送数据
*
@@ -1528,11 +1525,6 @@ public interface IAdasNetCommApi {
*/
void stopDispatchHandler();
/**
* Log是否开启打印
*/
void setEnableLog(boolean isEnableLog);
// /**
// * 获取与当前连接工控机兼容性

View File

@@ -15,13 +15,14 @@ public interface OnAdasConnectStatusListener {
/**
* 与工控机链接状态变化
*
* @param status {@link AdasConstants.IpcConnectionStatus}
* @param reason 连接信息需要判null,目前只有以下参数存在值其他全部为null
* {@link AdasConstants.IpcConnectionStatus#HEARTBEAT_TIMEOUT} 表示超时时间
* {@link AdasConstants.IpcConnectionStatus#CONNECT_EXCEPTION} 表示连接异常原因
* {@link AdasConstants.IpcConnectionStatus#SERVER_DISCONNECTED} 服务端断开如果服务端关闭时发送了原因将存在如果服务端关闭时未发送将为null
* @param callbackTime 回调时间
* @param status {@link AdasConstants.IpcConnectionStatus}
* @param reason 连接信息需要判null,目前只有以下参数存在值其他全部为null
* {@link AdasConstants.IpcConnectionStatus#HEARTBEAT_TIMEOUT} 表示超时时间
* {@link AdasConstants.IpcConnectionStatus#CONNECT_EXCEPTION} 表示连接异常原因
* {@link AdasConstants.IpcConnectionStatus#SERVER_DISCONNECTED} 服务端断开如果服务端关闭时发送了原因将存在如果服务端关闭时未发送将为null
*/
void onConnectionIPCStatus(AdasConstants.IpcConnectionStatus status, @Nullable String reason);
void onConnectionIPCStatus(long callbackTime, AdasConstants.IpcConnectionStatus status, @Nullable String reason);
/**
* 域控证书认证状态

View File

@@ -38,7 +38,7 @@ public class Constants {
/**
* 心跳协议
*/
public static final ByteString RAW_HEART_BEAT = ByteString.of((byte) 0x6e, (byte) 0x68, (byte) 0x6f, (byte) 0x69);
public static final ByteString RAW_HEARTBEAT = ByteString.of((byte) 0x6e, (byte) 0x68, (byte) 0x6f, (byte) 0x69);
public static final String DEFAULT_PING_ADDRESS_LIST = "[\"192.168.1.102\", \"192.168.8.102\"]";

View File

@@ -1,7 +1,10 @@
package com.zhidao.support.adas.high.common;
import android.os.SystemClock;
import com.zhjt.mogo.adas.data.AdasConstants;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -17,13 +20,21 @@ public class ReceiveTimeoutManager {
private static volatile ReceiveTimeoutManager INSTANCE;
private final AtomicBoolean isEnable = new AtomicBoolean(true);//是否启用超时检测
private long timeout = DEFAULT_TIMEOUT;//可配置超时时间
private double lastReceiveTime;//double 秒
private long timeoutPeriod;//超时时间,最后一次接收时间 + timeout
private final AtomicBoolean isHaveIpcHeartbeat = new AtomicBoolean(false);//域控是否存在心跳 false 没有或未知 true 域控有发送心跳
private long lastRefreshTime;//最后一次刷新时间
private long lastDataTimestamp;//isHaveIpcHeartbeat == true 表示最后一条域控心跳接收时间 isHaveIpcHeartbeat == false 表示Telematic最后一条数据发送时间Header中的timestamp
private OnTimeoutListener listener;
private volatile Timer timer;
public interface OnTimeoutListener {
void onTimeout(double time);
/**
* 超时回调
*
* @param isHaveIpcHeartbeat 域控是否存在心跳 false:没有或未知; true:域控有发送心跳
* @param lastDataTimestamp isHaveIpcHeartbeat == true 表示最后一条域控心跳接收时间 isHaveIpcHeartbeat == false 表示Telematic最后一条数据发送时间Header中的timestamp
* @param timeoutTime 超时时间
*/
void onTimeout(boolean isHaveIpcHeartbeat, long lastDataTimestamp, long timeoutTime);
}
public void setListener(OnTimeoutListener listener) {
@@ -48,6 +59,14 @@ public class ReceiveTimeoutManager {
return isEnable.get();
}
public boolean isHaveIpcHeartbeat() {
return isHaveIpcHeartbeat.get();
}
public void setHaveIpcHeartbeat(boolean isHave) {
isHaveIpcHeartbeat.set(isHave);
}
/**
* 是否启用服务器超时检测
*
@@ -72,8 +91,8 @@ public class ReceiveTimeoutManager {
*/
public void setTimeout(long timeout) {
//如果一启动 停止再启动 新timeout如果小于当前 老timeout将有可能出现时间差计算出错的情况
if (timeout < 1000L) {
timeout = 1000L;
if (timeout < 2000L) {
timeout = 2000L;
}
this.timeout = timeout;
if (timer != null) {
@@ -88,26 +107,31 @@ public class ReceiveTimeoutManager {
/**
* 刷新最后一次接收数据时间
* 如果 isHaveIpcHeartbeat true 表示域控发送心跳 timestamp表示域控发送心跳鹰眼接受到的时间
* 如果 isHaveIpcHeartbeat false 表示域控未发送心跳 timestamp表示域控发送数据中Header中的tel发送时间
*/
public void refreshLast(double timestamp) {
if (isEnable.get())
lastReceiveTime = timestamp;
public void refreshLast(long timestamp) {
if (isEnable.get()) {
lastRefreshTime = SystemClock.elapsedRealtime();
lastDataTimestamp = timestamp;
}
}
public synchronized void start() {
if (isEnable.get())
if (timer == null) {
timeoutPeriod = ((long) lastReceiveTime * 1000) + timeout;
lastRefreshTime = SystemClock.elapsedRealtime();
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
long difference = timeoutPeriod - ((long) lastReceiveTime * 1000);
timeoutPeriod = ((long) lastReceiveTime * 1000) + timeout;
CupidLogUtils.e(TAG, "最后一次接收时间=" + lastReceiveTime + " 时间差=" + difference + "ms");
if (difference >= timeout) {
long now = SystemClock.elapsedRealtime();
long difference = now - lastRefreshTime;
boolean isTimeout = difference >= timeout;
CupidLogUtils.e(TAG, buildTimeoutStr(isTimeout, isHaveIpcHeartbeat(), lastDataTimestamp, difference) + " 设备开机到现在的时间:" + now + "ms 最后一条数据刷新时间:" + lastRefreshTime + "ms");
if (isTimeout) {
if (listener != null) {
listener.onTimeout(difference / 1000.0);
listener.onTimeout(isHaveIpcHeartbeat(), lastDataTimestamp, difference);
}
}
}
@@ -122,5 +146,24 @@ public class ReceiveTimeoutManager {
}
}
public static String buildTimeoutStr(
boolean isTimeout,
boolean isHaveIpcHeartbeat,
long lastDataTimestamp,
long time
) {
String type = isTimeout
? "超时时间:"
: "最后刷新时间时间差:";
String type1 = isHaveIpcHeartbeat
? "最后一条Telematics心跳接收时间"
: "最后一条Telematics数据发送时间";
return String.format(Locale.getDefault(),
"%s%dms %s%d",
type,
time,
type1,
lastDataTimestamp
);
}
}

View File

@@ -82,7 +82,7 @@ public class ReconnectManager {
/**
* 启动重连
*/
public synchronized void start() {
public synchronized void start(long delay) {
if (isNeedReconnect()) {
if (!isReconnection()) {
CupidLogUtils.log(TAG, "开始重连");
@@ -101,7 +101,7 @@ public class ReconnectManager {
callOnReconnection(AdasConstants.IpcConnectionStatus.RECONNECTING_TIMER);
}
}
}, RECONNECT_INTERVAL, RECONNECT_INTERVAL);//延时
}, delay < 0 ? RECONNECT_INTERVAL : delay, RECONNECT_INTERVAL);//延时
}
}
}

View File

@@ -90,12 +90,14 @@ public class FpgaSocket implements IWebSocket {
.readTimeout(4, TimeUnit.SECONDS)
.connectTimeout(2, TimeUnit.SECONDS);
ReceiveTimeoutManager.getInstance().setListener(new ReceiveTimeoutManager.OnTimeoutListener() {
@Override
public void onTimeout(double time) {
public void onTimeout(boolean isHaveIpcHeartbeat, long lastDataTimestamp, long timeoutTime) {
setStatus(AdasConstants.IpcConnectionStatus.HEARTBEAT_TIMEOUT);
callConnectStatus(time);
callConnectStatus(isHaveIpcHeartbeat, lastDataTimestamp, timeoutTime);
close(false, 1001);
reconnectStart();
reconnectStart(10);
}
});
reconnectManager = new ReconnectManager(context, new ReconnectManager.OnReconnectListener() {
@@ -171,8 +173,12 @@ public class FpgaSocket implements IWebSocket {
* 启动重连功能
*/
private void reconnectStart() {
reconnectStart(-1);
}
private void reconnectStart(long delay) {
ReceiveTimeoutManager.getInstance().stop();
reconnectManager.start();
reconnectManager.start(delay);
}
private void reconnectStop(boolean isResetCurrentCount) {