[fea]
[add bridge module]
This commit is contained in:
yangyakun
2024-12-12 17:05:08 +08:00
parent 855c06b91c
commit ed30fe2694
160 changed files with 593 additions and 425 deletions

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.och.bridge">
</manifest>

View File

@@ -0,0 +1,174 @@
package com.mogo.och.bridge.autopilot;
import android.content.Context;
import androidx.annotation.Nullable;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotActionsListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatisticsListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotActionsListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotStatisticsListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerParallelDrivingActionsListenerManager;
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.utilcode.mogo.logger.Logger;
import com.mogo.eagle.core.utilcode.util.ParseVersionUtils;
import com.mogo.och.bridge.autopilot.callback.OchAdasStartFailureCallback;
import com.mogo.och.bridge.autopilot.line.LineManager;
import com.zhjt.mogo.adas.data.bean.AutopilotStatistics;
import com.zhjt.mogo.adas.data.bean.LaunchConditionData;
import com.zhjt.mogo.adas.data.bean.UnableLaunchReason;
import java.util.ArrayList;
import fsm.Fsm2024;
/**
* Created on 2022/10/9
* 工控机状态信息回调(判断是否能否启动自动驾驶的回调)
* 目前定的是3秒回调一次
*/
public class OCHAdasAbilityManager implements IMoGoAutopilotActionsListener, IMoGoAutopilotStatisticsListener {
private static final String TAG = OCHAdasAbilityManager.class.getSimpleName();
private boolean isAutopilotAbility;
private LaunchConditionData launchConditionData;
private ArrayList<UnableLaunchReason> unableAutopilotReasons;
private String startFailedCode = "";
private String startFailedMessage = "";
private OchAdasStartFailureCallback failureCallback = null;
private static final class SingletonHolder {
private static final OCHAdasAbilityManager INSTANCE = new OCHAdasAbilityManager();
}
public static OCHAdasAbilityManager getInstance() {
return SingletonHolder.INSTANCE;
}
public void init(Context context) {
this.isAutopilotAbility = CallerAutopilotActionsListenerManager.INSTANCE.isAutopilotAbility();
this.launchConditionData = CallerAutopilotActionsListenerManager.INSTANCE.getLaunchConditionData();
this.unableAutopilotReasons = CallerAutopilotActionsListenerManager.INSTANCE.getUnableAutopilotReasons();
initListeners();
}
public void setAdasStartFailureCallback(OchAdasStartFailureCallback callback){
failureCallback = callback;
}
public boolean getAutopilotAbilityStatus(){
return isAutopilotAbility;
}
public String getAbilityVersion() {
return launchConditionData == null ? "" : launchConditionData.abilityVersion;
}
public String getOriginalData() {
return launchConditionData == null ? "" : launchConditionData.getJson();
}
public ArrayList<UnableLaunchReason> getUnableAutopilotReasons() {
return unableAutopilotReasons;
}
public String getAutopilotUnAbilityReason(){
try {
if(unableAutopilotReasons==null||unableAutopilotReasons.isEmpty()){
return "未知异常";
}else {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < unableAutopilotReasons.size(); i++) {
stringBuilder.append(unableAutopilotReasons.get(i));
if(i<unableAutopilotReasons.size()-1){
stringBuilder.append("\n");
}
}
return stringBuilder.toString();
}
}catch (Exception e){
e.printStackTrace();
return "未知异常";
}
}
public String getStartFailedCode() {
return startFailedCode;
}
public String getStartFailedMessage() {
return startFailedMessage;
}
private void initListeners() {
//2022.10.9 工控机状态信息回调(判断是否能否启动自动驾驶的回调), 目前定的是3秒回调一次
CallerAutopilotActionsListenerManager.INSTANCE.addListener(TAG, this);
CallerAutopilotStatisticsListenerManager.INSTANCE.addListener(TAG,this);
}
private void releaseListeners() {
CallerAutopilotActionsListenerManager.INSTANCE.removeListener(this);
CallerAutopilotStatisticsListenerManager.INSTANCE.removeListener(this);
CallerParallelDrivingActionsListenerManager.INSTANCE.removeListener(TAG);
}
@Override
public void onAutopilotAbility(boolean isAutopilotAbility, @Nullable LaunchConditionData launchConditionData, @Nullable ArrayList<UnableLaunchReason> unableAutopilotReasons) {
this.isAutopilotAbility = isAutopilotAbility;
this.launchConditionData = launchConditionData;
this.unableAutopilotReasons = unableAutopilotReasons;
Logger.d(TAG, "是否可以启动自动驾驶=" + isAutopilotAbility + " 原因=" + (unableAutopilotReasons == null ? null : unableAutopilotReasons.toString()) + " 原始数据=" + (launchConditionData == null ? null : launchConditionData.getJson()));
if (unableAutopilotReasons != null && getMapVersion() < 30600) {
//刹车变化回调
Logger.d(TAG,"onAutopilotAbility = " + isAutopilotAbility +
" onAutopilotAbility =" + unableAutopilotReasons.toString());
if (unableAutopilotReasons.toString().contains(UnableLaunchReason.SourceType.CHASSIS.name())
&& unableAutopilotReasons.toString().contains(UnableLaunchReason.UnableType.CHASSIS_BRAKE.name())) {
//failureCallback.brakeStatusChanged(isAutopilotAbility);
}
}
}
@Override
public void onAutopilotStatistics(@Nullable AutopilotStatistics statistics) {
if (statistics == null) return;
Logger.d(TAG, "AutopilotStatistics= " + statistics.status);
if (failureCallback != null) {
if(statistics.status==1) {
if (statistics.fsmState != null) {
startFailedCode = "";
if (statistics.fsmState.hasSession()) {
Fsm2024.Session session = statistics.fsmState.getSession();
if (session.hasSessionFailReason()) {
startFailedMessage = session.getSessionFailReason();
}
}
} else if (statistics.failedMessage != null) {
startFailedCode = statistics.failedMessage.getCode();
startFailedMessage = statistics.failedMessage.getMsg();
}
failureCallback.onStartAutopilotFailure(startFailedCode, startFailedMessage);
if (!AppIdentityModeUtils.isSweeper(FunctionBuildConfig.appIdentityMode)) {
LineManager.invokeStartAutopilotFailure(startFailedCode, startFailedMessage);
}
Logger.d(TAG, String.format("statistics-startFailedCode = s%; startFailedMessage = s%",
startFailedCode, startFailedMessage));
}else if(statistics.status==0) {
//启动自驾成功
failureCallback.onStartAutopilotSuccess(statistics.source.toString());
LineManager.INSTANCE.triggerStartServiceEvent(true, 2,statistics.source.toString());
}
}
}
private int getMapVersion(){
return ParseVersionUtils.parseVersion(true, CallerAutoPilotStatusListenerManager.INSTANCE.getDockerVersion());
}
public void release() {
releaseListeners();
}
}

View File

@@ -0,0 +1,101 @@
package com.mogo.och.bridge.autopilot.autopilot;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.och.bridge.autopilot.autopilot.bean.ArrivedStation;
import com.zhjt.mogo.adas.data.AdasConstants;
import mogo.telematics.pad.MessagePad;
import mogo_msg.MogoReportMsg;
import system_master.SsmInfo;
import system_master.SystemStatusInfo;
public interface IOchAutopilotStatusListener {
/**
* 自驾路线触发下载请求回调
*/
default void onAutopilotTrajectoryDownloadReq(AutopilotControlParameters.AutoPilotLine autoPilotLine, int downloadType) {
}
/**
* 自动驾驶状态信息
*
* @param state 状态信息
*/
default void onAutopilotStatusResponse(int state) {
}
/**
* 自动驾驶docker信息
*
* @param dockerVersion docker版本
*/
default void onAutopilotDockerInfo(String dockerVersion) {
}
/**
* 自动驾驶到站
*
* @param arrivalNotification 所到车站的简单信息
*/
default void onAutopilotArriveAtStation(ArrivedStation arrivalNotification) {
}
/**
* 工控机获取SN
*/
default void onAutopilotSNRequest(MessagePad.BasicInfoReq basicInfoReq) {
}
/**
* 监控事件报告
*/
default void onAutopilotGuardian(MogoReportMsg.MogoReportMessage guardianInfo) {
}
/**
* 监控事件报告
*/
default void onAutopilotGuardian(MogoReportMsg.MogoReportMessage guardianInfo, long lineId) {
}
/**
* 工控机连接状态回调
*/
default void onAutopilotIpcConnectStatusChanged(AdasConstants.IpcConnectionStatus status, String reason) {
}
/**
* 工控机主动查询 AdasManager#sendStatusQueryReq(),后会收到如下回调
*/
default void onAutopilotStatusRespByQuery(SystemStatusInfo.StatusInfo status) {
}
/**
* 定频SSM
* 老版本 SSMSystemStatusInfo.StatusInfo HQ、M1 MAP350开始弃用其他车型MAP360开始弃用
*/
default void onSystemStatus(SsmInfo.SsmStatusInf statusInf) {
}
/**
* 自动驾驶路线ID回调
*/
default void onAutopilotRouteLineId(long lineId) {
}
/**
* 自动驾驶路线ID回调
*/
default void canStartAutopilot(boolean canStart) {
}
/**
* 自动驾驶状态信息 数据源来自底盘 不管是否存在FSM 都将进行底盘自驾状态的转发
*
* @param state 状态信息
*/
default void onAutopilotStatusResponseFromCan(int state) {}
default void onFsmCanStartAutopilot(boolean can){}
}

View File

@@ -0,0 +1,36 @@
package com.mogo.och.bridge.autopilot.autopilot;
public interface IOchEventKey {
// 无人化taxi event_key_unmanned_taxi_start_service *
// bus event_key_och_bus_start_service *
// shuttle event_key_och_bus_start_service *
// charter event_key_och_charter_start_service
// taxi event_key_och_taxi_start_service *
// 无人化taxiPa event_key_unmanned_taxi_start_service
String getEventKeyStartService();
// 无人化taxi event_key_unmanned_taxi_restart_autopilot *
// bus event_key_och_bus_restart_autopilot *
// shuttle event_key_och_bus_restart_autopilot *
// charter event_key_och_charter_restart_autopilot
// taxi event_key_och_taxi_restart_autopilot *
// 无人化taxiPa event_key_unmanned_taxi_restart_autopilot
String getEventKeyRestartService();
// 无人化taxi event_key_och_taxi_ap_unable_start_reason *
// bus event_key_och_bus_ap_unable_start_reason *
// shuttle event_key_och_bus_ap_unable_start_reason *
// charter event_key_och_charter_ap_unable_start_reason
// taxi event_key_och_taxi_ap_unable_start_reason *
// 无人化taxiPa event_key_och_taxi_ap_unable_start_reason
String getEventKeyApUnableStartReason();
// 无人化taxi event_key_unmanned_taxi_click_start_autopilot
// bus event_key_och_bus_click_start_autopilot
// shuttle event_key_och_bus_click_start_autopilot
// charter event_key_och_charter_click_start_autopilot
// taxi event_key_och_taxi_click_start_autopilot
// 无人化taxiPa event_key_och_taxi_passenger_click_start_autopilot
String getEventKeyClickStartAutopilot();
}

View File

@@ -0,0 +1,119 @@
package com.mogo.och.bridge.autopilot.autopilot
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.msgbox.AutopilotMsg
import com.mogo.eagle.core.data.msgbox.MsgBoxBean
import com.mogo.eagle.core.data.msgbox.MsgBoxType
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.msgbox.CallerMsgBoxManager
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.bridge.autopilot.OCHAdasAbilityManager
import com.mogo.och.bridge.autopilot.autopilot.bean.SessionWithTime
import com.mogo.och.bridge.autopilot.line.LineManager
import com.mogo.och.bridge.distance.TrajectoryAndDistanceManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import java.util.concurrent.atomic.AtomicReference
object OchAutoPilotManager {
const val TAG = "OchAutoPilotManager"
private val globalSessionId = AtomicReference<SessionWithTime>()
init {
}
@JvmStatic
fun canStartAutoPilotByDistance(lineId: Number?): String {
return TrajectoryAndDistanceManager.canStartAutopilot(lineId).apply {
if (!isNullOrBlank()) {
// 去启动绘制高精地图上的轨迹
val drawGlobalTrajectory = LineManager.drawGlobalTrajectory()
if (!drawGlobalTrajectory.first) {
ToastUtils.showLong(drawGlobalTrajectory.second)
}
}
}
}
/**
* 启动自驾中禁止再次启动自驾
*/
@JvmStatic
fun canStartAutopilotBySessionId(): Boolean {
val sessionInfo = globalSessionId.get()
val currentTimeMillis = System.currentTimeMillis()
if(sessionInfo==null){
return true
}else{
if(currentTimeMillis-sessionInfo.setTime>= OchAutopilotAnalytics.LOOP_PERIOD_16S){
clearGlobalSessionId("检测session 自带的时间 ${currentTimeMillis}_${sessionInfo.setTime}_${sessionInfo.sessionId}")
return true
}else{
ToastUtils.showLong("自驾启动中,请勿重复点击")
OchAutopilotAnalytics.triggerCanStartAutopilotBySessionId(globalSessionId.get())
return false
}
}
}
@JvmStatic
fun canStartAutopilotBySessionIdInner():Boolean{
OchChainLogManager.writeChainLogAutopilot("自驾流程","清理SessiongId")
return (globalSessionId.get()==null)
}
@JvmStatic
fun canStartAutoPilotSSM(): Boolean {
if (!FunctionBuildConfig.isDemoMode && !OCHAdasAbilityManager.getInstance().autopilotAbilityStatus) {
val reasons = OCHAdasAbilityManager.getInstance().unableAutopilotReasons
if ("AutopilotAbilityFSM" == OCHAdasAbilityManager.getInstance().abilityVersion && !reasons.isNullOrEmpty()) {
val msg = reasons[0].getUnableLaunchReason() + " 来源:" + reasons[0].source
CallerMsgBoxManager.saveMsgBox(
MsgBoxBean(
MsgBoxType.AUTOPILOT,
AutopilotMsg(0, "自动驾驶启动失败", msg, System.currentTimeMillis())
)
)
} else {
ToastUtils.showLong(
OCHAdasAbilityManager.getInstance().autopilotUnAbilityReason +
", 请稍候重试"
)
}
return false
}
return true
}
fun clearGlobalSessionId(log:String){
OchChainLogManager.writeChainLogAutopilot("自驾流程","清理SessiongId${globalSessionId}_${OchAutoPilotStatusListenerManager.fsmBackSessionId}_${log}")
globalSessionId.set(null)
}
@JvmStatic
fun startAutoPilot(controlParameters: AutopilotControlParameters?): Long {
val sessionId = CallerAutoPilotControlManager.startAutoPilot(controlParameters)
globalSessionId.set(SessionWithTime(sessionId,System.currentTimeMillis()))
OchAutoPilotStatusListenerManager.fsmBackSessionId.set(-1L)
OchAutopilotAnalytics.triggerStartAutopilotParameters(controlParameters, sessionId)
return sessionId
}
/**
* 启动自驾失败切 FSM返回的Session和启动SessionId 不同
*/
fun checkStartSessionAndFailSessionId(){
val sessionInfo = globalSessionId.get()
if(sessionInfo!=null) {
if (sessionInfo.sessionId != OchAutoPilotStatusListenerManager.fsmBackSessionId.get()) {
OchAutopilotAnalytics.triggerStartAutopilotFailCheckSessionDiff(
sessionInfo,
OchAutoPilotStatusListenerManager.fsmBackSessionId.get()
)
}
}
}
}

View File

@@ -0,0 +1,269 @@
package com.mogo.och.bridge.autopilot.autopilot
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoFsm2024Listener
import com.mogo.eagle.core.function.api.autopilot.IMoGoReceiveReceivedAckListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerFsm2024ListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerReceiveReceivedAckListenerManager
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.util.StringUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.common.module.R
import com.mogo.och.bridge.autopilot.autopilot.bean.ArrivedStation
import com.mogo.och.bridge.autopilot.line.LineManager
import com.mogo.och.bridge.autopilot.trajectory.TrajectoryManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.utils.CallerBase
import com.zhjt.mogo.adas.common.MessageType
import com.zhjt.mogo.adas.data.AdasConstants
import com.zhjt.mogo.adas.data.bean.ReceivedAck
import com.zhjt.mogo.adas.data.bean.ReceivedAck.Status
import fsm.Fsm2024
import mogo.telematics.pad.MessagePad
import mogo_msg.MogoReportMsg
import system_master.SsmInfo
import system_master.SystemStatusInfo
import java.util.concurrent.atomic.AtomicLong
import kotlin.properties.Delegates
object OchAutoPilotStatusListenerManager : CallerBase<IOchAutopilotStatusListener>(),IMoGoAutopilotStatusListener,
(Boolean) -> Unit, IMoGoFsm2024Listener, IMoGoReceiveReceivedAckListener {
const val TAG = "OCHAutoPilotStatusListenerManager"
val fsmBackSessionId = AtomicLong(-1L)
private var canStartAutopilot:Boolean? by Delegates.observable(null) { _, oldValue, newValue ->
if (oldValue != newValue) {
M_LISTENERS.forEach {
val listener = it.value
listener.canStartAutopilot(newValue?:false)
}
}
}
private var _canStartAutopilotFromFSM:Boolean by Delegates.observable(true) { _, oldValue, newValue ->
if (oldValue != newValue) {
M_LISTENERS.forEach {
val listener = it.value
listener.onFsmCanStartAutopilot(newValue)
}
}
}
val canStartAutopilotFromFSM:Boolean
get() = _canStartAutopilotFromFSM
private var _autopilotState: Int by Delegates.observable(0) { _, oldValue, newValue ->
if (oldValue != newValue) {
if (AppIdentityModeUtils.isDriver(FunctionBuildConfig.appIdentityMode)) {
if(oldValue==IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
if(newValue==IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE){
ToastUtils.showLong(R.string.common_change2_autopilot2_manual)
}
}else if(oldValue==IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING){
if(newValue==IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE){
ToastUtils.showLong(R.string.common_change2_pxjs_manual)
}
}
}
if(newValue==IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
LineManager.triggerStartServiceEvent(true, 0,"")
}
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotStatusResponse(newValue)
}
LineManager.searchAutopilotState()
}
}
val autopilotState:Int
get() = _autopilotState
init {
//2021.11.1 鹰眼架构整合由IMoGoAutopilotStatusListener逐步替代IMogoAdasOCHCallback接口
CallerAutoPilotStatusListenerManager.addListener(TAG, this)
CallerAutoPilotControlManager.addStartAutopilotStateListener(TAG,this)
CallerFsm2024ListenerManager.addListener(TAG,this)
CallerReceiveReceivedAckListenerManager.addListener(TAG,this)
}
override fun doSomeAfterAddListener(tag: String, listener: IOchAutopilotStatusListener) {
super.doSomeAfterAddListener(tag, listener)
listener.onAutopilotStatusResponse(_autopilotState)
}
override fun onAutopilotTrajectoryDownloadReq(
autoPilotLine: AutopilotControlParameters.AutoPilotLine,
downloadType: Int
) {
super.onAutopilotTrajectoryDownloadReq(autoPilotLine, downloadType)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotTrajectoryDownloadReq(autoPilotLine,downloadType)
}
}
override fun onAutopilotStatusResponse(state: Int) {
_autopilotState = state
}
override fun onAutopilotDockerInfo(dockerVersion: String) {
super.onAutopilotDockerInfo(dockerVersion)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotDockerInfo(dockerVersion)
}
}
override fun onAutopilotSNRequest(basicInfoReq: MessagePad.BasicInfoReq) {
super.onAutopilotSNRequest(basicInfoReq)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotSNRequest(basicInfoReq)
}
}
override fun onAutopilotGuardian(guardianInfo: MogoReportMsg.MogoReportMessage?) {
super.onAutopilotGuardian(guardianInfo)
var lineId = -1L
guardianInfo?.let {
val msg = guardianInfo.msg
try {
val regex = Regex("lineid:(\\d+)")
val matchResult = regex.find(msg)
if (matchResult != null) {
lineId = matchResult.groupValues[1].toLong()
}
} catch (e: Exception) {
}
if (lineId == -1L) {
try {
val regex = Regex("lineID=(\\d+)")
val matchResult = regex.find(msg)
if (matchResult != null) {
lineId = matchResult.groupValues[1].toLong()
}
} catch (e: Exception) {
}
}
}
TrajectoryManager.carDownLoadTrajectoryLog(guardianInfo?.code,lineId)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotGuardian(guardianInfo)
listener.onAutopilotGuardian(guardianInfo, lineId)
}
}
override fun onAutopilotIpcConnectStatusChanged(
status: AdasConstants.IpcConnectionStatus,
reason: String?
) {
super.onAutopilotIpcConnectStatusChanged(status, reason)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotIpcConnectStatusChanged(status,reason)
}
}
override fun onAutopilotStatusRespByQuery(status: SystemStatusInfo.StatusInfo) {
super.onAutopilotStatusRespByQuery(status)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotStatusRespByQuery(status)
}
}
override fun onSystemStatus(statusInf: SsmInfo.SsmStatusInf) {
super.onSystemStatus(statusInf)
M_LISTENERS.forEach {
val listener = it.value
listener.onSystemStatus(statusInf)
}
}
override fun onAutopilotRouteLineId(lineId: Long) {
super.onAutopilotRouteLineId(lineId)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotRouteLineId(lineId)
}
}
override fun invoke(canStartAutopilot: Boolean) {
OchAutoPilotStatusListenerManager.canStartAutopilot = canStartAutopilot
}
override fun onAutopilotStatusResponseFromCan(state: Int) {
super.onAutopilotStatusResponseFromCan(state)
LineManager.searchAutopilotState()
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotStatusResponseFromCan(state)
}
when (state) {
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING -> {
LineManager.triggerStartServiceEvent(true,1,"")
}
}
}
override fun onAutopilotArriveAtStation(arrivalNotification: MessagePad.ArrivalNotification?) {
super.onAutopilotArriveAtStation(arrivalNotification)
val trasform = ArrivedStation.trasform(arrivalNotification)
M_LISTENERS.forEach {
val listener = it.value
listener.onAutopilotArriveAtStation(trasform)
}
}
override fun onAutoPilotStation(
token: Long,
timestamp: Long,
autoPilotStation: SsmInfo.AutoPilotStation?
) {
OchChainLogManager.writeChainLogAutopilot("到站逻辑",
"token:$token 通过主动查询到站底盘传递:${autoPilotStation?.orderId}_${autoPilotStation?.arrivedStationFlag}")
autoPilotStation?.let {
if (it.hasOrderId() && !StringUtils.isEmpty(it.orderId)&&it.hasArrivedStationFlag()) {
LineManager.invokeArrivedStation(it.orderId,it.arrivedStationFlag)
}
}
}
override fun onAutoPilotInfo(token: Long, timestamp: Long, autoPilotInfo: SsmInfo.AutoPilotInfo?) {
OchChainLogManager.writeChainLogAutopilot("自驾状态",
"token:$token 通过主动查询到站底盘传递:${autoPilotInfo?.orderId}_第一次启动自驾${autoPilotInfo?.firstAutopilotFlag}_次数${autoPilotInfo?.count}")
autoPilotInfo?.let {
if (it.hasOrderId() && !StringUtils.isEmpty(it.orderId)) {
LineManager.invokeSetIsFirstAutopilot(it.orderId,it.firstAutopilotFlag,it.count)
}
}
}
override fun onFSM2024State(fsmState: Fsm2024.FSMStateMsg) {
fsmBackSessionId.set(fsmState.failToPilotSessionId)
_canStartAutopilotFromFSM = fsmState.pilotStandbyFlag
}
override fun onReceiveReceivedAck(receivedAck: ReceivedAck) {
if (receivedAck.messageType == MessageType.TYPE_SEND_SET_AUTOPILOT_MODE_REQ) {
OchAutopilotAnalytics.triggerStartAutopilotParametersAck(
receivedAck.toString(),
receivedAck.status == Status.NORMAL
)
if(receivedAck.status==Status.NORMAL){
// 底盘接受成功
LineManager.invokeStartAutopilotAckSuccess(receivedAck)
}
}
}
}

View File

@@ -0,0 +1,320 @@
package com.mogo.och.bridge.autopilot.autopilot
import android.text.TextUtils
import com.mogo.commons.debug.DebugConfig
import com.mogo.commons.utils.MogoAnalyticUtils
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.DateTimeUtils
import com.mogo.och.bridge.autopilot.OCHAdasAbilityManager
import com.mogo.och.bridge.autopilot.autopilot.bean.SessionWithTime
import com.mogo.och.bridge.autopilot.line.LineManager
import com.mogo.och.common.module.exception.InitException
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.utils.RxUtils
import io.reactivex.disposables.Disposable
object OchAutopilotAnalytics {
private const val EVENT_PARAM_AUTOPILOTANALYTICS_GROUP = "start_autopilot_group"
private const val EVENT_PARAM_ENV_ONLINE = "env_online" // 是否线上环境true/false
private const val EVENT_PARAM_START_SUCCESS_TIME = "time_success"
private const val EVENT_PARAM_START_FAIL_TIME = "time_fail"
private const val EVENT_PARAM_CLICK_TIME = "time_click"
private const val EVENT_PARAM_START_NAME = "start_name"
private const val EVENT_PARAM_END_NAME = "end_name"
private const val EVENT_PARAM_LINE_ID = "line_id"
private const val EVENT_PARAM_ORDER_NUMBER = "order_num"
private const val EVENT_PARAM_AUTOPILOT_FROM = "AUTOPILOT_FROM"
private const val EVENT_PARAM_UNABLE_START_REASON = "unable_start_reason"
private const val EVENT_PARAM_MAP_ORIGINAL_DATA = "map_original_data" // 域控原始状态信息
private const val EVENT_PARAM_AUTOPILOT_STATE = "autopilot_state" //原始的自动驾驶状态
private const val EVENT_PARAM_START_RESULT = "start_autopilot" // true/false
private const val EVENT_PARAM_START_RESULT_SOURCE = "start_autopilot_source" // true/false
private const val EVENT_PARAM_START_FAILURE_CODE = "start_autopilot_failure_code" // 启动自驾失败code
private const val EVENT_PARAM_START_FAILURE_MSG = "start_autopilot_failure_msg" // 启动自驾失败原因
private const val EVENT_PARAM_START_AUTOPILOT_SESSION_ID = "start_autopilot_cmd_session_id" //启动自驾命令请求的sessionId
private const val EVENT_KEY_START_AUTOPILOT_PARAMETERS = "event_key_och_start_autopilot_parameters"
private const val EVENT_PARAM_START_AUTOPILOT_PARAMETERS= "start_autopilot_parameters" // 启动自驾参数
private const val EVENT_KEY_START_AUTOPILOT_ACK = "event_key_och_start_autopilot_ack"
private const val EVENT_PARAM_START_AUTOPILOT_ACK= "start_autopilot_parameters_ack" // 启动自驾参数
private const val EVENT_PARAM_START_AUTOPILOT_ACK_SUCCESS= "start_autopilot_parameters_ack_isSuccess" // 启动自驾参数
private const val EVENT_KEY_INFO_AUTOPILOT_DISTANCE = "event_key_vehicle_start_autopilot_state_distance_15"
private const val EVENT_KEY_VEHICLE_START_AUTOPILOT_CMD_SESSION_DUPLICATED = "event_key_vehicle_start_autopilot_cmd_session_duplicated"
private const val EVENT_KEY_VEHICLE_START_AUTOPILOT_CMD_SESSION_DIFF = "event_key_vehicle_start_autopilot_cmd_session_diff"
private const val EVENT_PARAM_VEHICLE_START_AUTOPILOT_CMD_SESSION_ID_START = "cmd_session_id_start"
private const val EVENT_PARAM_VEHICLE_START_AUTOPILOT_CMD_SESSION_ID_FSM_RETURN = "cmd_session_id_fsm_return"
private val LOOP_PERIOD_15S = 15 * 1000L
val LOOP_PERIOD_16S = 16 * 1000L
private var mStartAutopilotKey: String? = null
private val mStartAutopilotParams = HashMap<String, Any>()
var ochEventKey: IOchEventKey? = null
/**
* 正式启动自驾把参数传给底层
*/
fun triggerStartAutopilotParameters(controlParameters: AutopilotControlParameters?, sessionId: Long){
val params = HashMap<String, Any>()
params[EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
params[EVENT_PARAM_START_AUTOPILOT_SESSION_ID] = sessionId
params[EVENT_PARAM_START_AUTOPILOT_PARAMETERS] = "${controlParameters.toString()}_${controlParameters?.autoPilotLine}_routeID:${controlParameters?.routeID}_routeName:${controlParameters?.routeName}"
params[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.addCommonParams(params)
MogoAnalyticUtils.track(EVENT_KEY_START_AUTOPILOT_PARAMETERS, params)
}
/**
* 底盘收到启动自驾的ack
*/
fun triggerStartAutopilotParametersAck(data:String,isSuccess:Boolean){
val params = HashMap<String, Any>()
params[EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
params[EVENT_PARAM_START_AUTOPILOT_ACK] = data
params[EVENT_PARAM_START_AUTOPILOT_ACK_SUCCESS] = isSuccess
params[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.addCommonParams(params)
MogoAnalyticUtils.track(EVENT_KEY_START_AUTOPILOT_ACK, params)
}
/**
* 启动自驾判断距离
*/
fun triggerDistance2LineorStation(info: String){
val map = hashMapOf<String, Any>()
map[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.writeChainLog("启动自驾距离判断",info, eventID = EVENT_KEY_INFO_AUTOPILOT_DISTANCE, patch = map)
}
/**
* 等待底盘返回的间隙 重复启动自驾
*/
fun triggerCanStartAutopilotBySessionId(globalSessionId : SessionWithTime){
val map = hashMapOf<String, Any>()
map[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.writeChainLog("启动自驾Session判断","已有globalSessionId:${globalSessionId.sessionId}_创建时间${globalSessionId.setTime}", eventID = EVENT_KEY_VEHICLE_START_AUTOPILOT_CMD_SESSION_DUPLICATED, patch = map)
}
/**
* 启动失败后 对比两个session是否相同
*/
fun triggerStartAutopilotFailCheckSessionDiff(globalSessionId: SessionWithTime, fsmBackSessionId: Long){
val map = hashMapOf<String, Any>()
map[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
map[EVENT_PARAM_VEHICLE_START_AUTOPILOT_CMD_SESSION_ID_START] = "${globalSessionId.sessionId}_${globalSessionId.setTime}"
map[EVENT_PARAM_VEHICLE_START_AUTOPILOT_CMD_SESSION_ID_FSM_RETURN] = fsmBackSessionId
OchChainLogManager.writeChainLog("启动自驾失败且SessionId不同","globalSessionId:${globalSessionId}——————fsmBackSessionId:${fsmBackSessionId}", eventID = EVENT_KEY_VEHICLE_START_AUTOPILOT_CMD_SESSION_DIFF, patch = map)
}
private var timeOutDisposable: Disposable?=null
/**
* 底盘明确告知启动自驾失败
*/
fun triggerStartAutopilotFailureEventByAdas(failCode: String, failMsg: String, startFailDate: Long) {
OchChainLogManager.writeChainLogAutopilot("自驾流程","启动自驾失败回调${failCode}_${failMsg}")
if(OchAutoPilotManager.canStartAutopilotBySessionIdInner()){
CallerLogger.e(SceneConstant.M_OCHCOMMON + "triggerStartAutopilotFailureEvent canStartAutopilotBySessionIdInner == false")
OchChainLogManager.writeChainLogAutopilot("自驾流程","异常情况 启动自驾失败回调${failCode}_${failMsg}_sessiong为空")
return
}
RxUtils.disposeSubscribe(timeOutDisposable)
// 判断Session 是否相同
OchAutoPilotManager.checkStartSessionAndFailSessionId()
triggerStartAutopilotFailureEvent(failCode, failMsg, startFailDate)
}
private fun clearStartAutopilotParams() {
mStartAutopilotParams.clear()
}
private fun triggerStartAutopilotFailureEvent(failCode: String, failMsg: String, startFailDate: Long) {
OchAutoPilotManager.clearGlobalSessionId("$failMsg _ ${failCode}")
if (mStartAutopilotParams.isEmpty()) {
return
}
CallerLogger.e(SceneConstant.M_OCHCOMMON + "triggerStartAutopilotFailureEvent", failMsg)
if (CallerAutoPilotStatusListenerManager.getState() != IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
mStartAutopilotParams[EVENT_PARAM_START_FAILURE_CODE] = failCode
mStartAutopilotParams[EVENT_PARAM_START_FAILURE_MSG] = failMsg
}
mStartAutopilotParams[EVENT_PARAM_MAP_ORIGINAL_DATA] = OCHAdasAbilityManager.getInstance().originalData
mStartAutopilotParams[EVENT_PARAM_AUTOPILOT_STATE] = CallerAutoPilotStatusListenerManager.getState()
mStartAutopilotParams[EVENT_PARAM_START_FAIL_TIME] = DateTimeUtils.getTimeText(startFailDate,DateTimeUtils.yyyy_MM_dd_HH_mm_ss_SSS)
mStartAutopilotParams[EVENT_PARAM_START_RESULT] = CallerAutoPilotStatusListenerManager.getState() == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING
mStartAutopilotParams[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.addCommonParams(mStartAutopilotParams)
MogoAnalyticUtils.track(mStartAutopilotKey, mStartAutopilotParams)
clearStartAutopilotParams() //清空参数数据,防止误传
}
/**
* 触发'开启自动驾驶'埋点流程
* 开启自动驾驶15s内成功则发送成功埋点否则发送失败埋点
* @param restart false点击'滑动出发'启动)/true接管后点击'自动驾驶'按钮启动)
* @param send 是否直接发送埋点15s内开启成功则直接发送成功埋点
*/
fun triggerStartAutopilotEvent(
restart: Boolean,
send: Boolean,
startName: String,
endName: String,
lineId: Int,
orderId:String?,
triggerDate: Long,
type:String? = "",
source:Int = 0
) {
OchChainLogManager.writeChainLogAutopilot("自驾流程","send:${send}_${restart}_${startName}_${endName}_${lineId}_${orderId}_${source}_${triggerDate}")
mStartAutopilotKey = if (restart) getEventKeyRestartService() else getEventKeyStartService()
if (send) {
if(OchAutoPilotManager.canStartAutopilotBySessionIdInner()){
CallerLogger.e(SceneConstant.M_OCHCOMMON + "triggerStartAutopilotEvent canStartAutopilotBySessionIdInner == false")
OchChainLogManager.writeChainLogAutopilot("自驾流程","异常情况 启动自驾成功_sessiong为空")
return
}
OchAutoPilotManager.clearGlobalSessionId("启动自驾成功")
RxUtils.disposeSubscribe(timeOutDisposable)
if (mStartAutopilotParams.isEmpty()) {
return
}
// 开启成功,上报埋点
mStartAutopilotParams[EVENT_PARAM_START_FAILURE_CODE] = ""
mStartAutopilotParams[EVENT_PARAM_START_FAILURE_MSG] = ""
mStartAutopilotParams[EVENT_PARAM_START_RESULT] = true
mStartAutopilotParams[EVENT_PARAM_AUTOPILOT_FROM] = type?:""
mStartAutopilotParams[EVENT_PARAM_START_RESULT_SOURCE] = source
// 自动驾驶状态变更时间
mStartAutopilotParams[EVENT_PARAM_START_SUCCESS_TIME] = DateTimeUtils.getTimeText(triggerDate,DateTimeUtils.yyyy_MM_dd_HH_mm_ss_SSS)
mStartAutopilotParams[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.addCommonParams(mStartAutopilotParams)
MogoAnalyticUtils.track(mStartAutopilotKey, mStartAutopilotParams)
clearStartAutopilotParams() //清空参数数据,防止误传
} else {
mStartAutopilotParams[EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
// 外部点击时间
mStartAutopilotParams[EVENT_PARAM_CLICK_TIME] = DateTimeUtils.getTimeText(triggerDate,DateTimeUtils.yyyy_MM_dd_HH_mm_ss_SSS)
mStartAutopilotParams[EVENT_PARAM_START_NAME] = startName
mStartAutopilotParams[EVENT_PARAM_END_NAME] = endName
mStartAutopilotParams[EVENT_PARAM_LINE_ID] = lineId
mStartAutopilotParams[EVENT_PARAM_ORDER_NUMBER] = orderId?:""
RxUtils.disposeSubscribe(timeOutDisposable)
timeOutDisposable = RxUtils.createSubscribe(LOOP_PERIOD_15S) {
triggerStartAutopilotFailureEvent("", "15s后app等待超时", System.currentTimeMillis())
LineManager.invokeStartAutopilotTimeOut()
}
}
}
/**
* 触发更新 启动自驾命令 发送的sessionId
*/
fun triggerUpdateStartAutoPilotSessionId(sessionId: Long) {
if (TextUtils.isEmpty(mStartAutopilotKey)) {
CallerLogger.e(SceneConstant.M_OCHCOMMON + "triggerUpdateStartAutoPilotSessionId but mStartAutopilotKey is null [sessionId = $sessionId] ")
return
}
if (mStartAutopilotParams.isEmpty()) {
CallerLogger.e(SceneConstant.M_OCHCOMMON + "triggerUpdateStartAutoPilotSessionId but mStartAutopilotParams is Empty [sessionId = $sessionId] ")
return
}
mStartAutopilotParams[EVENT_PARAM_START_AUTOPILOT_SESSION_ID] = sessionId
}
/**
* 触发"无法开启自驾已知异常"埋点
* @param startName
* @param endName
* @param lineId
*/
fun triggerUnableStartAPReasonEvent(
startName: String, endName: String, lineId: String, orderNo:String ,reason: String
) {
val params = HashMap<String, Any>()
params[EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
params[EVENT_PARAM_START_NAME] = startName
params[EVENT_PARAM_END_NAME] = endName
params[EVENT_PARAM_LINE_ID] = lineId
params[EVENT_PARAM_ORDER_NUMBER] = orderNo
params[EVENT_PARAM_UNABLE_START_REASON] = reason
params[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.addCommonParams(mStartAutopilotParams)
MogoAnalyticUtils.track(getEventKeyApUnableStartReason(), params)
}
/**
* 用户点击启动自驾的时间
*/
fun triggerClickStartAutopilotTime(triggerDate: Long) {
val params = HashMap<String, Any>()
params[EVENT_PARAM_ENV_ONLINE] = DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE
params[EVENT_PARAM_CLICK_TIME] = DateTimeUtils.getTimeText(triggerDate,DateTimeUtils.yyyy_MM_dd_HH_mm_ss_SSS)
params[EVENT_PARAM_AUTOPILOTANALYTICS_GROUP] = EVENT_PARAM_AUTOPILOTANALYTICS_GROUP
OchChainLogManager.addCommonParams(mStartAutopilotParams)
MogoAnalyticUtils.track(getEventKeyClickStartAutopilot(), params)
}
// 无人化taxi event_key_unmanned_taxi_start_service *
// bus event_key_och_bus_start_service *
// shuttle event_key_och_bus_start_service *
// charter event_key_och_charter_start_service
// taxi event_key_och_taxi_start_service *
// 无人化taxiPa event_key_unmanned_taxi_start_service
fun getEventKeyStartService(): String{
if(ochEventKey ==null){
throw InitException("请设置启动自驾埋点key")
}
return ochEventKey!!.getEventKeyStartService()
}
// 无人化taxi event_key_unmanned_taxi_restart_autopilot *
// bus event_key_och_bus_restart_autopilot *
// shuttle event_key_och_bus_restart_autopilot *
// charter event_key_och_charter_restart_autopilot
// taxi event_key_och_taxi_restart_autopilot *
// 无人化taxiPa event_key_unmanned_taxi_restart_autopilot
fun getEventKeyRestartService(): String{
if(ochEventKey ==null){
throw InitException("请设置启动自驾埋点key")
}
return ochEventKey!!.getEventKeyRestartService()
}
// 无人化taxi event_key_och_taxi_ap_unable_start_reason *
// bus event_key_och_bus_ap_unable_start_reason *
// shuttle event_key_och_bus_ap_unable_start_reason *
// charter event_key_och_charter_ap_unable_start_reason
// taxi event_key_och_taxi_ap_unable_start_reason *
// 无人化taxiPa event_key_och_taxi_ap_unable_start_reason
fun getEventKeyApUnableStartReason(): String{
if(ochEventKey ==null){
throw InitException("请设置启动自驾埋点key")
}
return ochEventKey!!.getEventKeyApUnableStartReason()
}
// 无人化taxi event_key_unmanned_taxi_click_start_autopilot
// bus event_key_och_bus_click_start_autopilot
// shuttle event_key_och_bus_click_start_autopilot
// charter event_key_och_charter_click_start_autopilot
// taxi event_key_och_taxi_click_start_autopilot
// 无人化taxiPa event_key_och_taxi_passenger_click_start_autopilot
fun getEventKeyClickStartAutopilot(): String{
if(ochEventKey ==null){
throw InitException("请设置启动自驾埋点key")
}
return ochEventKey!!.getEventKeyClickStartAutopilot()
}
}

View File

@@ -0,0 +1,37 @@
package com.mogo.och.bridge.autopilot.autopilot.bean;
import com.mogo.eagle.core.data.map.MogoLocation;
import mogo.telematics.pad.MessagePad;
public class ArrivedStation {
public ArrivedStation(MogoLocation mogoLocation) {
this.mogoLocation = mogoLocation;
}
private MogoLocation mogoLocation;
public MogoLocation getEndLocation(){
return mogoLocation;
}
public static ArrivedStation trasform(MessagePad.ArrivalNotification arrivalNotification){
MogoLocation mogoLocation = new MogoLocation();
MessagePad.Location endLocation = arrivalNotification.getEndLocation();
if(endLocation==null){
return null;
}
mogoLocation.setLongitude(endLocation.getLongitude());
mogoLocation.setLatitude(endLocation.getLatitude());
return new ArrivedStation(mogoLocation);
}
@Override
public String toString() {
return "ArrivedStation{" +
"mogoLocation=" + mogoLocation +
'}';
}
}

View File

@@ -0,0 +1,3 @@
package com.mogo.och.bridge.autopilot.autopilot.bean
data class SessionWithTime(val sessionId:Long,val setTime:Long)

View File

@@ -0,0 +1,11 @@
package com.mogo.och.bridge.autopilot.callback
/**
* @author: wangmingjun
* @date: 2022/11/9
*/
interface OchAdasStartFailureCallback {
fun onStartAutopilotFailure(startFailedCode : String, startFailedMessage : String)
fun onStartAutopilotSuccess(type:String)
}

View File

@@ -0,0 +1,22 @@
package com.mogo.och.bridge.autopilot.line;
public interface ILineCallback {
default void clearLineSuccess(){}
default void drawLineSuccess(){}
default void drawLineFail(){}
default void sendStartAutopilotSuccess(){}
default void startAutopilotTimeOut(){}
default void sendStartAutopilotSuccessAck(){}
default void startAutopilotFailure(String startFailedCode,String startFailedMessage){}
default void startAutopilotSuccess(){}
default void arrivedStationSuccessBySearch(){}
}

View File

@@ -0,0 +1,559 @@
package com.mogo.och.bridge.autopilot.line
import android.text.TextUtils
import com.elegant.network.utils.GsonUtil
import com.mogo.commons.env.ProjectUtils
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters.AutoPilotLine
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters.AutoPilotLonLat
import com.mogo.eagle.core.data.config.FunctionBuildConfig
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.data.och.OchInfo
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.datacenter.CallerDataCenterBizListener
import com.mogo.eagle.core.function.call.map.CallerMapGlobalTrajectoryDrawManager
import com.mogo.eagle.core.function.call.och.CallerEagleBaseFunctionCall4OchManager
import com.mogo.eagle.core.utilcode.mogo.AppIdentityModeUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.e
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_BUS
import com.mogo.eagle.core.utilcode.util.CoordinateUtils
import com.mogo.eagle.core.utilcode.util.StringUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.common.module.constant.OchCommonConst
import com.mogo.och.bridge.autopilot.OCHAdasAbilityManager
import com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotManager
import com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotManager.startAutoPilot
import com.mogo.och.bridge.autopilot.autopilot.OchAutopilotAnalytics
import com.mogo.och.bridge.autopilot.location.OchLocationManager
import com.mogo.och.bridge.distance.TrajectoryAndDistanceManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.utils.CallerBase
import com.mogo.och.common.module.voice.VoiceNotice
import com.mogo.och.data.bean.BusStationBean
import com.mogo.och.data.bean.ContraiInfo
import com.mogo.och.data.bean.LineInfo
import com.zhjt.mogo.adas.data.bean.ReceivedAck
import kotlin.properties.Delegates
/**
* 订单中
*/
object LineManager : CallerBase<ILineCallback>() {
const val TAG = "LineManager"
/**
* 线路信息
*/
@JvmStatic
private var _lineInfos: LineInfo? = null
val lineInfos:LineInfo?
@JvmStatic
get() = _lineInfos
/**
* 启动自驾信息
*/
@JvmStatic
private var _contraiInfo: ContraiInfo? = null
val contraiInfo:ContraiInfo?
@JvmStatic
get() = _contraiInfo
/**
* 起始站点
*/
private var startStation: BusStationBean? = null
/**
* 结束站点
*/
private var endStation: BusStationBean? = null
var isFirstStartAutopilot = true
/**
* 线路、轨迹、站点 三者确定的id
*/
private var autopilotId: String by Delegates.observable("") { _, oldValue, newValue ->
if (oldValue != newValue) {
CallerEagleBaseFunctionCall4OchManager.setOchAutopilotOrderId(newValue)
isFirstStartAutopilot = true
if(!AppIdentityModeUtils.isBus(FunctionBuildConfig.appIdentityMode)&&!AppIdentityModeUtils.isShuttle(FunctionBuildConfig.appIdentityMode)){
val (start, end) = getStations()
if(start!=null&&end!=null){
val ochInfo = OchInfo(0, mutableListOf(start.toMogoLocation(), end.toMogoLocation()))
CallerDataCenterBizListener.invokeOchInfo(ochInfo)
OchChainLogManager.writeChainLogMap("向地图传参数", "参数信息:${ochInfo}")
}else{
val ochInfo = OchInfo(0, mutableListOf())
CallerDataCenterBizListener.invokeOchInfo(ochInfo)
OchChainLogManager.writeChainLogMap("向地图传参数", "参数信息:${ochInfo}")
}
}
}
}
// 自车定位
private val mMapLocationListener = object : IMoGoChassisLocationGCJ02Listener {
override fun onChassisLocationGCJ02(mogoLocation: MogoLocation?) {
if (null == mogoLocation) return
getStations { start, end ->
val startLon = end.gcjLon
val startLat = end.gcjLat
val distance = CoordinateUtils.calculateLineDistance(
startLon, startLat,
mogoLocation.longitude, mogoLocation.latitude
)
if (distance <= OchCommonConst.ARRIVE_AT_END_STATION_DISTANCE) {
val token = CallerAutoPilotControlManager.sendSsmFuncQueryAutoPilotStation(autopilotId)
OchChainLogManager.writeChainLogAutopilot("到站逻辑","距离站点:$distance 请求token$token")
}
}
}
}
fun invokeArrivedStation(orderId: String, arrivedStationFlag: Boolean) {
if (this.autopilotId==orderId&&arrivedStationFlag){
M_LISTENERS.forEach {
it.value.arrivedStationSuccessBySearch()
}
}
}
fun invokeSetIsFirstAutopilot(orderId: String?, firstAutopilotFlag: Boolean, count: Int) {
if (this.autopilotId==orderId){
if(count>=1){
isFirstStartAutopilot = false
}else{
isFirstStartAutopilot = true
}
}
}
/**
* 设置站点信息
*/
fun setStartAndEndStation(startStation: BusStationBean?, endStation: BusStationBean?) {
if(this.startStation!=startStation||this.endStation!=endStation){
isFirstStartAutopilot = true
}
this.startStation = startStation
this.endStation = endStation
if(startStation==null||endStation==null){
clearAutopilotControlParameters()
}else {
setAutopilotControlParameters()
}
OchChainLogManager.writeChainLogAutopilot("自驾参数", "站点信息:${startStation}---${endStation}")
}
fun setContraiInfo(contraiInfo: ContraiInfo?){
this._contraiInfo = contraiInfo
setAutopilotControlParameters()
OchChainLogManager.writeChainLogAutopilot("自驾参数", "轨迹信息:${contraiInfo}")
}
@JvmStatic
fun setLineInfo(lineInfo: LineInfo?) {
if (lineInfo == null) {
clearGlobalTrajectory(true)
CallerEagleBaseFunctionCall4OchManager.updateOrderLine("")
}
_lineInfos = lineInfo
setAutopilotControlParameters()
_lineInfos?.let { line ->
if (ProjectUtils.isSaas()) {
val sb = StringBuilder()
sb.append(line.lineName)
line.multiMap?.forEach {
sb.append(it.value)
}
CallerEagleBaseFunctionCall4OchManager.updateOrderLine(sb.toString())
}
}
OchChainLogManager.writeChainLogAutopilot("自驾参数", "线路信息:$contraiInfo")
}
fun getStations(): Pair<BusStationBean?, BusStationBean?> {
return Pair(startStation, endStation)
}
fun getStations(function: (start: BusStationBean, end: BusStationBean) -> Unit) {
startStation?.let { start ->
endStation?.let { end ->
function.invoke(start, end)
return
}
}
OchChainLogManager.writeChainLog("异常情况","startStation:${startStation}__endStation:$endStation")
}
fun getStationsWithLine(function: (start: BusStationBean, end: BusStationBean, lineInfo: LineInfo) -> Unit) {
startStation?.let { start ->
endStation?.let { end ->
_lineInfos?.let { line ->
function.invoke(start, end, line)
return
}
}
}
OchChainLogManager.writeChainLog(
"异常情况",
"startStation:${startStation}__endStation:${endStation}__lineInfos:$_lineInfos"
)
}
fun getStationsWithLineAndContrai(function: (start: BusStationBean, end: BusStationBean, lineInfo: LineInfo, contrai: ContraiInfo) -> Unit) {
startStation?.let { start ->
endStation?.let { end ->
_lineInfos?.let { line ->
_contraiInfo?.let { contrai ->
function.invoke(start, end, line, contrai)
return
}
}
}
}
OchChainLogManager.writeChainLog(
"异常情况",
"startStation:${startStation}__endStation:${endStation}__lineInfos:${_lineInfos}__contraiInfo:$_contraiInfo"
)
}
fun getStartStation(function: (start: BusStationBean) -> Unit) {
startStation?.let { start ->
function.invoke(start)
return
}
OchChainLogManager.writeChainLog("异常情况", "startStation:$startStation")
}
fun getLineInfo(function: (lineInfo: LineInfo) -> Unit){
_lineInfos?.let { line->
function.invoke(line)
return
}
OchChainLogManager.writeChainLog("异常情况", "lineInfos:$_lineInfos")
}
override fun doSomeAfterAddListener(tag: String, listener: ILineCallback) {
super.doSomeAfterAddListener(tag, listener)
if (hasDrawnGlobalTrajectory()) {
listener.drawLineSuccess()
} else {
listener.drawLineFail()
}
}
private fun setAutopilotControlParameters(){
getStationsWithLineAndContrai { start, end, lineInfo, contrai ->
val parameters = initAutopilotControlParameters()
if (null == parameters) {
e(M_BUS + TAG, "AutopilotControlParameters is empty.")
return@getStationsWithLineAndContrai
}
d(M_BUS + TAG, "AutopilotControlParameters is update.")
CallerAutoPilotStatusListenerManager.updateAutopilotControlParameters(parameters)
val startStationLocation = MogoLocation()
startStationLocation.latitude = start.gcjLat
startStationLocation.longitude = start.gcjLon
val endStationLocation = MogoLocation()
endStationLocation.latitude = end.gcjLat
endStationLocation.longitude = end.gcjLon
TrajectoryAndDistanceManager.setStationPoint(startStationLocation, endStationLocation, lineInfo.lineId)
OchLocationManager.addGCJ02Listener(TAG,1, mMapLocationListener)
// 恢复启动自驾信息
searchAutopilotState()
}
}
fun searchAutopilotState(){
CallerAutoPilotControlManager.sendSsmFuncQueryAutoPilotInfo()
}
private fun clearAutopilotControlParameters(){
CallerAutoPilotStatusListenerManager.updateAutopilotControlParameters(null)
TrajectoryAndDistanceManager.setStationPoint(null, null, null)
autopilotId = ""
OchLocationManager.removeGCJ02Listener(TAG)
}
fun hasDrawnGlobalTrajectory(): Boolean {
return CallerMapGlobalTrajectoryDrawManager.hasDrawnGlobalTrajectory()
}
fun clearGlobalTrajectory(isClearData: Boolean) {
CallerMapGlobalTrajectoryDrawManager.clearGlobalTrajectory(isClearData)
if (!hasDrawnGlobalTrajectory()) {
M_LISTENERS.forEach {
it.value.clearLineSuccess()
}
}
}
fun drawGlobalTrajectory(): Pair<Boolean, String> {
if (_lineInfos == null) {
return Pair(false, "请设置正确线路或订单")
}
return CallerMapGlobalTrajectoryDrawManager.drawGlobalTrajectory().apply {
if (first) {
M_LISTENERS.forEach {
it.value.drawLineSuccess()
}
} else {
M_LISTENERS.forEach {
it.value.drawLineFail()
}
}
}
}
fun initAutopilotControlParameters(): AutopilotControlParameters? {
var parameters: AutopilotControlParameters? = null
getStationsWithLineAndContrai { start, end, lineInfo, contrai ->
parameters = AutopilotControlParameters()
parameters?.routeID = lineInfo.lineId.toInt()
parameters?.routeName = lineInfo.lineName
parameters?.startName = start.name
parameters?.endName = end.name
parameters?.startLatLon = AutoPilotLonLat(start.lat, start.lon)
parameters?.endLatLon = AutoPilotLonLat(end.lat, end.lon)
parameters?.vehicleType = 10
autopilotId = "${lineInfo.lineId}_${start.siteId}_${end.siteId}_${lineInfo.orderId}"
parameters?.orderId = autopilotId
parameters?.firstAutopilotFlag = isFirstStartAutopilot
if (parameters?.autoPilotLine == null) {
parameters?.autoPilotLine = AutoPilotLine(
lineInfo.lineId,
lineInfo.lineName,
contrai.csvFileUrl,
contrai.csvFileMd5,
contrai.txtFileUrl,
contrai.txtFileMd5,
contrai.contrailSaveTime,
"",
"",
"",
"",
"",
0L
)
}
val (wayLatLons, blackLatLons) = getWayBlackLatLons(contrai.passPoints, contrai.blackPoints)
parameters?.wayLatLons = wayLatLons
parameters?.blackLatLons = blackLatLons
}
if (parameters == null) {
ToastUtils.showShort("未设置起始或终点站点")
}
return parameters
}
fun getWayBlackLatLons(
passPoints: MutableList<BusStationBean>?,
blackPoints: MutableList<BusStationBean>?
): Pair<MutableList<AutoPilotLonLat>, MutableList<AutoPilotLonLat>> {
val wayLatLons = mutableListOf<AutoPilotLonLat>()
// 途经点
if (!passPoints.isNullOrEmpty()) {
for (mogoLatLng in passPoints) {
wayLatLons.add(
AutoPilotLonLat(
mogoLatLng.lat,
mogoLatLng.lon,
when (mogoLatLng.pointType) {
1 -> {//途径点
false
}
2 -> {//禁行点
false
}
3 -> {//站点
true
}
else -> {
false
}
}
)
)
}
}
val blackLatLons = mutableListOf<AutoPilotLonLat>()
// 黑名单点
if (!blackPoints.isNullOrEmpty()) {
for (mogoLatLng in blackPoints) {
blackLatLons.add(
AutoPilotLonLat(
mogoLatLng.lat,
mogoLatLng.lat,
when (mogoLatLng.pointType) {
1 -> {//途径点
false
}
2 -> {//禁行点
false
}
3 -> {//站点
true
}
else -> {
false
}
}
)
)
}
}
return Pair(wayLatLons,blackLatLons)
}
// 启动自动驾驶
fun startAutopilot() {
if(startStation ==null|| endStation ==null){
ToastUtils.showShort("未设置起始或终点站点")
return
}
startStation?.let {
if(!it.isLeaving){
ToastUtils.showShort("请滑动出发后再启动自驾")
return
}
}
/**
* 存在Session
*/
if (!OchAutoPilotManager.canStartAutopilotBySessionId()) {
return
}
OchAutopilotAnalytics.triggerClickStartAutopilotTime(System.currentTimeMillis())
//1、判断轨迹url是否可用
if(_contraiInfo ==null){
ToastUtils.showLong("无发布轨迹, 请发布后重试")
return
}else{
if (FunctionBuildConfig.isPassStartAutopilotCommand
&& TextUtils.isEmpty(_contraiInfo!!.csvFileUrl)
&& TextUtils.isEmpty(_contraiInfo!!.csvFileMd5)
) {
ToastUtils.showLong("无发布轨迹, 请发布后重试")
e(
TAG, "isPassStartAutopilotCommand = " +
FunctionBuildConfig.isPassStartAutopilotCommand
+ "busRoutesResult.csvFileUrl = " + _contraiInfo!!.csvFileUrl
)
return
}
}
//2、6个条件判断
if (!CallerAutoPilotControlManager.isCanStartAutopilot(true, 0)) {
return
}
// 3、距离轨迹15m计算
val resion = OchAutoPilotManager.canStartAutoPilotByDistance(_contraiInfo!!.lineId)
if (!StringUtils.isEmpty(resion)) {
ToastUtils.showShort(resion)
VoiceNotice.showNotice(resion)
return
}
//4、ssm 给出数据
if (!OchAutoPilotManager.canStartAutoPilotSSM()) {
triggerUnableStartAPReasonEvent()
return
}
triggerStartServiceEvent(false,0,"")
val parameters = initAutopilotControlParameters()
if (null == parameters) {
e(M_BUS + TAG, "行程日志-AutopilotControlParameters is empty.")
return
}
val sessionId = startAutoPilot(parameters)
OchAutopilotAnalytics.triggerUpdateStartAutoPilotSessionId(sessionId)
d(
M_BUS + TAG,
"行程日志-开启自动驾驶====" + GsonUtil.jsonFromObject(parameters)
+ " startLatLon=" + parameters.startName + "endLatLon=" + parameters.endName +
"isRestart = " + isFirstStartAutopilot
)
M_LISTENERS.forEach {
it.value.sendStartAutopilotSuccess()
}
}
private fun triggerUnableStartAPReasonEvent() {
getStationsWithLine { start, end, line ->
OchAutopilotAnalytics.triggerUnableStartAPReasonEvent(
start.name, end.name,line.lineId.toString() , "",
OCHAdasAbilityManager.getInstance().autopilotUnAbilityReason
)
}
}
fun triggerStartServiceEvent(send: Boolean,source:Int,type:String) {
getStationsWithLine { start, end, lineInfo ->
OchAutopilotAnalytics.triggerStartAutopilotEvent(
isFirstStartAutopilot,
send,
start.name,
end.name,
lineInfo.lineId.toInt(),
"",
System.currentTimeMillis(),
type,
source
)
}
}
@JvmStatic
fun invokeStartAutopilotTimeOut(){
M_LISTENERS.forEach {
it.value.startAutopilotTimeOut()
}
}
@JvmStatic
fun invokeStartAutopilotFailure(startFailedCode: String, startFailedMessage: String) {
OchAutopilotAnalytics.triggerStartAutopilotFailureEventByAdas(startFailedCode,startFailedMessage,System.currentTimeMillis())
M_LISTENERS.forEach {
it.value.startAutopilotFailure(startFailedCode,startFailedMessage)
}
}
fun invokeStartAutopilotAckSuccess(receivedAck: ReceivedAck) {
M_LISTENERS.forEach {
it.value.sendStartAutopilotSuccessAck()
}
}
}

View File

@@ -0,0 +1,47 @@
package com.mogo.och.bridge.autopilot.location
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationWGS84Listener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationWGS84ListenerManager
object OchLocationManager {
@JvmStatic
fun getGCJ02Location(): MogoLocation {
return CallerChassisLocationGCJ02ListenerManager.getChassisLocationGCJ02()
}
@JvmStatic
fun getWgs02Location():MogoLocation {
return CallerChassisLocationWGS84ListenerManager.getChassisLocationWGS84()
}
@JvmStatic
fun addGCJ02Listener(tag:String,callBackHz:Int,listener: IMoGoChassisLocationGCJ02Listener){
CallerChassisLocationGCJ02ListenerManager.addListener(tag, callBackHz, listener)
}
@JvmStatic
fun removeGCJ02Listener(tag:String){
CallerChassisLocationGCJ02ListenerManager.removeListener(tag)
}
@JvmStatic
fun removeGCJ02Listener(listener: IMoGoChassisLocationGCJ02Listener){
CallerChassisLocationGCJ02ListenerManager.removeListener(listener)
}
@JvmStatic
fun addWgs02Listener(tag:String,callBackHz:Int,listener: IMoGoChassisLocationWGS84Listener){
CallerChassisLocationWGS84ListenerManager.addListener(tag, callBackHz, listener)
}
@JvmStatic
fun removeWgs02Listener(tag:String){
CallerChassisLocationWGS84ListenerManager.removeListener(tag)
}
@JvmStatic
fun removeWgs02Listener(listener: IMoGoChassisLocationWGS84Listener){
CallerChassisLocationWGS84ListenerManager.removeListener(listener)
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.och.bridge.autopilot.trajectory
import com.mogo.eagle.core.data.map.MogoLocation
interface ITrajectoryListListener{
/**
* @param trajectoryList gcj 坐标轨迹集合
*/
fun trajectoryCallback(trajectoryList: MutableList<MogoLocation>){}
/**
* @param maxDistanceAllPoint 轨迹全长
*/
fun trajectoryDistanceCallback(maxDistanceAllPoint: Double){}
// 开始下载轨迹
fun onDownLoadStart(lineId: Long){}
// 下载轨迹成功
fun onDownLoadSuccess(lineId: Long){}
// 下载轨迹是吧
fun onDownLoadFail(lineId: Long){}
// 下载轨迹超时
fun onDownLoadTimeout(lineId: Long){}
// 加载轨迹失败
fun onLoadFail(lineId: Long){}
// 轨迹不存在
fun onTrajectoryNotExist(lineId: Long){}
// 底盘已就行
fun onDownLoadReady(lineId: Long) {}
// 当前位置距离轨迹大于15m
fun onDistanceWithTrajectory(lineId: Long) {}
}

View File

@@ -0,0 +1,67 @@
package com.mogo.och.bridge.autopilot.trajectory
import com.elegant.network.utils.GsonUtil
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_OCHCOMMON
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.IOException
object TrajectoryCache {
private const val TAG = "TrajectoryCache"
private val dir = "${AbsMogoApplication.getApp().cacheDir}/trajectory/"
fun writeLastTrajectory2jsonFile(latLngModels: MutableList<MogoLocation>,name:String) {
val jsonFromObject = GsonUtil.jsonFromObject(latLngModels)
try {
val path = File(dir)
if(!path.exists()){
path.mkdir()
}else {
path.deleteRecursively()
CallerLogger.d(M_OCHCOMMON + TAG,"删除缓存:${name}")
path.mkdir()
}
val writer = FileWriter("${path.path}/${name}")
writer.write(jsonFromObject)
writer.close()
CallerLogger.d(M_OCHCOMMON + TAG,"写入缓存:${name}")
} catch (e: IOException) {
e.printStackTrace()
}
}
fun deleteCatcheFile(){
try {
val path = File(dir)
if(path.exists()){
path.deleteRecursively()
CallerLogger.d(M_OCHCOMMON + TAG,"删除缓存")
}
} catch (e: IOException) {
e.printStackTrace()
}
}
fun redCatche(name: String): MutableList<MogoLocation>? {
try {
val path = File(dir)
if(!path.exists()){
return null
}
val fileReader = FileReader("${path.path}/${name}")
val readText = fileReader.readText()
fileReader.close()
return GsonUtil.arrayFromJson(readText, MogoLocation::class.java)
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
}

View File

@@ -0,0 +1,176 @@
package com.mogo.och.bridge.autopilot.trajectory
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningRottingListener
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.CoordinateUtils
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager.writeChainLog
import com.mogo.och.common.module.utils.CallerBase
import com.mogo.och.bridge.utils.CoordinateCalculateRouteUtil
import com.zhjt.mogo.adas.data.bean.MogoReport
import mogo.telematics.pad.MessagePad
object TrajectoryManager : CallerBase<ITrajectoryListListener>(),IMoGoPlanningRottingListener {
const val TAG = "TrajectoryManager"
private val downLoadSuccessLineIds = mutableListOf<Long>()
/**
* 所有轨迹点
*/
@Volatile
var mRoutePoints = mutableListOf<MogoLocation>()
/**
* 0-1 1-2 2-3 各个轨迹点的距离
*/
@Volatile
private var mRoutePointsDistance: MutableList<Float>? = ArrayList()
/**
* 所有轨迹点距离的和
*/
@Volatile
private var maxDistanceAllPoint: Double = 0.0
init {
CallerPlanningRottingListenerManager.addListener(TAG, this)
}
private var lineId:Long?=0
override fun onAutopilotRotting(globalPathResp: MessagePad.GlobalPathResp?) {
CallerLogger.d(SceneConstant.M_OCHCOMMON + TAG, "onAutopilotRotting: 收到轨迹")
globalPathResp?.wayPointsList?.let {
if (it.size > 0) {
CallerLogger.d(
SceneConstant.M_OCHCOMMON + TAG,
"收到轨迹:轨迹个数${it.size}第一个点${it[0]}最后一个点:${it.last()} 轨迹id:${globalPathResp.lineId}"
)
@Suppress("SENSELESS_COMPARISON")
if (globalPathResp.lineId != null) {// 适配低版本不传递lineId
if (globalPathResp.lineId == lineId) {
CallerLogger.d(SceneConstant.M_OCHCOMMON + TAG, "重复轨迹")
return
}
lineId = globalPathResp.lineId
}
updateRoutePoints(it)
}
}
}
private fun updateRoutePoints(routePoints: List<MessagePad.Location>?) {
mRoutePointsDistance = null
val latLngModels = CoordinateCalculateRouteUtil
.coordinateConverterWgsToGcjLocations(AbsMogoApplication.getApp(), routePoints!!)
mRoutePoints = latLngModels
mRoutePointsDistance = ArrayList()
maxDistanceAllPoint = 0.0
mRoutePoints.forEachIndexed { index, current ->
if (mRoutePoints.last() != current) {
val next = mRoutePoints[index + 1]
val distanceItem = CoordinateUtils.calculateLineDistance(
current.longitude,
current.latitude,
next.longitude,
next.latitude
)
mRoutePointsDistance?.add(distanceItem)
maxDistanceAllPoint += distanceItem
}
}
M_LISTENERS.forEach {
it.value.trajectoryCallback(mRoutePoints)
}
M_LISTENERS.forEach {
it.value.trajectoryDistanceCallback(maxDistanceAllPoint)
}
}
private fun addDownLoadSuccessLine(lineId:Long){
if(lineId>0){
OchChainLogManager.writeChainLog("轨迹监控", " 轨迹下载成功${lineId}", true, OchChainLogManager.EVENT_KEY_INFE_WITH_TRAJECTORY)
downLoadSuccessLineIds.add(lineId)
}
}
private fun removeDownLoadSuccessLine(lineId:Long){
if(lineId>0&& downLoadSuccessLineIds.contains(lineId)){
downLoadSuccessLineIds.remove(lineId)
}
}
fun carDownLoadTrajectoryLog(code: String?, lineId: Long){
when (code) {
MogoReport.Code.Info.ISYS.INIT_TRAJECTORY_START -> {
writeTrajectoryLog("轨迹开始下载")
M_LISTENERS.forEach {
it.value.onDownLoadStart(lineId)
}
}
MogoReport.Code.Info.ISYS.INIT_TRAJECTORY_SUCCESS -> {
writeTrajectoryLog("轨迹下载成功")
addDownLoadSuccessLine(lineId)
M_LISTENERS.forEach {
it.value.onDownLoadSuccess(lineId)
}
}
MogoReport.Code.Info.ISYS.INIT_TRAJECTORY_FAILURE -> {
writeTrajectoryLog("轨迹下载失败,本地无对应轨迹")
// 更新轨迹没有更新lineId 第二次下载失败 删除App成功缓存(概率很低)
removeDownLoadSuccessLine(lineId)
M_LISTENERS.forEach {
it.value.onDownLoadFail(lineId)
}
}
MogoReport.Code.Info.ISYS.INIT_TRAJECTORY_WARNING -> {
writeTrajectoryLog("轨迹下载失败,本地有对应轨迹,认为成功")
M_LISTENERS.forEach {
it.value.onDownLoadSuccess(lineId)
}
}
MogoReport.Code.Info.ISYS.INIT_TRAJECTORY_TIMEOUT -> {
writeTrajectoryLog("轨迹下载超时")
M_LISTENERS.forEach {
it.value.onDownLoadTimeout(lineId)
}
}
MogoReport.Code.Info.ISSM.FUNC_AUTO_PILOT_READY -> {
writeTrajectoryLog("再次发起下载、 ssm ready再次发起下载")
M_LISTENERS.forEach {
it.value.onDownLoadReady(lineId)
}
}
MogoReport.Code.Error.EMAP.TRA_LOAD_FAILED -> {
writeTrajectoryLog("onAutopilotGuardian() 加载轨迹文件失败")
M_LISTENERS.forEach {
it.value.onLoadFail(lineId)
}
}
MogoReport.Code.Error.EMAP.TRA_NOT_EXIST -> {
writeTrajectoryLog("onAutopilotGuardian() 无法找到轨迹文件")
M_LISTENERS.forEach {
it.value.onTrajectoryNotExist(lineId)
}
}
MogoReport.Code.Error.EMAP.ATTITUDE_INIT_FAILED->{
writeTrajectoryLog("onAutopilotGuardian() 当前位置距离轨迹距离大于15m")
M_LISTENERS.forEach {
it.value.onDistanceWithTrajectory(lineId)
}
}
else -> {}
}
}
private fun writeTrajectoryLog(message:String){
writeChainLog("轨迹监控", message, true, OchChainLogManager.EVENT_KEY_INFE_WITH_TRAJECTORY)
}
}

View File

@@ -0,0 +1,45 @@
package com.mogo.och.bridge.device
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.och.bridge.autopilot.location.OchLocationManager
import com.mogo.och.common.module.utils.RxUtils
import com.mogo.och.common.module.wigets.toast.ToastCharterUtils
import io.reactivex.disposables.Disposable
object LightAirconditionDoorManager {
private const val TAG = "LightAirconditionDoorManager"
private var dooorSubscribe: Disposable? = null
fun go2OpenDoor(go2Open:Boolean){
val canOpenOrCloseDoor = canOpenOrCloseDoor()
if(!canOpenOrCloseDoor.isNullOrBlank()){
ToastCharterUtils.showToastLong(canOpenOrCloseDoor)
return
}
RxUtils.disposeSubscribe(dooorSubscribe)
dooorSubscribe = RxUtils.createSubscribe(1000) {
CallerAutoPilotControlManager.sendRoboBusJinlvM1FrontDoorCmd(0)
}
if(go2Open) {
if (LightAirconditionDoorStatusManager.doorStatus.isOpen) {
return
}
CallerAutoPilotControlManager.sendRoboBusJinlvM1FrontDoorCmd(1)
}else{
if (!LightAirconditionDoorStatusManager.doorStatus.isOpen) {
return
}
CallerAutoPilotControlManager.sendRoboBusJinlvM1FrontDoorCmd(2)
}
}
private fun canOpenOrCloseDoor(): String? {
val location = OchLocationManager.getGCJ02Location()
return if(location.gnssSpeed<0.3){
null
}else{
"车辆行驶中不可以开关门哦~"
}
}
}

View File

@@ -0,0 +1,128 @@
package com.mogo.och.bridge.device
import chassis.VehicleStateOuterClass
import com.mogo.eagle.core.function.api.autopilot.IMoGoRoboBusJinlvM1StatesListener
import com.mogo.eagle.core.function.call.autopilot.CallerRoboBusJinlvM1StatesListenerManager
import com.mogo.och.bridge.device.callback.LightAirconditionDoorCallback
import com.mogo.och.bridge.device.data.AirconditionStatus
import com.mogo.och.bridge.device.data.DoorStatus
import com.mogo.och.bridge.device.data.HeaterStatue
import com.mogo.och.bridge.device.data.LightStatus
import java.util.concurrent.ConcurrentHashMap
object LightAirconditionDoorStatusManager : IMoGoRoboBusJinlvM1StatesListener {
private const val TAG = "OCHM1LightAirconditionDoorStatusManager"
init {
CallerRoboBusJinlvM1StatesListenerManager.addListener(TAG, this)
CallerRoboBusJinlvM1StatesListenerManager.setListenerHz(TAG,5)
}
val M_LISTENERS: ConcurrentHashMap<String, LightAirconditionDoorCallback> =
ConcurrentHashMap()
val airconditionStatus = AirconditionStatus(false, 0, 26, 2)
val heaterStatue = HeaterStatue(false, 2)
val doorStatus = DoorStatus(false)
val lightStatus = LightStatus(false, false, false)
fun addListener(
tag: String,
listener: LightAirconditionDoorCallback
) {
if (M_LISTENERS.containsKey(tag)) {
return
}
M_LISTENERS[tag] = listener
listener.onLightTop1Callback(lightStatus,true)
listener.onLightTop2Callback(lightStatus,true)
listener.onLightAtmosphereCallback(lightStatus,true)
listener.onAirconditionStatusCallback(heaterStatue.isOpen, airconditionStatus,true)
listener.onHeaterStatusCallback(airconditionStatus.isOpen, heaterStatue,true)
}
fun removeListener(tag: String) {
if (!M_LISTENERS.containsKey(tag)) {
return
}
M_LISTENERS.remove(tag)
}
/**
* 删除监听
* @param listener 要删除的监听对象
*/
fun removeListener(listener: LightAirconditionDoorCallback) {
if (!M_LISTENERS.containsValue(listener)) {
return
}
M_LISTENERS.forEach {
if (it.value == listener) {
M_LISTENERS.remove(it.key)
}
}
}
override fun onRoboBusJinlvM1States(states: VehicleStateOuterClass.RoboBusJinlvM1State) {
val airConditionerState = states.airConditionerState
val heaterState = states.heaterState
if (airConditionerState.isOn != airconditionStatus.isOpen ||
airConditionerState.mode != airconditionStatus.pattert ||
airConditionerState.temperature != airconditionStatus.temperature ||
airConditionerState.windSpeed != airconditionStatus.windSpeed
) {
airconditionStatus.isOpen = airConditionerState.isOn
airconditionStatus.pattert = airConditionerState.mode
airconditionStatus.temperature = airConditionerState.temperature
airconditionStatus.windSpeed = airConditionerState.windSpeed
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onAirconditionStatusCallback(heaterState.isOn, airconditionStatus,false)
}
}
if (heaterState.isOn != heaterStatue.isOpen ||
heaterState.windSpeed != heaterStatue.windSpeed
) {
heaterStatue.isOpen = heaterState.isOn
heaterStatue.windSpeed = heaterState.windSpeed
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onHeaterStatusCallback(airConditionerState.isOn, heaterStatue,false)
}
}
if (states.frontDoorState.isOn != doorStatus.isOpen) {
doorStatus.isOpen = states.frontDoorState.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onDoorStatusCallback(doorStatus.isOpen,false)
}
}
if(states.mainLamp1State.isOn != lightStatus.isOpenLight1){
lightStatus.isOpenLight1 = states.mainLamp1State.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onLightTop1Callback(lightStatus,false)
}
}
if(states.mainLamp2State.isOn != lightStatus.isOpenLight2){
lightStatus.isOpenLight2 = states.mainLamp2State.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onLightTop2Callback(lightStatus,false)
}
}
if(states.smallLampState.isOn != lightStatus.isOpenatmosphere){
lightStatus.isOpenatmosphere = states.smallLampState.isOn
M_LISTENERS.forEach {
val tag = it.key
val listener = it.value
listener.onLightAtmosphereCallback(lightStatus,false)
}
}
}
}

View File

@@ -0,0 +1,134 @@
package com.mogo.och.bridge.device
import chassis.Chassis
import chassis.VehicleStateOuterClass
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisDoorStateListener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisDoorStateListenerManager
import com.mogo.eagle.core.function.call.base.CallerBase
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.och.bridge.device.callback.DoorStateCallback
import com.mogo.och.bridge.device.data.DoorPosition
import com.mogo.och.bridge.device.data.DoorState
object TaxiDoorStateManager : IMoGoChassisDoorStateListener,
CallerBase<DoorStateCallback>() {
private val TAG = TaxiDoorStateManager::class.java.simpleName
@Volatile
private var haveOpenDoor: Boolean? = null
init {
CallerChassisDoorStateListenerManager.addListener(TAG, this)
}
override fun doSomeAfterAddListener(tag: String, listener: DoorStateCallback) {
val doorList = CallerChassisDoorStateListenerManager.getDoorList()
if(doorList is MutableList<VehicleStateOuterClass.DoorStateV2>){
doSomeAfterAddListenerInner(doorList)
doorList.forEach {
onAutopilotSingleDoorState(it.number,it.status)
}
}else{
doSomeAfterAddListenerInner(mutableListOf())
}
}
private fun doSomeAfterAddListenerInner(doorList: MutableList<VehicleStateOuterClass.DoorStateV2>){
var have = false
doorList.forEach {
if (it.status == 1) {//0关闭 1开着 2未知
have = true
}
}
haveOpenDoor = have
invokeOpenState(have)
}
/**
* 主要用来判断是否有门开着
*/
override fun onAutopilotDoorState(doorList: MutableList<VehicleStateOuterClass.DoorStateV2>) {
var have = false
doorList.forEach {
if (it.status == 1) {//0关闭 1开着 2未知
have = true
}
}
if (have != haveOpenDoor) {
haveOpenDoor = have
invokeOpenState(have)
}
}
/**
* 判断单个车门是否开着
*/
override fun onAutopilotSingleDoorState(num: Chassis.DoorNumber, status: Int) {
CallerLogger.d(SceneConstant.M_TAXI_P + TAG, "门太变化:${num}--${status}")
when (status) {
0 -> { exchangeEnum(num, DoorState.CLOSE)
}
1 -> {
exchangeEnum(num, DoorState.OPEN)
}
2 -> {
exchangeEnum(num, DoorState.UNKNOWN)
}
else -> {}
}
}
private fun exchangeEnum(num: Chassis.DoorNumber, state: DoorState) {
when (num) {
Chassis.DoorNumber.FRONT_LEFT -> {
invokeSingleDoorOpenState(DoorPosition.FRONT_LEFT, state)
}
Chassis.DoorNumber.FRONT_RIGHT -> {
invokeSingleDoorOpenState(DoorPosition.FRONT_RIGHT, state)
}
Chassis.DoorNumber.REAR_LEFT -> {
invokeSingleDoorOpenState(DoorPosition.REAR_LEFT, state)
}
Chassis.DoorNumber.REAR_RIGHT -> {
invokeSingleDoorOpenState(DoorPosition.REAR_RIGHT, state)
}
Chassis.DoorNumber.MIDDLE -> {
invokeSingleDoorOpenState(DoorPosition.MIDDLE, state)
}
else -> {}
}
}
/**
* @param have true 有车门开着
* false 没有车门开着(可能开着可能未知)
*/
@Synchronized
private fun invokeOpenState(have: Boolean) {
M_LISTENERS.forEach {
val listener = it.value
listener.hasOpenDoor(have)
}
}
/**
* @param doorPosition 车门位置
* @param doorState 车门状态
*/
@Synchronized
private fun invokeSingleDoorOpenState(doorPosition: DoorPosition, doorState: DoorState) {
M_LISTENERS.forEach {
val listener = it.value
listener.doorStateChangeCallback(doorPosition,doorState)
}
}
}

View File

@@ -0,0 +1,19 @@
package com.mogo.och.bridge.device.callback
import com.mogo.och.bridge.device.data.DoorPosition
import com.mogo.och.bridge.device.data.DoorState
interface DoorStateCallback {
/**
* @param have true 有车门开着
* false 没有车门开着(可能开着可能未知)
*/
fun hasOpenDoor(have:Boolean){}
/**
* @param position 车门位置
* @param state 当前车门状态
*/
fun doorStateChangeCallback(position: DoorPosition, state: DoorState){}
}

View File

@@ -0,0 +1,26 @@
package com.mogo.och.bridge.device.callback
import com.mogo.och.bridge.device.data.AirconditionStatus
import com.mogo.och.bridge.device.data.HeaterStatue
import com.mogo.och.bridge.device.data.LightStatus
interface LightAirconditionDoorCallback {
fun onAirconditionStatusCallback(heaterIsOpen: Boolean, airconditionStatus: AirconditionStatus,
isFirst: Boolean) {
}
fun onHeaterStatusCallback(airconditionIsOpen: Boolean, heaterStatue: HeaterStatue,
isFirst: Boolean) {
}
fun onDoorStatusCallback(isOpen: Boolean, isFirst: Boolean) {}
fun onLightTop1Callback(lightStatus: LightStatus, isFirst: Boolean) {
}
fun onLightTop2Callback(lightStatus: LightStatus, isFirst: Boolean) {}
fun onLightAtmosphereCallback(lightStatus: LightStatus, isFirst: Boolean) {}
}

View File

@@ -0,0 +1,43 @@
package com.mogo.och.bridge.device.checkvin
import android.content.Context
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.och.bridge.R
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import kotlinx.android.synthetic.main.common_checkvin_view.actv_see
class CheckVinErrorDialog(context: Context) : BaseFloatDialog(context), LifecycleObserver {
companion object{
const val EVENT_KEY_INFO_CHECK_VIN = "analytics_event_och_check_vin"
}
init {
setContentView(R.layout.common_checkvin_view)
setCanceledOnTouchOutside(true)
actv_see.onClick {
OchChainLogManager.writeChainLog("vinCheck","用户点击了 ”我知道了“",true,
EVENT_KEY_INFO_CHECK_VIN
)
dismiss()
}
}
fun showDialog() {
if (isShowing) {
return
}
show()
}
fun hideDialog() {
if (isShowing) {
dismiss()
}
}
}

View File

@@ -0,0 +1,74 @@
package com.mogo.och.bridge.device.checkvin
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotCarConfigListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotCarConfigListenerManager
import com.mogo.eagle.core.utilcode.util.ActivityUtils
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.common.module.biz.login.LoginStatusManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.manager.loop.BizLoopManager
import com.mogo.och.common.module.manager.loop.LoopInfo
import io.reactivex.schedulers.Schedulers
import mogo.telematics.pad.MessagePad
object CheckVinManager : IMoGoAutopilotCarConfigListener {
private val TAG = CheckVinManager::class.java.simpleName
init {
BizLoopManager.setLoopFunction(TAG, LoopInfo(60*5, CheckVinManager::checkVin,scheduler = Schedulers.io()))
CallerAutopilotCarConfigListenerManager.addListener(TAG,this)
}
private var checkVinErrorDialog: CheckVinErrorDialog?=null
override fun onAutopilotCarConfig(carConfigResp: MessagePad.CarConfigResp) {
checkVin()
}
fun getVin(): String {
var delineVin = CallerAutoPilotControlManager.getVIN()
if(delineVin.isEmpty()){
delineVin = LoginStatusManager.getLoginInfo()?.vin?:""
}
return delineVin
}
private fun checkVin() {
LoginStatusManager.getLoginInfo()?.let {loginInfo ->
val serverVin = loginInfo.vin
val delineVin = CallerAutoPilotControlManager.getVIN()
OchChainLogManager.writeChainLog(
"checkVin",
"5分钟检测VIN服务器Vin:${serverVin}-----底盘Vin:${delineVin}", true,
CheckVinErrorDialog.EVENT_KEY_INFO_CHECK_VIN
)
if(serverVin.isNullOrBlank()){
return
}
if(delineVin.isNullOrBlank()){
return
}
if(serverVin!=delineVin){
val topActivity = ActivityUtils.getTopActivity()
topActivity?.let {
UiThreadHandler.post({
if(checkVinErrorDialog !=null&& checkVinErrorDialog!!.isShowing){
return@post
}
checkVinErrorDialog = CheckVinErrorDialog(it)
checkVinErrorDialog?.showDialog()
},UiThreadHandler.MODE.QUEUE)
}
}
}
}
fun load() {
OchChainLogManager.writeChainLogInit("初始化信息","初始checkVin 轮训")
}
}

View File

@@ -0,0 +1,8 @@
package com.mogo.och.bridge.device.data
data class AirconditionStatus(
var isOpen: Boolean,
var pattert: Int,
var temperature: Int,
var windSpeed: Int
)

View File

@@ -0,0 +1,12 @@
package com.mogo.och.bridge.device.data
data class DoorStatus(var isOpen: Boolean)
enum class DoorPosition {
FRONT_LEFT, FRONT_RIGHT, REAR_LEFT, REAR_RIGHT, MIDDLE
}
enum class DoorState {
OPEN,CLOSE,UNKNOWN
}

View File

@@ -0,0 +1,3 @@
package com.mogo.och.bridge.device.data
data class HeaterStatue(var isOpen: Boolean, var windSpeed: Int)

View File

@@ -0,0 +1,7 @@
package com.mogo.och.bridge.device.data
data class LightStatus(
var isOpenLight1: Boolean,
var isOpenLight2: Boolean,
var isOpenatmosphere: Boolean
)

View File

@@ -0,0 +1,54 @@
package com.mogo.och.bridge.distance
data class DistanceDegree(var distance: Float, var degree: Double?, var isNext: Boolean?) :
Comparable<DistanceDegree> {
override fun compareTo(other: DistanceDegree): Int {
// 对比距离
if (distance == other.distance) {
return 0;
} else if (distance < other.distance) {
return -1;
}
return 1;
}
var next: DistanceDegree? = null
fun recycle() {
synchronized(sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool
sPool = this
sPoolSize++
}
//Logger.d("DistanceDegree","缓存对象个数${sPoolSize}个")
}
}
companion object {
var sPoolSync = Any()
private var sPool: DistanceDegree? = null
private var sPoolSize = 0
private var MAX_POOL_SIZE = 20
fun obtain(distance: Float, degree: Double?, isNext: Boolean?): DistanceDegree {
synchronized(sPoolSync) {
if (sPool != null) {
val m: DistanceDegree = sPool!!
sPool = m.next
m.next = null
m.distance = distance
m.degree = degree
m.isNext = isNext
sPoolSize--
//Logger.d("DistanceDegree","取出一个对象个数${sPoolSize}个")
return m
}
//Logger.d("DistanceDegree","创建一个对象 ${sPoolSize}个")
return DistanceDegree(distance, degree, isNext)
}
}
}
}

View File

@@ -0,0 +1,43 @@
package com.mogo.och.bridge.distance
import com.mogo.eagle.core.data.map.MogoLocation
interface IDistanceListener {
/**
* @param distance 距离终点坐标的距离(终点坐标设置错误可能为负值)
*/
fun distanceCallback(distance: Float){}
/**
* 两个站点之间的距离
*/
fun stationDistanceCallback(stationDistance:Float){}
}
interface ITrajectoryListener{
/**
* @param routeArrivied 已经走过的坐标
* @param routeArriving 没有走过的坐标
* @param location 车的坐标
* @return
*/
fun trajectoryCallback(
routeArrivied: MutableList<MogoLocation>,
routeArriving: MutableList<MogoLocation>,
location: MogoLocation
)
}
interface ITrajectoryWithStationListener{
/**
* @param routeArrivied 已经走过的坐标 第一个是开始站点坐标
* @param routeArriving 没有走过的坐标 最后一个是结束站点坐标
* @param location 车的坐标
* @return
*/
fun trajectoryWithStationCallback(
routeArrivied: MutableList<MogoLocation>,
routeArriving: MutableList<MogoLocation>,
location: MogoLocation
)
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.bridge.distance
import com.mogo.eagle.core.data.map.MogoLocation
data class StationAndIndex(
var stationPoint: MogoLocation?,// 站点坐标
var index: Int?,// 坐标对应轨迹中最近的点
var distance: Float?,//轨迹中最近的点
var isNext:Boolean?,// 最近的点在轨迹中是在站点的下一个还是上一个
var lineId:Long?,// 站点所属轨迹 todo 未来轨迹线路id和站点线路id 分别存储
)

View File

@@ -0,0 +1,752 @@
package com.mogo.och.bridge.distance
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningRottingListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.e
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.Companion.M_OCHCOMMON
import com.mogo.eagle.core.utilcode.util.CoordinateUtils
import com.mogo.eagle.core.utilcode.util.LocationUtils
import com.mogo.och.common.module.constant.OchCommonConst
import com.mogo.och.bridge.autopilot.autopilot.OchAutopilotAnalytics
import com.mogo.och.bridge.autopilot.location.OchLocationManager
import com.mogo.och.bridge.autopilot.trajectory.TrajectoryCache
import com.mogo.och.common.module.manager.loop.BizLoopManager
import com.mogo.och.common.module.manager.loop.LoopInfo
import com.mogo.och.bridge.utils.CoordinateCalculateRouteUtil
import io.reactivex.schedulers.Schedulers
import mogo.telematics.pad.MessagePad
import java.util.concurrent.ConcurrentHashMap
/**
* 计算当前位置距离站点距离和走过的和未走过的轨迹点
*/
object TrajectoryAndDistanceManager : IMoGoPlanningRottingListener {
private val distanceListeners: ConcurrentHashMap<String, IDistanceListener> =
ConcurrentHashMap()
private val trajectoryListeners: ConcurrentHashMap<String, ITrajectoryListener> =
ConcurrentHashMap()
private val trajectoryWithStationListeners: ConcurrentHashMap<String, ITrajectoryWithStationListener> =
ConcurrentHashMap()
private const val TAG = "DistanceManager"
private const val DISTANCE = "BusPassengerModelDistance"
const val errorTypeNoneLineId = "起始站点值异常,请重新选择此任务执行并上报问题"
fun addDistanceListener(tag: String, listener: IDistanceListener) {
if (distanceListeners.containsKey(tag)) {
return
}
distanceListeners[tag] = listener
}
fun addTrajectoryListener(tag: String, listener: ITrajectoryListener) {
if (trajectoryListeners.containsKey(tag)) {
return
}
trajectoryListeners[tag] = listener
}
@Suppress("unused")
fun addTrajectoryWithStationListener(tag: String, listener: ITrajectoryWithStationListener) {
if (trajectoryWithStationListeners.containsKey(tag)) {
return
}
trajectoryWithStationListeners[tag] = listener
}
/**
* 删除监听
* @param tag 标记,用来注销监听使用
*/
fun removeListener(tag: String) {
distanceListeners.remove(tag)
trajectoryListeners.remove(tag)
trajectoryWithStationListeners.remove(tag)
}
/**
* 所有轨迹点
*/
@Volatile
private var mRoutePoints: MutableList<MogoLocation>? = ArrayList()
/**
* 0-1 1-2 2-3 各个轨迹点的距离
*/
@Volatile
private var mRoutePointsDistance: MutableList<Float>? = ArrayList()
/**
* 所有轨迹点距离的和
*/
@Volatile
private var maxDistanceAllPoint: Double = 0.0
/**
* 线路Id
*/
@Volatile
private var lineId: Long? = null
/**
* 结束站点
*/
@Volatile
private var endStationInfo: StationAndIndex = StationAndIndex(null, null, null, null, null)
/**
* 开始站点
*/
@Volatile
private var startStationInfo: StationAndIndex = StationAndIndex(null, null, null, null, null)
/**
* startStationInfo endStationInfo 站点之间的距离
*/
private val stationDistance: ConcurrentHashMap<String, Float> = ConcurrentHashMap()
/**
* 上一次(上一个tick)计算最近点的缓存
*/
private var preCarLocationIndexInTrajectory = 0
init {
CallerPlanningRottingListenerManager.addListener(TAG, this)
}
override fun onAutopilotRotting(globalPathResp: MessagePad.GlobalPathResp?) {
d(M_OCHCOMMON + TAG, "onAutopilotRotting: 收到轨迹")
globalPathResp?.wayPointsList?.let {
if (it.size > 0) {
d(
M_OCHCOMMON + TAG,
"收到轨迹:轨迹个数${it.size}第一个点${it[0]}最后一个点:${it.last()} 轨迹id:${globalPathResp.lineId}"
)
@Suppress("SENSELESS_COMPARISON")
if (globalPathResp.lineId != null) {// 适配低版本不传递lineId
if (globalPathResp.lineId == lineId && !mRoutePoints.isNullOrEmpty()) {
d(M_OCHCOMMON + TAG, "重复轨迹")
startCalculateDistanceLoop()
return
}else{
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
}
this.lineId = globalPathResp.lineId
}
endCalculateDistanceLoop()
updateRoutePoints(it)
preCarLocationIndexInTrajectory = 0
startCalculateDistanceLoop()
}
}
}
private fun updateRoutePoints(routePoints: List<MessagePad.Location>?) {
mRoutePoints = null
mRoutePointsDistance = null
val latLngModels = CoordinateCalculateRouteUtil
.coordinateConverterWgsToGcjLocations(AbsMogoApplication.getApp(), routePoints!!)
mRoutePoints = latLngModels
TrajectoryCache.writeLastTrajectory2jsonFile(latLngModels, lineId.toString())
mRoutePointsDistance = ArrayList()
maxDistanceAllPoint = 0.0
mRoutePoints?.forEachIndexed { index, current ->
if (mRoutePoints!!.last() != current) {
val next = mRoutePoints!![index + 1]
val distanceItem = CoordinateUtils.calculateLineDistance(
current.longitude,
current.latitude,
next.longitude,
next.latitude
)
mRoutePointsDistance?.add(distanceItem)
maxDistanceAllPoint += distanceItem
}
}
}
/**
* 设置或清理站点坐标
*/
fun setStationPoint(
startStationInfo: MogoLocation?,
endStationInfo: MogoLocation?,
lineId: Long?
) {
d(
M_OCHCOMMON + TAG,
"线路id:${lineId}设置站点:开始站点${startStationInfo}、结束站点:${endStationInfo}"
)
if (startStationInfo == null || endStationInfo == null || lineId == -1L) {
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.endStationInfo.lineId = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
this.startStationInfo.lineId = null
preCarLocationIndexInTrajectory = 0
endCalculateDistanceLoop()
mRoutePoints = null
mRoutePointsDistance = null
TrajectoryCache.deleteCatcheFile()
this.endStationInfo.stationPoint = null
this.startStationInfo.stationPoint = null
this.lineId = null
stationDistance.clear()
} else {
if (isSameStation(this.startStationInfo.stationPoint, startStationInfo) &&
isSameStation(this.endStationInfo.stationPoint, endStationInfo)
) {
if (this.lineId != lineId) {
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.endStationInfo.lineId = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
this.startStationInfo.lineId = null
mRoutePoints = null
mRoutePointsDistance = null
TrajectoryCache.deleteCatcheFile()
stationDistance.clear()
}
} else {
this.endStationInfo.index = null
this.endStationInfo.distance = null
this.endStationInfo.isNext = null
this.endStationInfo.lineId = null
this.startStationInfo.index = null
this.startStationInfo.distance = null
this.startStationInfo.isNext = null
this.startStationInfo.lineId = null
if (this.lineId == 0L || this.lineId == null) {
// 兼容老MAP 不返回轨迹id lineID
} else {
if (this.lineId != lineId) {// bus 切换站点不会切线路
mRoutePoints = null
mRoutePointsDistance = null
TrajectoryCache.deleteCatcheFile()
}
}
stationDistance.clear()
}
this.endStationInfo.stationPoint = endStationInfo
this.startStationInfo.stationPoint = startStationInfo
this.lineId = lineId
startCalculateDistanceLoop()
}
}
private fun isSameStation(stationOne: MogoLocation?, stationTwo: MogoLocation?): Boolean {
if (stationOne == null || stationTwo == null) {
return false
}
if (stationOne.longitude == stationTwo.longitude && stationOne.latitude == stationTwo.latitude) {
return true
}
return false
}
/**
* 根据两点距离和轨迹距离来计算真实距离
*/
private fun calculateDistance() {
//mLocation gcj坐标
OchLocationManager.getGCJ02Location().let {
if (mRoutePoints.isNullOrEmpty() || endStationInfo.stationPoint == null) {
d(M_OCHCOMMON + TAG, "没有轨迹或站点坐标停止计算")
//结束距离计算
endCalculateDistanceLoop()
return
}
if (it.latitude == 0.0 && it.longitude == 0.0) {
return
}
calculateRouteSumLength(it)
}
}
/**
* 暂停路距计算
*/
fun suspendCalculate() {
endCalculateDistanceLoop()
}
@Suppress("unused")
fun reStartCalculate() {
startCalculateDistanceLoop()
}
/**
* 启动路距计算
*/
private fun startCalculateDistanceLoop() {
BizLoopManager.setLoopFunction(
DISTANCE,
LoopInfo(
1,
::calculateDistance,
scheduler = Schedulers.computation()
)
)
d(M_OCHCOMMON + TAG, "开始路距计算")
}
/**
* 结束启动路距计算
*/
private fun endCalculateDistanceLoop() {
BizLoopManager.removeLoopFunction(DISTANCE)
d(M_OCHCOMMON + TAG, "结束路距计算")
}
@Synchronized
fun calculateRouteSumLength(
location: MogoLocation,
) {
val autoPilotState = CallerAutoPilotStatusListenerManager.getAutoPilotStatusInfo().state
val locationInfo =
"自动驾驶状态:$autoPilotState line信息${lineId}定位信息:${location.latitude},${location.longitude},${location.heading} 当前速度:${location.gnssSpeed}"
if (mRoutePoints.isNullOrEmpty()) return
// 计算起始站点在轨迹中的信息 这个是一个常量
if (startStationInfo.stationPoint != null
&& startStationInfo.isNext == null
&& startStationInfo.index == null
&& startStationInfo.distance == null
) {
//要前往的站在轨迹中对应的点的信息
val startStationInfo = CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory,
mRoutePoints!!.size,
mRoutePoints!!,
startStationInfo.stationPoint!!,
1,
useHeading = false
)
this.startStationInfo.isNext = startStationInfo.second
this.startStationInfo.index = startStationInfo.first
this.startStationInfo.distance = startStationInfo.third
preCarLocationIndexInTrajectory = startStationInfo.first
val calculateData =
"距离起始站点最近的点:${startStationInfo.first} 点在站的后面:${startStationInfo.second} 距离点的距离:${startStationInfo.third}"
writeLog(calculateData, locationInfo)
}
// 计算结束站点在轨迹中的信息 这个是一个常量可以缓存
if (endStationInfo.stationPoint != null
&& endStationInfo.isNext == null
&& endStationInfo.index == null
&& endStationInfo.distance == null
) {
//要前往的站在轨迹中对应的点
val endStationInfo = CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory,
mRoutePoints!!.size,
mRoutePoints!!,
endStationInfo.stationPoint!!,
3,
useHeading = false
)
this.endStationInfo.isNext = endStationInfo.second
this.endStationInfo.index = endStationInfo.first
this.endStationInfo.distance = endStationInfo.third
val calculateData =
"距离结束站点最近的点:${endStationInfo.first} 点在站的后面:${endStationInfo.second} 距离点的距离:${endStationInfo.third}"
writeLog(calculateData, locationInfo)
}
try {
if (endStationInfo.stationPoint != null
&& endStationInfo.isNext != null
&& endStationInfo.index != null
&& endStationInfo.distance != null
&& startStationInfo.stationPoint != null
&& startStationInfo.isNext != null
&& startStationInfo.index != null
&& startStationInfo.distance != null
) {
calculateStationDistance()
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "计算两个站点间的距离")
}
val carLocationInfo: Triple<Int, Boolean?, Float> = if (endStationInfo.isNext == true) {
// 计算车的位置在轨迹中的信息 这个是一个变量可以缓存
CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory, endStationInfo.index!!, mRoutePoints!!, location, 2
)
} else {
CoordinateCalculateRouteUtil.getNearestPointInfo(
preCarLocationIndexInTrajectory,
endStationInfo.index!! + 1,
mRoutePoints!!,
location,
2
)
}
val calculateData =
"距离轨迹点最近的点:${carLocationInfo.first} 点在站的后面:${carLocationInfo.second} 距离点的距离:${carLocationInfo.third}"
writeLog(calculateData, locationInfo)
if (carLocationInfo.second == null || carLocationInfo.third > 1_000) {
preCarLocationIndexInTrajectory = 0
return
}
var maxDisatance = 0.0f
if (carLocationInfo.second == true) {
if (carLocationInfo.first > 0) {
maxDisatance = mRoutePointsDistance?.get(carLocationInfo.first - 1) ?: 0f
}
} else {
maxDisatance = mRoutePointsDistance?.get(carLocationInfo.first) ?: 0f
}
if (carLocationInfo.third > maxDisatance) {
preCarLocationIndexInTrajectory = 0
writeLog("到点的距离${carLocationInfo.third},最大距离${maxDisatance}", locationInfo)
return
}
preCarLocationIndexInTrajectory = carLocationInfo.first
// 距离回调
try {
if (distanceListeners.size > 0) {
invokeDistance(carLocationInfo, location, locationInfo)
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "距离计算错误")
}
// 不带站点轨迹回调
try {
if (trajectoryListeners.size > 0) {
invokeTrajectory(carLocationInfo, location)
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "轨迹线(轨迹两头)计算错误")
}
// 只展示站点之间轨迹
try {
if (trajectoryWithStationListeners.size > 0) {
invokeTrajectoryWithStation(carLocationInfo, location)
}
} catch (e: Exception) {
e(M_OCHCOMMON + TAG, "轨迹线(站点两头)计算错误")
}
}
/**
* 计算站点间距离
*/
private fun calculateStationDistance() {
var lastSumLength: Float
val key = getKey()
if (stationDistance[key] != null) {
return
}
val stationIndex = endStationInfo.index ?: 0
if (startStationInfo.index!! < stationIndex - 1) {
// subList 是[) 需要的是[]
val subList = mRoutePoints!!.subList(startStationInfo.index!!, stationIndex + 1)
// 轨迹点所有的距离
lastSumLength = CoordinateCalculateRouteUtil.calculateRouteSumLength(subList)
val stationDistance = endStationInfo.distance ?: 0f
if (endStationInfo.isNext == true) {// isNext = true
lastSumLength -= stationDistance
} else {// isNext = false
lastSumLength += stationDistance
}
if (startStationInfo.isNext == true) {// 是否是下一个 true 下一个
lastSumLength += startStationInfo.distance!!
} else {
lastSumLength -= startStationInfo.distance!!
}
} else {
val lastPoints = endStationInfo.stationPoint
lastSumLength = CoordinateUtils.calculateLineDistance(
startStationInfo.stationPoint!!.longitude, startStationInfo.stationPoint!!.latitude,
lastPoints!!.longitude, lastPoints.latitude
)
}
d(M_OCHCOMMON + TAG, "两站点距离:$lastSumLength")
stationDistance[key] = lastSumLength
if (distanceListeners.size > 0) {
distanceListeners.forEach {
//val tag = it.key
val listener = it.value
listener.stationDistanceCallback(lastSumLength)
}
}
}
private fun getKey(): String {
return "${startStationInfo.stationPoint!!.longitude}${startStationInfo.stationPoint!!.latitude}${endStationInfo.stationPoint!!.longitude}${endStationInfo.stationPoint!!.latitude}"
}
/**
* 到结束站点的距离 站点设置错误可能为负值
*/
private fun invokeDistance(
carLocationInfo: Triple<Int, Boolean?, Float>,
location: MogoLocation,
locationInfo: String
) {
var lastSumLength: Float
val stationIndex = endStationInfo.index ?: 0
if (carLocationInfo.first < stationIndex - 1) {
// subList 是[) 需要的是[]
val subList = mRoutePoints!!.subList(carLocationInfo.first, stationIndex + 1)
// 轨迹点所有的距离
lastSumLength = CoordinateCalculateRouteUtil.calculateRouteSumLength(subList)
val stationDistance = endStationInfo.distance ?: 0f
if (endStationInfo.isNext == true) {// isNext = true
lastSumLength -= stationDistance
} else {// isNext = false
lastSumLength += stationDistance
}
if (carLocationInfo.second == true) {// 是否是下一个 true 下一个
lastSumLength += carLocationInfo.third
} else {
lastSumLength -= carLocationInfo.third
}
} else {
val lastPoints = endStationInfo.stationPoint
lastSumLength = CoordinateUtils.calculateLineDistance(
lastPoints!!.longitude, lastPoints.latitude,
location.longitude, location.latitude
)
}
d(M_OCHCOMMON + TAG, "距离终点:$lastSumLength")
if (lastSumLength > maxDistanceAllPoint) {
// 大于最大值需要需要删除此次计算
writeLog("距离终点:$lastSumLength", locationInfo)
return
}
if (distanceListeners.size > 0) {
distanceListeners.forEach {
//val tag = it.key
val listener = it.value
listener.distanceCallback(lastSumLength)
}
}
}
/**
* 计算站点之间的轨迹(从轨迹起点算起到轨迹结束点结束)
* routeArrivied 已经走过的轨迹点
* routeArriving 还没有走过的轨迹
* location 车的轨迹点
*/
private fun invokeTrajectory(
carLocationInfo: Triple<Int, Boolean?, Float>,
location: MogoLocation
) {
if (mRoutePoints.isNullOrEmpty()) return
val routeArrivied = mutableListOf<MogoLocation>()
val routeArriving = mutableListOf<MogoLocation>()
if (carLocationInfo.second == true) {// isNext = true
routeArrivied.addAll(mRoutePoints!!.subList(0, carLocationInfo.first))
routeArriving.addAll(mRoutePoints!!.subList(carLocationInfo.first, mRoutePoints!!.size))
} else {// isNext = false
val indexNext = carLocationInfo.first + 1
routeArrivied.addAll(mRoutePoints!!.subList(0, indexNext))
routeArriving.addAll(mRoutePoints!!.subList(indexNext, mRoutePoints!!.size))
}
if (trajectoryListeners.size > 0) {
trajectoryListeners.forEach {
//val tag = it.key
val listener = it.value
listener.trajectoryCallback(routeArrivied, routeArriving, location)
}
}
}
/**
* 计算站点之间的轨迹(从开始站点算起到结束站点)
* routeArrivied 已经走过的轨迹点
* routeArriving 还没有走过的轨迹
* location 车的轨迹点
*/
private fun invokeTrajectoryWithStation(
carLocationInfo: Triple<Int, Boolean?, Float>,
location: MogoLocation
) {
if (mRoutePoints.isNullOrEmpty()) return
val routeArrivied = mutableListOf<MogoLocation>()
val routeArriving = mutableListOf<MogoLocation>()
var fromCut = 0
var endCut = mRoutePoints!!.size
if (startStationInfo.stationPoint != null
&& startStationInfo.isNext != null
&& startStationInfo.index != null
&& startStationInfo.distance != null
) {
if (startStationInfo.isNext == true) {
fromCut = startStationInfo.index!!
} else {
fromCut = startStationInfo.index!! + 1
}
}
if (endStationInfo.stationPoint != null
&& endStationInfo.isNext != null
&& endStationInfo.index != null
&& endStationInfo.distance != null
) {
if (endStationInfo.isNext == true) {
endCut = endStationInfo.index!!
} else {
endCut = endStationInfo.index!! + 1
}
}
d(M_OCHCOMMON + TAG, "根据站点切个:第一个点:$fromCut 最后一个点$endCut")
if (carLocationInfo.second == true) {// isNext = true
routeArrivied.addAll(mRoutePoints!!.subList(fromCut, carLocationInfo.first))
routeArriving.addAll(mRoutePoints!!.subList(carLocationInfo.first, endCut))
} else {// isNext = false
val indexNext = carLocationInfo.first + 1
routeArrivied.addAll(mRoutePoints!!.subList(fromCut, indexNext))
routeArriving.addAll(mRoutePoints!!.subList(indexNext, endCut))
}
routeArrivied.add(0, startStationInfo.stationPoint!!)
routeArriving.add(endStationInfo.stationPoint!!)
if (trajectoryWithStationListeners.size > 0) {
trajectoryWithStationListeners.forEach {
//val tag = it.key
val listener = it.value
listener.trajectoryWithStationCallback(routeArrivied, routeArriving, location)
}
}
}
// @ChainLog(
// linkChainLog = ChainConstant.CHAIN_TYPE_OCH,
// linkCode = ChainConstant.CHAIN_SOURCE_OCH,
// nodeAliasCode = ChainConstant.CHAIN_CODE_OCH_COMMON_DISTANCE,
// paramIndexes = [0,1]
// )
private fun writeLog(carLocationInfo: String, location: String) {
d(M_OCHCOMMON + TAG, carLocationInfo)
d(M_OCHCOMMON + TAG, location)
}
/**
* 返回空为可启动自驾
* 返回其他不可启动自驾 返回为原因
*/
fun canStartAutopilot(lineId: Number?): String {
if (lineId == null) {
OchAutopilotAnalytics.triggerDistance2LineorStation("未传轨迹ID")
return "请确认线路ID"
}
OchAutopilotAnalytics.triggerDistance2LineorStation("条件记录:lineId:${lineId}----this.lineId:${this.lineId}、mRoutePoints情况:${mRoutePoints?.size}")
try {
if (mRoutePoints.isNullOrEmpty()) {
// 判断距离起始站的距离
// 没有收到过轨迹
// 收到过轨迹 且底盘没有在自动驾驶中(没办法申请轨迹) 自驾中重启底盘的形式
val redCatche = TrajectoryCache.redCatche(lineId.toString())
return if (redCatche.isNullOrEmpty()) {
distanceWithStartStation()
} else {
val currentPoint = OchLocationManager.getGCJ02Location()
distanceWithTrajectory(redCatche,currentPoint)
}
} else {
return if (this.lineId == 0L || this.lineId == null) {
val currentPoint = OchLocationManager.getGCJ02Location()
distanceWithTrajectory(mRoutePoints!!,currentPoint)
} else {
if (lineId.toLong() != this.lineId) {
// 判断距离起始站的距离
distanceWithStartStation()
} else {
val currentPoint = OchLocationManager.getGCJ02Location()
distanceWithTrajectory(mRoutePoints!!,currentPoint)
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
OchAutopilotAnalytics.triggerDistance2LineorStation("距离站点距离:不支持的条件直接放过")
return ""
}
/**
* 距离站点的距离
*/
private fun distanceWithStartStation(): String {
if (startStationInfo.stationPoint == null) {
return errorTypeNoneLineId
}
val currentPoint = OchLocationManager.getGCJ02Location()
val distance = CoordinateUtils.calculateLineDistance(
currentPoint.longitude,
currentPoint.latitude,
startStationInfo.stationPoint!!.longitude,
startStationInfo.stationPoint!!.latitude
)
OchAutopilotAnalytics.triggerDistance2LineorStation("距离站点距离:${distance}")
return if (distance <= OchCommonConst.AUTOMATIC_PLANNING_MAX_DISTANCE) {
""
} else {
"距离起始站点超过15米"
}
}
/**
* 距离轨迹的距离
*/
fun distanceWithTrajectory(redCatche: MutableList<MogoLocation>,currentPoint:MogoLocation): String {
redCatche.forEachIndexed { index, mogoLocation ->
if(index!=0){
val prePoint = redCatche.get(index - 1)
val pointToLine = LocationUtils.pointToLine(
prePoint.longitude,
prePoint.latitude,
mogoLocation.longitude,
mogoLocation.latitude,
currentPoint.longitude,
currentPoint.latitude
)
if(pointToLine<=OchCommonConst.AUTOMATIC_PLANNING_MAX_DISTANCE){
OchAutopilotAnalytics.triggerDistance2LineorStation("距离轨迹线距离:${pointToLine}")
return ""
}
}
}
OchAutopilotAnalytics.triggerDistance2LineorStation("距离轨迹线超过15m,无法启动自驾")
return "距离轨迹线超过15米"
}
}

View File

@@ -0,0 +1,236 @@
package com.mogo.och.bridge.ui.autopilot
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.animation.LinearInterpolator
import androidx.annotation.DrawableRes
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.bridge.R
import com.mogo.och.bridge.autopilot.autopilot.IOchAutopilotStatusListener
import com.mogo.och.common.module.utils.BigFrameAnimatorContainer
import com.mogo.och.common.module.utils.ResourcesUtils
import kotlinx.android.synthetic.main.common_autopilot_view.view.aciv_autopilot_running_ani
import kotlinx.android.synthetic.main.common_autopilot_view.view.aciv_autopilot_state
import kotlinx.android.synthetic.main.common_autopilot_view.view.actv_autopilot_head
import kotlinx.android.synthetic.main.common_autopilot_view.view.actv_autopilot_state
import kotlinx.android.synthetic.main.common_autopilot_view.view.actv_pxjs_state
class AutopilotState @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), IOchAutopilotStatusListener,
AutopilotStateModel.AutopilotStateCallback {
private val TAG = "AutopilotState"
private var viewModel:AutopilotStateModel?=null
private var autopilotStateAnimator: BigFrameAnimatorContainer?=null
private lateinit var autopilotLoadingAnimator: ObjectAnimator
private lateinit var autopilotChangeStateAnimator: ObjectAnimator
private fun initView() {
LayoutInflater.from(context).inflate(R.layout.common_autopilot_view, this, true)
autopilotStateAnimator = BigFrameAnimatorContainer(R.array.in_auto, 31, aciv_autopilot_running_ani)
autopilotLoadingAnimator = ObjectAnimator.ofFloat(aciv_autopilot_state, "rotation", 0f, 360f);
autopilotLoadingAnimator.interpolator = LinearInterpolator()
autopilotLoadingAnimator.repeatCount = -1 //无限循环
autopilotLoadingAnimator.duration = 2000 //无限循环
autopilotChangeStateAnimator = ObjectAnimator.ofFloat(aciv_autopilot_state, "alpha", 1f, 0f,1f);
autopilotChangeStateAnimator.interpolator = LinearInterpolator()
autopilotChangeStateAnimator.duration = 300
onClick {
startAutopilot()
}
}
fun startAutopilot(){
viewModel?.startAutopilot()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
viewModel = findViewTreeViewModelStoreOwner()?.let {
ViewModelProvider(it).get(AutopilotStateModel::class.java)
}
viewModel?.setViewCallback(this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
autopilotStateAnimator?.release()
}
init {
try {
initView()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun startAutopilotAnimation() {
CallerLogger.d(TAG,"播放启动自驾动画")
updateImageInAnimator(R.drawable.common_autopilot_starting_new)
actv_pxjs_state.visibility = GONE
actv_autopilot_head.visibility = VISIBLE
actv_autopilot_state.visibility = VISIBLE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_starting)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = false
autopilotLoadingAnimator.start()
val animatorSet = AnimatorSet()
animatorSet.playTogether(autopilotChangeStateAnimator, autopilotLoadingAnimator)
animatorSet.start()
}
override fun stopAutopilotAnimation() {
CallerLogger.d(TAG,"结束启动自驾动画")
updateImageInAnimator(R.drawable.common_autopilot_enable)
actv_pxjs_state.visibility = GONE
actv_autopilot_head.visibility = VISIBLE
actv_autopilot_state.visibility = VISIBLE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_starting)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = false
autopilotLoadingAnimator.cancel()
autopilotChangeStateAnimator.start()
}
override fun inAutopilot() {
CallerLogger.d(TAG,"展示自驾中UI")
updateImageInAnimator(R.drawable.common_autopilot_running)
actv_pxjs_state.visibility = GONE
actv_autopilot_head.visibility = VISIBLE
actv_autopilot_state.visibility = VISIBLE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_running)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = false
aciv_autopilot_running_ani.visibility = VISIBLE
autopilotStateAnimator?.start()
autopilotLoadingAnimator.cancel()
autopilotChangeStateAnimator.start()
}
override fun autopilotDisable() {
CallerLogger.d(TAG,"不可启动自驾")
updateImageInAnimator(R.drawable.common_autopilot_unenable)
actv_pxjs_state.visibility = GONE
actv_autopilot_head.visibility = VISIBLE
actv_autopilot_state.visibility = VISIBLE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_start)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = false
aciv_autopilot_running_ani.visibility = GONE
autopilotStateAnimator?.stop()
autopilotLoadingAnimator.cancel()
autopilotChangeStateAnimator.start()
}
override fun canStartAutopilot() {
CallerLogger.d(TAG,"可以启动自驾")
updateImageInAnimator(R.drawable.common_autopilot_enable)
actv_pxjs_state.visibility = GONE
actv_autopilot_head.visibility = VISIBLE
actv_autopilot_state.visibility = VISIBLE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_start)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = true
aciv_autopilot_running_ani.visibility = GONE
autopilotStateAnimator?.stop()
autopilotLoadingAnimator.cancel()
autopilotChangeStateAnimator.start()
}
override fun inRemoteDriver() {
CallerLogger.d(TAG,"展示平行驾驶中UI")
updateImageInAnimator(R.drawable.common_autopilot_running)
actv_pxjs_state.visibility = VISIBLE
actv_autopilot_head.visibility = GONE
actv_autopilot_state.visibility = GONE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_start)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = false
aciv_autopilot_running_ani.visibility = VISIBLE
autopilotStateAnimator?.start()
autopilotLoadingAnimator.cancel()
autopilotChangeStateAnimator.start()
}
override fun startAutopilotSuccess() {
}
override fun startAutopilotFail() {
CallerLogger.d(TAG,"启动自动驾驶失败")
updateImageInAnimator(R.drawable.common_autopilot_fail)
actv_pxjs_state.visibility = GONE
actv_autopilot_head.visibility = VISIBLE
actv_autopilot_state.visibility = VISIBLE
actv_autopilot_head.setTextColor(ResourcesUtils.getColor(R.color.common_B3FFFFFF))
actv_autopilot_state.setTextColor(ResourcesUtils.getColor(R.color.common_FF4E41))
actv_autopilot_state.text = ResourcesUtils.getString(R.string.common_autopilot_fail)
actv_pxjs_state.setTextColor(ResourcesUtils.getColor(R.color.common_19FFCB))
AutopilotState@this.isEnabled = false
aciv_autopilot_running_ani.visibility = GONE
autopilotStateAnimator?.stop()
autopilotLoadingAnimator.cancel()
autopilotChangeStateAnimator.start()
}
private fun updateImageInAnimator(@DrawableRes resource:Int){
UiThreadHandler.postDelayed({
aciv_autopilot_state.setImageResource(resource)
},150,UiThreadHandler.MODE.QUEUE)
}
}

View File

@@ -0,0 +1,206 @@
package com.mogo.och.bridge.ui.autopilot
import androidx.lifecycle.ViewModel
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.common.module.debug.autopilot.AutopilotStateDebug
import com.mogo.och.common.module.debug.autopilot.IOchDebugAutopilotStatusListener
import com.mogo.och.bridge.autopilot.autopilot.IOchAutopilotStatusListener
import com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager
import com.mogo.och.bridge.autopilot.line.ILineCallback
import com.mogo.och.bridge.autopilot.line.LineManager
import com.mogo.och.common.module.manager.logchainanalytic.OchChainLogManager
import com.mogo.och.common.module.manager.loop.BizLoopManager
import com.mogo.och.common.module.utils.RxUtils
import java.util.concurrent.atomic.AtomicBoolean
/**
* @author XuXinChao
* @description BadCase录包管理页面
* @since: 2022/12/15
*/
class AutopilotStateModel : ViewModel(),
com.mogo.och.bridge.autopilot.autopilot.IOchAutopilotStatusListener,
com.mogo.och.bridge.autopilot.line.ILineCallback,
IOchDebugAutopilotStatusListener {
private val TAG = AutopilotStateModel::class.java.simpleName
private var viewCallback: AutopilotStateCallback?=null
private val isPalyStartAni = AtomicBoolean(false)
override fun onCleared() {
this.viewCallback = null
AutopilotStateDebug.removeListener(TAG)
com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.removeListener(TAG)
com.mogo.och.bridge.autopilot.line.LineManager.removeListener(TAG)
}
fun setViewCallback(viewCallback: AutopilotStateCallback){
this.viewCallback = viewCallback
AutopilotStateDebug.addListener(TAG,this)
com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.addListener(TAG,this)
com.mogo.och.bridge.autopilot.line.LineManager.addListener(TAG,this)
}
override fun debugStatusChange(debugStatus: Boolean) {
super.debugStatusChange(debugStatus)
if(debugStatus){
com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.removeListener(TAG)
com.mogo.och.bridge.autopilot.line.LineManager.removeListener(TAG)
}else{
com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.addListener(TAG,this)
com.mogo.och.bridge.autopilot.line.LineManager.addListener(TAG,this)
}
}
override fun debugDispatchState(state: Int?) {
super.debugDispatchState(state)
when (state) {
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE-> {// 不可用 不可启动自驾
this.viewCallback?.autopilotDisable()
}
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE-> {
this.viewCallback?.canStartAutopilot()
}
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING-> {// 自驾中
this.viewCallback?.stopAutopilotAnimation()
this.viewCallback?.inAutopilot()
}
IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING-> {// 平行驾驶中
this.viewCallback?.stopAutopilotAnimation()
this.viewCallback?.inRemoteDriver()
}
}
}
override fun onAutopilotStatusResponse(state: Int) {
OchChainLogManager.writeChainLog("自驾信息","自驾状态变化:${state}")
autopilotStateChange()
}
override fun canStartAutopilot(canStart: Boolean) {
OchChainLogManager.writeChainLog("自驾信息","能否启动自驾:${canStart}")
autopilotStateChange()
}
override fun onFsmCanStartAutopilot(can: Boolean) {
OchChainLogManager.writeChainLog("自驾信息","FSM能否启动自驾:${can}")
autopilotStateChange()
}
private fun autopilotStateChange(){
// 正在起自驾过程中
// 自驾状态变化为非自驾状态
// 或者
// FSM 状态改为不能启动自驾
// 按照启动自驾失败计算
if(isPalyStartAni.get() &&
(!CallerAutoPilotControlManager.isCanStartAutopilot(false)
|| com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.autopilotState!=IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING)){
OchChainLogManager.writeChainLog("自驾信息","正在起自驾过程中、自驾状态变化切为非自驾状态或者FSM 状态改为不能启动自驾")
startAutopilotFail()
return
}
BizLoopManager.runInMainThread{
OchChainLogManager.writeChainLog("自驾信息","自驾状态:${com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.autopilotState} 能否启动自驾:${CallerAutoPilotControlManager.isCanStartAutopilot(false)}")
when (com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.autopilotState) {
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE-> {// 不可用 不可启动自驾
this.viewCallback?.autopilotDisable()
}
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE-> {
if (CallerAutoPilotControlManager.isCanStartAutopilot(false) && com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.canStartAutopilotFromFSM) {// 不可用
this.viewCallback?.canStartAutopilot()
} else {// 部分可用
this.viewCallback?.autopilotDisable()
}
}
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING-> {// 自驾中
this.viewCallback?.stopAutopilotAnimation()
this.viewCallback?.inAutopilot()
}
IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING-> {// 平行驾驶中
this.viewCallback?.stopAutopilotAnimation()
this.viewCallback?.inRemoteDriver()
}
}
}
}
fun startAutopilot() {
OchChainLogManager.writeChainLog("自驾信息","启动自驾")
if(AutopilotStateDebug.debugStatus){
sendStartAutopilotSuccess()
RxUtils.createSubscribe(5_000) {
startAutopilotFail()
}
}else {
com.mogo.och.bridge.autopilot.line.LineManager.startAutopilot()
}
}
/**
* 条件过滤完成 正式进入启动自驾状态
*/
override fun sendStartAutopilotSuccess() {
OchChainLogManager.writeChainLog("自驾信息","启动自驾成功")
BizLoopManager.runInMainThread {
this.viewCallback?.startAutopilotAnimation()
isPalyStartAni.set(true)
}
}
override fun startAutopilotTimeOut() {
OchChainLogManager.writeChainLog("自驾信息","启动自驾超时失败")
if(com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.autopilotState == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
autopilotStateChange()
}else {
startAutopilotFail()
}
}
override fun startAutopilotFailure(startFailedCode: String?, startFailedMessage: String?) {
OchChainLogManager.writeChainLog("自驾信息","底盘强制失败原因:${startFailedCode}_${startFailedMessage}")
if(com.mogo.och.bridge.autopilot.autopilot.OchAutoPilotStatusListenerManager.autopilotState == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
autopilotStateChange()
}else {
startAutopilotFail()
}
}
private fun startAutopilotFail(){
BizLoopManager.runInMainThread{
this.viewCallback?.stopAutopilotAnimation()
this.viewCallback?.startAutopilotFail()
this.isPalyStartAni.set(false)
UiThreadHandler.postDelayed({
autopilotStateChange()
},3000,UiThreadHandler.MODE.QUEUE)
}
}
interface AutopilotStateCallback{
//开始动画
fun startAutopilotAnimation()
//结束动画
fun stopAutopilotAnimation()
// 进入自动驾驶
fun inAutopilot()
// 自动驾驶不可用
fun autopilotDisable()
// 可用启动自驾
fun canStartAutopilot()
// 进入平行驾驶
fun inRemoteDriver()
// 启动自驾成功
fun startAutopilotSuccess()
// 启动自驾失败
fun startAutopilotFail()
}
}

View File

@@ -0,0 +1,75 @@
package com.mogo.och.bridge.ui.drawline
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import com.mogo.eagle.core.utilcode.kotlin.onClick
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.bridge.R
import com.mogo.och.bridge.autopilot.line.LineManager
import com.mogo.och.common.module.utils.ResourcesUtils
import kotlinx.android.synthetic.main.common_line_view.view.iv_toolkit_item_head
import kotlinx.android.synthetic.main.common_line_view.view.iv_toolkit_item_title
/**
*
*/
class LineView : ConstraintLayout, LineViewModel.ILineViewCallback {
private val TAG = "LineView"
constructor(context: Context) : super(context)
constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(
context,
attributeSet,
defStyleAttr
)
private fun initView() {
LayoutInflater.from(context).inflate(R.layout.common_line_view, this, true)
iv_toolkit_item_head.setImageResource(R.drawable.common_map_line_close)
iv_toolkit_item_title.text = ResourcesUtils.getString(R.string.common_line_info)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
onClick {
if (LineManager.hasDrawnGlobalTrajectory()) {
LineManager.clearGlobalTrajectory(false)
} else {
val drawGlobalTrajectory = LineManager.drawGlobalTrajectory()
if (!drawGlobalTrajectory.first) {
ToastUtils.showLong(drawGlobalTrajectory.second)
}
}
}
val viewModel = findViewTreeViewModelStoreOwner()?.let {
ViewModelProvider(it).get(LineViewModel::class.java)
}
viewModel?.setDistanceCallback(this)
}
init {
try {
initView()
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun setImageViewResource(name: Int) {
iv_toolkit_item_head.setImageResource(name)
}
}

View File

@@ -0,0 +1,46 @@
package com.mogo.och.bridge.ui.drawline
import androidx.annotation.DrawableRes
import androidx.lifecycle.ViewModel
import com.mogo.och.common.module.R
import com.mogo.och.bridge.autopilot.line.ILineCallback
import com.mogo.och.bridge.autopilot.line.LineManager
class LineViewModel : ViewModel(), ILineCallback {
private val TAG = LineViewModel::class.java.simpleName
private var viewCallback: ILineViewCallback? = null
init {
}
override fun onCleared() {
super.onCleared()
LineManager.removeListener(TAG)
this.viewCallback = null
}
fun setDistanceCallback(viewCallback: ILineViewCallback) {
LineManager.addListener(TAG,this)
this.viewCallback = viewCallback
}
override fun clearLineSuccess() {
this.viewCallback?.setImageViewResource(R.drawable.common_map_line_close)
}
override fun drawLineSuccess() {
this.viewCallback?.setImageViewResource(R.drawable.common_map_line_open)
}
override fun drawLineFail() {
this.viewCallback?.setImageViewResource(R.drawable.common_map_line_close)
}
interface ILineViewCallback {
fun setImageViewResource(@DrawableRes name: Int)
}
}

View File

@@ -0,0 +1,23 @@
package com.mogo.och.bridge.ui.mapdirectionview;
/**
* @author xiaoyuzhou
* @date 2021/6/24 11:33 上午
*/
public interface IMapDirectionView {
/**
* 绘制路径线
*/
void drawablePolyline();
/**
* 清除路径线
*/
void clearPolyline();
/**
* 设置路径中起终点marker
*/
void setLineMarker();
}

View File

@@ -0,0 +1,404 @@
package com.mogo.och.bridge.ui.mapdirectionview
import android.content.Context
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.RelativeLayout
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import com.amap.api.maps.AMap
import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.TextureMapView
import com.amap.api.maps.model.BitmapDescriptor
import com.amap.api.maps.model.BitmapDescriptorFactory
import com.amap.api.maps.model.CameraPosition
import com.amap.api.maps.model.CustomMapStyleOptions
import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds
import com.amap.api.maps.model.Marker
import com.amap.api.maps.model.MarkerOptions
import com.amap.api.maps.model.Polyline
import com.amap.api.maps.model.PolylineOptions
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
import com.mogo.eagle.core.utilcode.mogo.MapAssetStyleUtils
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.common.module.R
import com.mogo.och.bridge.autopilot.location.OchLocationManager
import com.mogo.och.shuttle.passenger.ui.mapdirectionview.MapDirectionViewModel
import me.jessyan.autosize.utils.AutoSizeUtils
/**
* 乘客屏小地图
*/
class MapDirectionView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : RelativeLayout(context, attrs, defStyleAttr), IMoGoChassisLocationGCJ02Listener,
IMapDirectionView, AMap.OnCameraChangeListener,
MapDirectionViewModel.ItineraryViewCallback {
companion object {
//小地图名称
const val TAG = "BusPassengerMapDirectionView"
}
private var mapStylePath: String? = null
private var mapStyleExtraPath: String? = null
private var carDrawable: Int = -1
private var compassDrawable: Int = -1
private var startPointDrawable: Int = -1
private var endPointDrawable: Int = -1
private var arrivedDrawable: Int = -1
private var unArrivedDrawable: Int = -1
private var resetDrawableMarginRight: Int = -1
private var resetDrawableMarginBottom: Int = -1
private var leftPadding: Int = 100
private var topPadding: Int = 100
private var rightPadding: Int = 100
private var bottomPadding: Int = 100
private lateinit var mAMapNaviView: TextureMapView
private lateinit var mAMap: AMap
private var mPolyline: Polyline? = null
private val mLineMarkers: MutableList<Marker> = ArrayList()
private lateinit var mCarMarker: Marker
// 除了开始站点和结束站点的中间站点 用来设置图标和地图范围圈定
private val mLineStationLatLng: MutableList<LatLng> = ArrayList() //站点坐标数据
var textureList: MutableList<BitmapDescriptor?> = ArrayList()
var texIndexList: MutableList<Int> = ArrayList()
private var mArrivedRes: BitmapDescriptor? = null
private var mUnArrivedRes: BitmapDescriptor? = null
private var mStartStationPoint: BitmapDescriptor? = null
private var mEndStationPoint: BitmapDescriptor? = null
private var mArrivedStationPoint: BitmapDescriptor? = null
private var mUArrivedStationPoint: BitmapDescriptor? = null
private val routeArrived: MutableList<LatLng> = ArrayList()
private val routeArriving: MutableList<LatLng> = ArrayList()
private var location: MogoLocation? = null
init {
try {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.MapDirectionView)
mapStylePath = typedArray.getString(R.styleable.MapDirectionView_mapStylePath)
mapStyleExtraPath = typedArray.getString(R.styleable.MapDirectionView_mapStyleExtraPath)
carDrawable = typedArray.getResourceId(R.styleable.MapDirectionView_carDrawable, -1)
compassDrawable = typedArray.getResourceId(R.styleable.MapDirectionView_compassDrawable, -1)
startPointDrawable =
typedArray.getResourceId(R.styleable.MapDirectionView_startPointDrawable, -1)
endPointDrawable =
typedArray.getResourceId(R.styleable.MapDirectionView_endPointDrawable, -1)
arrivedDrawable = typedArray.getResourceId(R.styleable.MapDirectionView_arrivedDrawable, -1)
unArrivedDrawable =
typedArray.getResourceId(R.styleable.MapDirectionView_unArrivedDrawable, -1)
resetDrawableMarginRight = typedArray.getResourceId(
R.styleable.MapDirectionView_resetDrawableMarginRight,
AutoSizeUtils.dp2px(context, 40f)
)
resetDrawableMarginBottom = typedArray.getResourceId(
R.styleable.MapDirectionView_resetDrawableMarginBottom,
AutoSizeUtils.dp2px(context, 40f)
)
leftPadding = typedArray.getInt(R.styleable.MapDirectionView_leftPadding, 100)
topPadding = typedArray.getInt(R.styleable.MapDirectionView_topPadding, 100)
rightPadding = typedArray.getInt(R.styleable.MapDirectionView_rightPadding, 100)
bottomPadding = typedArray.getInt(R.styleable.MapDirectionView_bottomPadding, 100)
typedArray.recycle()
initView(context)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun initView(context: Context) {
d(SceneConstant.M_BUS_P + TAG, "initView")
val smpView = LayoutInflater.from(context).inflate(R.layout.shuttle_p_jl_map_view, this)
mAMapNaviView = smpView.findViewById<View>(R.id.bus_p_line_amap_view) as TextureMapView
initAMapView()
// 注册定位监听
OchLocationManager.addGCJ02Listener(TAG, 1, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
// 注册定位监听
OchLocationManager.removeGCJ02Listener(TAG)
}
private fun initAMapView() {
mAMap = mAMapNaviView.map
// 设置导航地图模式aMap是地图控制器对象。
mAMap.mapType = AMap.MAP_TYPE_NIGHT
// 关闭显示实时路况图层aMap是地图控制器对象。
mAMap.isTrafficEnabled = false
// 设置 锚点 图标
mCarMarker = mAMap.addMarker(
MarkerOptions().icon(
BitmapDescriptorFactory.fromResource(if (carDrawable != -1) carDrawable else R.drawable.common_map_car))
.anchor(0.5f, 0.5f)
)
mArrivedRes = BitmapDescriptorFactory.fromResource(if (arrivedDrawable != -1) arrivedDrawable else R.drawable.taxi_map_arrow_arrived)
mUnArrivedRes = BitmapDescriptorFactory.fromResource(if (unArrivedDrawable != -1) unArrivedDrawable else R.drawable.taxi_map_arrow_un_arrive)
mStartStationPoint = BitmapDescriptorFactory.fromResource(if (startPointDrawable != -1) startPointDrawable else R.drawable.common_map_start_point)
mEndStationPoint = BitmapDescriptorFactory.fromResource(if (endPointDrawable != -1) endPointDrawable else R.drawable.common_map_end_point)
mArrivedStationPoint = BitmapDescriptorFactory.fromResource(if (endPointDrawable != -1) endPointDrawable else R.drawable.common_map_arrived_point)
mUArrivedStationPoint = BitmapDescriptorFactory.fromResource(if (endPointDrawable != -1) endPointDrawable else R.drawable.common_map_unarrived_point)
// 加载自定义样式
val customMapStyleOptions = CustomMapStyleOptions().setEnable(true)
if (!mapStylePath.isNullOrEmpty() && !mapStyleExtraPath.isNullOrEmpty()) {
customMapStyleOptions.styleData =
MapAssetStyleUtils.getAssetsStyle(context, mapStylePath)
customMapStyleOptions.styleExtraData =
MapAssetStyleUtils.getAssetsExtraStyle(context, mapStyleExtraPath)
}
// 设置自定义样式
mAMap.setCustomMapStyle(customMapStyleOptions)
// 设置地图的样式
mAMap.uiSettings.apply {
isZoomControlsEnabled = false // 地图缩放级别的交换按钮
setAllGesturesEnabled(true) // 所有手势
isMyLocationButtonEnabled = false // 显示默认的定位按钮
setLogoBottomMargin(-150) //设置Logo下边界距离屏幕底部的边距,设置为负值即可
}
mAMap.setOnMapLoadedListener {
d(SceneConstant.M_BUS_P + TAG, "smp---onMapLoaded")
// 加载自定义样式
val options = CustomMapStyleOptions().setEnable(true)
if (!mapStylePath.isNullOrEmpty() && !mapStyleExtraPath.isNullOrEmpty()) {
options.styleData =
MapAssetStyleUtils.getAssetsStyle(context, mapStylePath)
options.styleExtraData =
MapAssetStyleUtils.getAssetsExtraStyle(context, mapStyleExtraPath)
}
// 设置自定义样式
mAMap.setCustomMapStyle(options)
mAMap.uiSettings?.isZoomControlsEnabled = false
mAMapNaviView.map.setPointToCenter(
mAMapNaviView.width / 2,
mAMapNaviView.height / 2
)
}
//设置地图状态的监听接口
mAMap.setOnCameraChangeListener(this)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
val viewModel = findViewTreeViewModelStoreOwner()?.let {
ViewModelProvider(it).get(MapDirectionViewModel::class.java)
}
viewModel?.setDistanceCallback(this)
}
fun clearMapView() {
UiThreadHandler.post( {
clearPolyline()
clearCoordinatesLatLng()
}, UiThreadHandler.MODE.QUEUE)
}
override fun onChassisLocationGCJ02(mogoLocation: MogoLocation?) {
if (mogoLocation == null) {
return
}
val currentLatLng = LatLng(mogoLocation.latitude, mogoLocation.longitude)
//更新车辆位置
mCarMarker.rotateAngle = (360 - mogoLocation.heading).toFloat()
mCarMarker.position = currentLatLng
mCarMarker.setToTop()
try {
//圈定地图显示范围
val boundsBuilder = LatLngBounds.Builder()
routeArrived.forEach {
boundsBuilder.include(it)
}
routeArriving.forEach {
boundsBuilder.include(it)
}
mLineStationLatLng.forEach {
boundsBuilder.include(it)
}
boundsBuilder.include(currentLatLng)
mAMap.moveCamera(
CameraUpdateFactory.newLatLngBoundsRect(
boundsBuilder.build(),
100,
100,
100,
100
)
)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun drawablePolyline() {
if (routeArrived.isEmpty() && routeArriving.isEmpty()) {
d(SceneConstant.M_TAXI + TAG, "没有点")
return
}
try {
texIndexList.clear()
val allPoints = ArrayList(routeArrived)
for (i in routeArrived.indices) {
if (routeArrived.size > 1 && i < routeArrived.size - 1) {
texIndexList.add(0)
}
}
texIndexList.add(0)
location?.let {
allPoints.add(LatLng(it.latitude, it.longitude))
}
allPoints.addAll(routeArriving)
for (ignored in routeArrived) {
texIndexList.add(1)
}
mPolyline?.let {
it.points = allPoints
it.options.customTextureIndex = texIndexList
return
}
if (textureList.isEmpty()) {
textureList.add(mArrivedRes)
textureList.add(mUnArrivedRes)
}
//设置线段纹理
val polylineOptions = PolylineOptions().apply {
addAll(allPoints)
isUseTexture = true
width(15f)
lineCapType(PolylineOptions.LineCapType.LineCapRound)
customTextureList = textureList
customTextureIndex = texIndexList
}
// 绘制线
mPolyline = mAMap.addPolyline(polylineOptions)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun clearPolyline() {
mPolyline?.remove()
mPolyline = null
}
override fun setLineMarker() {}
fun clearCoordinatesLatLng() {
try {
textureList.clear()
texIndexList.clear()
routeArrived.clear()
routeArriving.clear()
mLineStationLatLng.clear()
}catch (e:Exception){
e.printStackTrace()
}
d(SceneConstant.M_BUS_P + TAG, " mCoordinatesLatLng.clear ")
}
fun onCreateView(savedInstanceState: Bundle?) {
mAMapNaviView.onCreate(savedInstanceState)
}
fun onResume() {
mAMapNaviView.onResume()
}
fun onPause() {
mAMapNaviView.onPause()
}
fun onDestroy() {
mAMapNaviView.onDestroy()
}
override fun setCoordinatesLatLng(
routeArrived: List<LatLng>,
routeArriving: List<LatLng>,
location: MogoLocation?
) {
try {
this.routeArrived.clear()
this.routeArrived.addAll(routeArrived)
this.routeArriving.clear()
this.routeArriving.addAll(routeArriving)
this.location = location
UiThreadHandler.post({
drawablePolyline()
}, UiThreadHandler.MODE.QUEUE)
}catch (e:Exception){
e.printStackTrace()
}
}
fun clearLineMarkers() {
for (i in mLineMarkers.indices) {
mLineMarkers[i].isVisible = false
mLineMarkers[i].remove()
}
mLineMarkers.clear()
}
// 设置站点
fun setLinePointMarkerAndDraw(mLineStationsList: List<LatLng>, currentIndex: Int) {
clearLineMarkers()
mLineStationLatLng.clear()
mLineStationLatLng.addAll(mLineStationsList)
if (mLineStationsList.isNotEmpty()) {
// 起点marker, 终点marker, 过站marker, 未过站marker
val size = mLineStationsList.size
val mStartMarker = mAMap.addMarker(
MarkerOptions()
.icon(mStartStationPoint)
)
val mEndMarker = mAMap.addMarker(MarkerOptions().icon(mEndStationPoint))
mStartMarker.position = mLineStationsList[0]
mLineMarkers.add(0, mStartMarker)
for (i in mLineStationsList.indices) {
if (currentIndex <= i && i < size - 1 && i > 0) { //未到达
val unArrivedMarker = mAMap.addMarker(MarkerOptions().icon(mUArrivedStationPoint))
unArrivedMarker.position = mLineStationsList[i]
mLineMarkers.add(i, unArrivedMarker)
} else if (i in 1 until currentIndex) {
val arrivedMarker = mAMap.addMarker(MarkerOptions().icon(mArrivedStationPoint))
arrivedMarker.position = mLineStationsList[i]
mLineMarkers.add(i, arrivedMarker)
}
}
mEndMarker.position = mLineStationsList[size - 1]
mLineMarkers.add(size - 1, mEndMarker)
}
}
override fun onCameraChange(cameraPosition: CameraPosition) {
}
override fun onCameraChangeFinish(cameraPosition: CameraPosition) {}
}

View File

@@ -0,0 +1,60 @@
package com.mogo.och.shuttle.passenger.ui.mapdirectionview
import androidx.lifecycle.ViewModel
import com.amap.api.maps.model.LatLng
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.och.bridge.distance.ITrajectoryListener
import com.mogo.och.bridge.distance.TrajectoryAndDistanceManager
class MapDirectionViewModel: ViewModel(), ITrajectoryListener {
private val TAG = MapDirectionViewModel::class.java.simpleName
private var viewCallback: ItineraryViewCallback?=null
init {
TrajectoryAndDistanceManager.addTrajectoryListener(TAG,this)
}
fun setDistanceCallback(viewCallback: ItineraryViewCallback){
this.viewCallback = viewCallback
}
override fun onCleared() {
super.onCleared()
this.viewCallback = null
TrajectoryAndDistanceManager.removeListener(TAG)
}
interface ItineraryViewCallback{
fun setCoordinatesLatLng(routeArrived: List<LatLng>, routeArriving: List<LatLng>, location: MogoLocation?)
}
override fun trajectoryCallback(
routeArrivied: MutableList<MogoLocation>,
routeArriving: MutableList<MogoLocation>,
location: MogoLocation
) {
val routeArrivedTemp: MutableList<LatLng> = ArrayList()
val routeArrivingTemp: MutableList<LatLng> = ArrayList()
var temp: LatLng
for (mogoLocation in routeArrivied) {
temp = LatLng(mogoLocation.latitude, mogoLocation.longitude)
routeArrivedTemp.add(temp)
}
for (mogoLocation in routeArriving) {
temp = LatLng(mogoLocation.latitude, mogoLocation.longitude)
routeArrivingTemp.add(temp)
}
this.viewCallback?.setCoordinatesLatLng(
routeArrivedTemp,
routeArrivingTemp,
location
)
CallerLogger.d(TAG,"已经走过的点routeArrivied:${routeArrivied.size} 未走过的点routeArriving${routeArriving.size}")
}
}

View File

@@ -0,0 +1,870 @@
package com.mogo.och.bridge.utils
import android.content.Context
import android.location.Location
import com.amap.api.maps.CoordinateConverter
import com.amap.api.maps.model.LatLng
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.utilcode.mogo.logger.Logger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.eagle.core.utilcode.util.CoordinateUtils
import com.mogo.eagle.core.utilcode.util.DrivingDirectionUtils
import com.mogo.och.bridge.distance.DistanceDegree
import mogo.telematics.pad.MessagePad
import java.util.TreeMap
import kotlin.math.acos
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
/**
* @author: wangmingjun
* @date: 2022/3/28
*/
object CoordinateCalculateRouteUtil {
@JvmStatic
fun <T> calculateRouteSumLength(points: List<T>?): Float {
if (null == points || points.size == 0) return 0f
var sumLength = 0f
if (points[0] is MogoLocation) {
//计算全路径总距离
var i = 0
while (i + 1 < points.size) {
val locationPre = points[i] as MogoLocation
val location = points[i + 1] as MogoLocation
val preLat = locationPre.latitude
val preLon = locationPre.longitude
val laLat = location.latitude
val laLon = location.longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
} else if (points[0] is Location) {
//计算全路径总距离
var i = 0
while (i + 1 < points.size) {
val locationPre = points[i] as Location
val location = points[i + 1] as Location
val preLat = locationPre.latitude
val preLon = locationPre.longitude
val laLat = location.latitude
val laLon = location.longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
} else if (points[0] is LatLng) {
var i = 0
while (i + 1 < points.size) {
val locationPre = points[i] as LatLng
val location = points[i + 1] as LatLng
val preLat = locationPre.latitude
val preLon = locationPre.longitude
val laLat = location.latitude
val laLon = location.longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
}
return sumLength
}
fun calculateRouteSumLength(
mRoutePoints: List<MogoLocation>?,
location: MogoLocation,
station: MogoLocation
): Float {
if (null == mRoutePoints || mRoutePoints.size == 0) return 0f
var lastSumLength = 0f
//当前位置距离轨迹中最近的点
val currentRouteIndex = getArrivedPointIndexNew(
0, mRoutePoints, location.longitude, location.latitude
)
// 距离当前位置轨迹中最近的轨迹点坐标
val currentPoint = mRoutePoints[currentRouteIndex]
// 当前位置距离最近的点的距离
val calculateCurrentdex = CoordinateUtils.calculateLineDistance(
location.longitude, location.latitude,
currentPoint.longitude, currentPoint.latitude
)
//要前往的站在轨迹中对应的点
val stationPointInRouteIndex = getArrivedPointIndexNew(
currentRouteIndex, mRoutePoints,
station.longitude,
station.latitude
)
// 距离站点最近的轨迹点
val stationPointInRoute = mRoutePoints[stationPointInRouteIndex]
// 站点距离轨迹中最近点的距离
val calculateLineDistance = CoordinateUtils.calculateLineDistance(
stationPointInRoute.longitude, stationPointInRoute.latitude,
station.longitude, station.latitude
)
if (currentRouteIndex < stationPointInRouteIndex) {
// subList 是[) 需要的是[]
val subList = mRoutePoints.subList(currentRouteIndex, stationPointInRouteIndex + 1)
// 轨迹点所有的距离
lastSumLength = calculateRouteSumLength(subList)
// region 站点坐标和 站点坐标对应轨迹点的坐标距离
// 需要加距离 和下一个轨迹点成钝角
if (stationPointInRouteIndex + 1 < mRoutePoints.size) {
val lastPointsNext = mRoutePoints[stationPointInRouteIndex + 1]
val degree = getDegree(
station.longitude, station.latitude,
stationPointInRoute.longitude, stationPointInRoute.latitude,
lastPointsNext.longitude, lastPointsNext.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength + calculateLineDistance
}
}
// 需要减距离 和上一个轨迹点成钝角
if (stationPointInRouteIndex - 1 >= 0) {
val lastPointsPre = mRoutePoints[stationPointInRouteIndex - 1]
val degree = getDegree(
station.longitude, station.latitude,
stationPointInRoute.longitude, stationPointInRoute.latitude,
lastPointsPre.longitude, lastPointsPre.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength - calculateLineDistance
}
}
// endregion
// region 当前位置和 对应轨迹点的坐标距离
// 需要加距离 和下一个轨迹点成钝角
if (currentRouteIndex + 1 < stationPointInRouteIndex) {
val currentPointsNext = mRoutePoints[currentRouteIndex + 1]
val degree = getDegree(
location.longitude, location.latitude,
currentPoint.longitude, currentPoint.latitude,
currentPointsNext.longitude, currentPointsNext.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength - calculateCurrentdex
}
}
// 需要减距离 和上一个轨迹点成钝角
if (currentRouteIndex - 1 >= 0) {
val lastPointsPre = mRoutePoints[currentRouteIndex - 1]
val degree = getDegree(
location.longitude, location.latitude,
currentPoint.longitude, currentPoint.latitude,
lastPointsPre.longitude, lastPointsPre.latitude
).toDouble()
if (degree > 90) {
lastSumLength = lastSumLength + calculateCurrentdex
}
}
// endregion
} else {
val lastPoints = mRoutePoints[stationPointInRouteIndex]
lastSumLength = CoordinateUtils.calculateLineDistance(
lastPoints.longitude, lastPoints.latitude,
location.longitude, location.latitude
)
}
return lastSumLength
}
@JvmStatic
fun coordinateConverterWgsToGcjListCommon(
mContext: Context?,
models: List<MessagePad.Location>
): List<LatLng> {
//转成MogoLatLng集合
val list: MutableList<LatLng> = ArrayList()
for (m in models) {
val mogoLatLng = coordinateConverterWgsToGcj(mContext, m)
list.add(mogoLatLng)
}
return list
}
@JvmStatic
fun coordinateConverterWgsToGcj(
mContext: Context?,
mogoLatLng: MessagePad.Location
): LatLng {
val mCoordinateConverter =
CoordinateConverter(mContext)
mCoordinateConverter.from(CoordinateConverter.CoordType.GPS)
mCoordinateConverter.coord(
LatLng(
mogoLatLng.latitude,
mogoLatLng.longitude
)
)
return mCoordinateConverter.convert()
}
@JvmStatic
fun coordinateConverterWgsToGcj(
mContext: Context?,
lon: Double,
lat: Double
): LatLng {
val mCoordinateConverter =
CoordinateConverter(mContext)
mCoordinateConverter.from(CoordinateConverter.CoordType.GPS)
mCoordinateConverter.coord(LatLng(lat, lon))
return mCoordinateConverter.convert()
}
/**
* 简单粗暴 直接比较 todo 需要优化
* @param mRoutePoints
* @param realLon
* @param realLat
* @return
*/
fun getRemainPointListByCompare(
mRoutePoints: List<LatLng>,
realLon: Double,
realLat: Double
): List<LatLng> {
val latePoints: MutableList<LatLng> = ArrayList()
var currentIndex = 0 //记录疑似点
if (mRoutePoints.size > 0) {
//基础点
val baseLatLng = mRoutePoints[0]
if (baseLatLng != null) {
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLon, realLat, baseLatLng.longitude, baseLatLng.latitude
) // lon,lat, prelon, prelat
for (i in 1 until mRoutePoints.size) {
val latLng = mRoutePoints[i]
val diff = CoordinateUtils.calculateLineDistance(
realLon, realLat, latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff) {
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+i+"-------先记录点----- ");
baseDiffDis = diff
currentIndex = i
}
}
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+currentIndex+"-------是最近的点------ ");
if (currentIndex == mRoutePoints.size - 1) {
latePoints.add(mRoutePoints[currentIndex])
} else if (currentIndex < mRoutePoints.size - 1) {
latePoints.addAll(mRoutePoints.subList(currentIndex, mRoutePoints.size))
}
return latePoints
}
}
return latePoints
}
/**
* 简单粗暴 直接比较 todo 需要优化
* @param mRoutePoints
* @param realLon
* @param realLat
* @return 返回已经到达点的index
*/
fun getArrivedPointIndex(mRoutePoints: List<LatLng>, realLon: Double, realLat: Double): Int {
var currentIndex = 0 //记录疑似点
if (mRoutePoints.size > 0) {
//基础点
val baseLatLng = mRoutePoints[0]
if (baseLatLng != null) {
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLon, realLat, baseLatLng.longitude, baseLatLng.latitude
) // lon,lat, prelon, prelat
for (i in 1 until mRoutePoints.size) {
val latLng = mRoutePoints[i]
val diff = CoordinateUtils.calculateLineDistance(
realLon, realLat, latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff) {
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+i+"-------先记录点----- ");
baseDiffDis = diff
currentIndex = i
}
}
return currentIndex
}
}
return currentIndex
}
fun calculateRouteSumLengthByLocation(points: List<Location>?): Float {
if (points.isNullOrEmpty()) return 0f
var sumLength = 0f
//计算全路径总距离
var i = 0
while (i + 1 < points.size) {
val preLat = points[i].latitude
val preLon = points[i].longitude
val laLat = points[i + 1].latitude
val laLon = points[i + 1].longitude
val length = CoordinateUtils.calculateLineDistance(laLon, laLat, preLon, preLat)
sumLength += length
i++
}
return sumLength
}
@JvmStatic
fun coordinateConverterWgsToGcjLocations(
mContext: Context?,
models: List<MessagePad.Location>
): MutableList<MogoLocation> {
//转成MogoLatLng集合
val list = mutableListOf<MogoLocation>()
for (m in models) {
val mogoLatLng = coordinateConverterWgsToGcj(mContext, m)
val location = MogoLocation()
location.heading = m.heading.toFloat().toDouble()
location.latitude = mogoLatLng.latitude
location.longitude = mogoLatLng.longitude
list.add(location)
}
return list
}
@JvmStatic
fun coordinateConverterLatlngToLocation(models: List<LatLng>): List<MogoLocation> {
//转成MogoLatLng集合
val list: MutableList<MogoLocation> = ArrayList()
for (m in models) {
val location = MogoLocation()
location.latitude = m.latitude
location.longitude = m.longitude
list.add(location)
}
return list
}
@JvmStatic
fun coordinateConverterLocationToLatLng(
mContext: Context?,
models: List<MogoLocation>
): List<LatLng> {
//转成MogoLatLng集合
val list: MutableList<LatLng> = ArrayList()
for (m in models) {
val mogoLatLng = LatLng(m.latitude, m.longitude)
list.add(mogoLatLng)
}
return list
}
/**
* 根据前一个index经纬度航向角确认剩余轨迹
* @param preIndex
* @param mRoutePoints
* @param realLocation
* @return
*/
@JvmStatic
fun getRemainPointListByCompareNew(
preIndex: Int,
mRoutePoints: List<MogoLocation>,
realLocation: MogoLocation
): Map<Int, List<MogoLocation>> {
val routePonits: MutableMap<Int, List<MogoLocation>> = HashMap()
val latePoints: MutableList<MogoLocation> = ArrayList() // 剩余轨迹集合
var currentIndex = 0 //记录疑似点
if (mRoutePoints.size > 0) {
//基础点
val baseLatLng = mRoutePoints[0]
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude, baseLatLng.longitude, baseLatLng.longitude
) // lon,lat, prelon, prelat
for (i in mRoutePoints.indices) {
val latLng = mRoutePoints[i]
//todo 先看index对应点的方向和realLocation方向是否一致 方向角度不能过90度
if (realLocation.heading == realLocation.heading - latLng.heading ||
Math.abs(realLocation.heading - latLng.heading) <= 90
) {
val diff = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude,
latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff) {
baseDiffDis = diff
currentIndex = i
}
}
}
Logger.d("calculateRouteSumLength", "点:$currentIndex-------是最近的点------ ")
if (currentIndex == mRoutePoints.size - 1) {
val location = mRoutePoints[currentIndex]
// LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
latePoints.add(location)
} else {
val locations = mRoutePoints.subList(currentIndex, mRoutePoints.size)
for (location in locations) {
// LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
latePoints.add(location)
}
}
routePonits[currentIndex] = latePoints
return routePonits
}
return routePonits
}
@JvmStatic
fun getArrivedPointIndexNew(
preIndex: Int, mRoutePoints: List<MogoLocation>,
realLocation: MogoLocation
): Int {
var currentIndex = 0 //记录疑似点 //基础点
val baseLatLng = mRoutePoints[0]
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude, baseLatLng.longitude, baseLatLng.longitude
) // lon,lat, prelon, prelat
for (i in mRoutePoints.indices) {
val latLng = mRoutePoints[i]
if (realLocation.heading == realLocation.heading - latLng.heading ||
Math.abs(realLocation.heading - latLng.heading) <= 90
) {
val diff = CoordinateUtils.calculateLineDistance(
realLocation.longitude,
realLocation.latitude,
latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff && i > currentIndex) {
baseDiffDis = diff
currentIndex = i
}
}
}
Logger.d("calculateRouteSumLength", "点:$currentIndex-------是最近的点------ ")
return currentIndex
}
@JvmStatic
fun getArrivedPointIndexNew(
preIndex: Int, mRoutePoints: List<MogoLocation>,
realLon: Double, realLat: Double
): Int {
var currentIndex = preIndex //记录疑似点 //基础点
val baseLatLng = mRoutePoints[0]
var baseDiffDis = CoordinateUtils.calculateLineDistance(
realLon,
realLat, baseLatLng.longitude, baseLatLng.longitude
) // lon,lat, prelon, prelat
for (i in preIndex until mRoutePoints.size) {
val latLng = mRoutePoints[i]
// if (realLocation.getBearing() == realLocation.getBearing() - latLng.getBearing() ||
// Math.abs(realLocation.getBearing() - latLng.getBearing()) <= 90){
val diff = CoordinateUtils.calculateLineDistance(
realLon,
realLat,
latLng.longitude, latLng.latitude
)
if (baseDiffDis > diff && i > currentIndex) {
// Logger.d(M_TAXI + "calculateRouteSumLength", "点:"+i+"-------先记录点----- ");
baseDiffDis = diff
currentIndex = i
}
// }
}
Logger.d("calculateRouteSumLength", "点:$currentIndex-------是最近的点------ ")
return currentIndex
}
/**
* https://blog.csdn.net/Jeanne_0523/article/details/106056255
* @param vertexPointX
* @param vertexPointY
* @param point0X 角
* @param point0Y 角
* @param point1X
* @param point1Y
* @return
*/
fun getDegree(
vertexPointX: Double,
vertexPointY: Double,
point0X: Double,
point0Y: Double,
point1X: Double,
point1Y: Double
): Int {
//向量的点乘
val vector =
(point0X - vertexPointX) * (point1X - vertexPointX) + (point0Y - vertexPointY) * (point1Y - vertexPointY)
//向量的模乘
val sqrt = Math.sqrt(
(Math.abs((point0X - vertexPointX) * (point0X - vertexPointX)) + Math.abs((point0Y - vertexPointY) * (point0Y - vertexPointY)))
* (Math.abs((point1X - vertexPointX) * (point1X - vertexPointX)) + Math.abs((point1Y - vertexPointY) * (point1Y - vertexPointY)))
)
//反余弦计算弧度
val radian = Math.acos(vector / sqrt)
//弧度转角度制
return (180 * radian / Math.PI).toInt()
}
private fun ball2xyz(thera: Double, fie: Double, r: Double): Triple<Double, Double, Double> {
val x = r * cos(thera) * cos(fie)
val y = r * cos(thera) * sin(fie)
val z = r * sin(thera)
return Triple(x, y, z)
}
/**
* https://blog.csdn.net/reborn_lee/article/details/82497577
* 将地理经纬度转换成笛卡尔坐标系
*/
private fun geo2xyz(lat: Double, lng: Double): Triple<Double, Double, Double> {
val thera = Math.PI * lat / 180
val fie = Math.PI * lng / 180
return ball2xyz(thera, fie, 6400.0)
}
/**
* 计算3个地理坐标点之间的夹角
* @param l1 顶点坐标
* @param l2
* @param l3
* @return l1为顶点的角度 精度没有angleOflocation高
*/
fun getDegree(l2: MogoLocation, l1: MogoLocation, l3: MogoLocation): Double {
val (x1, y1, z1) = geo2xyz(l1.latitude, l1.longitude)
val (x2, y2, z2) = geo2xyz(l2.latitude, l2.longitude)
val (x3, y3, z3) = geo2xyz(l3.latitude, l3.longitude)
// 计算向量 P2P1 和 P2P3 的夹角 https://www.zybang.com/question/3379a30c0dd3041b3ef966803f0bf758.html
val p1P2 = sqrt((x2 - x1).pow(2.0) + (y2 - y1).pow(2.0) + (z2 - z1).pow(2.0))
val p2p3 = sqrt((x3 - x2).pow(2.0) + (y3 - y2).pow(2.0) + (z3 - z2).pow(2.0))
val p = (x1 - x2) * (x3 - x2) + (y1 - y2) * (y3 - y2) + (z1 - z2) * (z3 - z2) //P2P1*P2P3
return acos(p / (p1P2 * p2p3)) / Math.PI * 180
}
fun getHeadingAngle(location: MogoLocation, nextPoint: MogoLocation): Double {
return getHeadingAngle(
location.longitude,
location.latitude,
nextPoint.longitude,
nextPoint.latitude
)
}
fun getHeadingAngle(location: LatLng, nextPoint: LatLng): Double {
return getHeadingAngle(
location.longitude,
location.latitude,
nextPoint.longitude,
nextPoint.latitude
)
}
fun getHeadingAngle(locationLongitude: Double, locationLatitude: Double,
nextPointLongitude: Double, nextPointLatitude: Double): Double {
val y = sin(nextPointLongitude - locationLongitude) * cos(nextPointLatitude)
val x = cos(locationLatitude) * sin(nextPointLatitude) - sin(locationLatitude) *
cos(nextPointLatitude) * cos(nextPointLongitude - locationLongitude)
var bearing = atan2(y, x)
bearing = Math.toDegrees(bearing)
if (bearing < 0) {
bearing += 360.0
}
return 360-bearing
}
fun getHeadingAngleTemp(locationLongitude: Double, locationLatitude: Double,
nextPointLongitude: Double, nextPointLatitude: Double): Double {
val lat1Rad = Math.toRadians(locationLatitude)
val lat2Rad = Math.toRadians(nextPointLatitude)
val lon1Rad = Math.toRadians(locationLongitude)
val lon2Rad = Math.toRadians(nextPointLongitude)
val y = sin(lon2Rad - lon1Rad) * cos(lat2Rad)
val x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(lon2Rad - lon1Rad)
val bearing = Math.toDegrees(atan2(y, x))
return (bearing + 360) % 360
}
/**
* 支持带航行角的和不带航向角的
* 带航向角的会删除 航向角差大于90°的点
* @param preIndex 上次计算缓存
* @param mRoutePoints 轨迹点
* @param location 目标坐标
* @param type 1 开始站点 2 定位点 3 结束站点
* @return Triple<Int,Boolean?,Float>
* 距离目标坐标最近的轨迹点下标、
* 最近点坐标是目标坐标的上一个点还是下一个点
* 目标到最近轨迹点的距离
*/
@JvmStatic
fun getNearestPointInfo(
preIndex: Int,
endIndex: Int,
mRoutePoints: List<MogoLocation>,
location: MogoLocation,
type:Int,
size:Int = 4,
useHeading:Boolean = true,
): Triple<Int,Boolean?,Float> {
val startTime = System.currentTimeMillis()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"参数:[$preIndex $endIndex) mRoutePoints:${mRoutePoints.size} type:$type size:$size" +
" location:(${location.latitude},${location.longitude},${location.heading})")
var currentIndex:Int = preIndex //记录疑似点 //基础点
// 轨迹中的点和定位点的距离集合
val distanceMap: TreeMap<DistanceDegree, Int> = TreeMap()
for (index in preIndex until endIndex) {
val latLngIndex = mRoutePoints[index]
val distance = CoordinateUtils.calculateLineDistance(
location.longitude,
location.latitude,
latLngIndex.longitude,
latLngIndex.latitude
)
distanceMap[DistanceDegree.obtain(distance, null,null)] = index
if (distanceMap.size > size) {
distanceMap.pollLastEntry()?.key?.recycle()
}
}
distanceMap.forEach {
val distanceDegree = it.key
val pointIndex = it.value
val currentPoint = mRoutePoints[pointIndex]// 疑似最近的点
var nextDegree = 0.0
var preDegree = 0.0
if (pointIndex + 1 < mRoutePoints.size) {
val nextPoint = mRoutePoints[pointIndex + 1]
nextDegree = getDegree(location, currentPoint, nextPoint)
}else{
nextDegree = 135.0
}
// 需要减距离 和上一个轨迹点成钝角
if (pointIndex - 1 >= 0) {
val prePoint = mRoutePoints[pointIndex - 1]
preDegree = getDegree(
location,
currentPoint,
prePoint
)
}else{// 第一个个点处理
preDegree = 135.0
}
fun getDegreeNext(){
if (pointIndex + 1 < mRoutePoints.size) {
val nextPoint = mRoutePoints[pointIndex + 1]
val headingAngle = getHeadingAngle(currentPoint, nextPoint)
distanceDegree.degree = headingAngle
distanceDegree.isNext = false
}else{
val prePoint = mRoutePoints[pointIndex - 1]
val headingAngle = getHeadingAngle(prePoint,currentPoint)
distanceDegree.degree = headingAngle
distanceDegree.isNext = true
}
}
fun getDegreePre(){
if (pointIndex - 1 >= 0) {
val prePoint = mRoutePoints[pointIndex - 1]
val headingAngle = getHeadingAngle(prePoint, currentPoint)
distanceDegree.degree = headingAngle;
distanceDegree.isNext = true
}else{
val nextPoint = mRoutePoints[pointIndex + 1]
val headingAngle = getHeadingAngle(currentPoint,nextPoint)
distanceDegree.degree = headingAngle
distanceDegree.isNext = false
}
}
if(nextDegree>90){
getDegreeNext()
}
if(preDegree>90){
getDegreePre()
}
if(preDegree<90&&nextDegree<90&&preDegree+nextDegree>90){
if (pointIndex + 1 < mRoutePoints.size&&pointIndex - 1 >= 0) {
val nextPoint = mRoutePoints[pointIndex + 1]
val prePoint = mRoutePoints[pointIndex - 1]
val degree = getDegree(currentPoint, prePoint, nextPoint)
if(degree>90) {
getDegreePre()
// isNext 值无所谓了 就 ture和false都行
// 通过航向角过滤一遍
if(distanceDegree.degree!=null&&DrivingDirectionUtils.getAngleDiff(location.heading, distanceDegree.degree!!)<90){
currentIndex = pointIndex
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
return Triple(currentIndex,distanceDegree.isNext,distanceDegree.distance)
}
}
}
}
}
// 根据角度来排除一些点
val iterator = distanceMap.iterator()
// 有航向角比较航向角
while (iterator.hasNext()) {
val next = iterator.next()
val key = next.key
if (key.degree == null) {
key.recycle()
iterator.remove()
} else {
if (location.heading != 0.0 && useHeading) {
key.degree?.let {
val dexAngle = DrivingDirectionUtils.getAngleDiff(location.heading, it)
if (dexAngle > 90) {
key.recycle()
iterator.remove()
}
}
}
}
}
if(distanceMap.size==0&&size<16){
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return getNearestPointInfo(preIndex,endIndex,mRoutePoints,location,type,size+2)
}
// 最近点中包含上次计算的点和上次计算的最近的一个点
if(distanceMap.containsValue(preIndex)&&distanceMap.containsValue(preIndex+1)&&type==1){
var preIndexDistance: com.mogo.och.bridge.distance.DistanceDegree?=null
var preIndexNextDistance: com.mogo.och.bridge.distance.DistanceDegree?=null
distanceMap.iterator().forEach { en ->
val key = en.key
val value = en.value
if(value==preIndex){
preIndexDistance = key
}else if(value==preIndex+1){
preIndexNextDistance = key
}
}
if(preIndexDistance!=null&&preIndexNextDistance!=null){
if(preIndexDistance!!.distance<preIndexNextDistance!!.distance){
currentIndex = preIndex
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,preIndexDistance?.isNext,preIndexDistance!!.distance)
}else{
currentIndex = preIndex+1
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,preIndexNextDistance?.isNext,preIndexNextDistance!!.distance)
}
}
}
var maxIndex = 0
var minIndex = Int.MAX_VALUE
distanceMap.iterator().forEach { en ->
val value = en.value
if(value<minIndex){
minIndex = value
}
if(value>maxIndex){
maxIndex = value
}
}
val middleVale =minIndex + (maxIndex-minIndex)/2
when (type) {
1 -> {// 求开始站点
val iteratorRemove = distanceMap.iterator()
while (iteratorRemove.hasNext()) {
val next = iteratorRemove.next()
val key = next.key
if(next.value>middleVale){
key.recycle()
iteratorRemove.remove()
}
}
}
3 -> {// 求结束站点
val iteratorRemove = distanceMap.iterator()
while (iteratorRemove.hasNext()) {
val next = iteratorRemove.next()
val key = next.key
if(next.value<middleVale){
key.recycle()
iteratorRemove.remove()
}
}
}
else -> {}
}
// 根据距离来计算 最近的点 只有一个前面的点
var tempDistance = Float.MAX_VALUE
var isNext:Boolean? = null
distanceMap.iterator().forEach { en ->
val key = en.key
val value = en.value
// 排除没有第一个值0是
if(value==preIndex+1&&preIndex!=0&&type==1){
currentIndex = value
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,key.isNext,key.distance)
}
key.distance.let {
if (it < tempDistance) {
tempDistance = it
currentIndex = value
isNext = key.isNext
}
}
}
val iterator1 = distanceMap.iterator()
while (iterator1.hasNext()) {
iterator1.next().key.recycle()
iterator1.remove()
}
distanceMap.clear()
Logger.d(SceneConstant.M_OCHCOMMON + "calculateRouteSumLength",
"计算时间:${System.currentTimeMillis()-startTime}")
return Triple(currentIndex,isNext,tempDistance)
}
}

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_282"
android:layout_height="@dimen/dp_282"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/aciv_autopilot_bg"
android:src="@drawable/common_autopilot_bg"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="@dimen/dp_282"
android:layout_height="@dimen/dp_282" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/aciv_autopilot_state"
android:src="@drawable/common_autopilot_enable"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/gl_horizontal_top"
app:layout_constraintBottom_toBottomOf="@+id/gl_horizontal_bottom"
android:layout_width="@dimen/dp_180"
android:layout_height="@dimen/dp_180" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl_horizontal_top"
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/dp_37"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/gl_horizontal_bottom"
android:orientation="horizontal"
app:layout_constraintGuide_begin="@dimen/dp_245"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_autopilot_head"
android:textColor="@color/common_B3FFFFFF"
android:textSize="@dimen/dp_32"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintTop_toBottomOf="@+id/gl_horizontal_top"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@+id/actv_autopilot_state"
android:text="AD"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_autopilot_state"
android:textColor="@color/common_B3FFFFFF"
android:textSize="@dimen/dp_32"
app:layout_constraintTop_toBottomOf="@+id/actv_autopilot_head"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@+id/gl_horizontal_bottom"
android:text="START"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_pxjs_state"
android:textColor="@color/common_19FFCB"
android:visibility="gone"
android:textSize="@dimen/dp_32"
app:layout_constraintTop_toBottomOf="@+id/gl_horizontal_top"
app:layout_constraintBottom_toTopOf="@+id/gl_horizontal_bottom"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="平行驾驶"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/aciv_autopilot_running_ani"
android:src="@drawable/start_auto_001"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_width="@dimen/dp_280"
android:layout_height="@dimen/dp_280" />
</merge>

View File

@@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="@dimen/dp_939"
android:layout_height="wrap_content"
android:background="@drawable/common_error_vin">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_error_head"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_60"
android:layout_marginStart="@dimen/dp_60"
android:layout_marginEnd="@dimen/dp_60"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="@dimen/dp_46"
app:layout_constraintBottom_toTopOf="@+id/actv_error_body"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="VIN码不匹配" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_error_body"
android:layout_width="match_parent"
android:layout_marginStart="@dimen/dp_60"
android:layout_marginEnd="@dimen/dp_60"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_41"
android:layout_marginBottom="@dimen/dp_39"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="@dimen/dp_40"
app:layout_constraintBottom_toTopOf="@+id/actv_error_body_red"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/actv_error_head"
android:text="请暂停运营并上报问题,待问题解决后用车" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_error_body_red"
android:layout_width="match_parent"
android:layout_marginStart="@dimen/dp_60"
android:layout_marginEnd="@dimen/dp_60"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@android:color/holo_red_dark"
android:textSize="@dimen/dp_40"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/actv_error_body"
android:text="【仍继续用车有安全风险!!!】" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/actv_see"
android:layout_width="@dimen/dp_350"
android:layout_height="@dimen/dp_130"
android:layout_marginTop="40dp"
android:layout_marginBottom="@dimen/dp_80"
android:background="@drawable/common_error_vin_submit"
android:gravity="center"
android:text="我知道了"
android:textColor="@android:color/white"
android:textSize="@dimen/dp_46"
app:layout_constraintTop_toBottomOf="@+id/actv_error_body_red"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:background="@color/acc_default_txt_color"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/iv_toolkit_item_head"
android:layout_width="@dimen/dp_160"
android:layout_height="@dimen/dp_160"
android:scaleType="fitXY"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:src="@drawable/common_map_line_close"
/>
<TextView
android:id="@+id/iv_toolkit_item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_1"
android:ellipsize="end"
android:gravity="center"
android:maxLines="2"
android:text="车道指引"
android:textColor="@color/color_FFFFFF"
android:textSize="@dimen/sp_38"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_toolkit_item_head"/>
</merge>