[sweeper运营&云控合并]refactor: module name Sweeper->Driver, 删除gradle中与SweeperCloud有关的部分;

This commit is contained in:
aibingbing
2023-10-18 11:09:33 +08:00
parent 3da4860af5
commit de55548d2f
266 changed files with 4 additions and 53 deletions

View File

@@ -0,0 +1,24 @@
package com.mogo.och.sweeper
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.mogo.och.sweeper", appContext.packageName)
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mogo.och.sweeper">
<application>
<!--这里是为了测试增加的广播-->
<receiver android:name=".common.receiver.TestSweeperBroadcastReceiver">
<intent-filter>
<action android:name="com.bus.test_control" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
</manifest>

Binary file not shown.

View File

@@ -0,0 +1,24 @@
package com.mogo.och.sweeper;
import androidx.annotation.IdRes;
import androidx.fragment.app.FragmentActivity;
import com.mogo.eagle.core.function.api.base.IMoGoFunctionProvider;
public
/**
* @author congtaowang
* @since 2021/1/15
*
* 网约车抽象接口
*/
interface IMogoOCH extends IMoGoFunctionProvider {
/**
* 初始化网约车容器
*
* @param activity
* @param containerId 容器ID
*/
void createCoverage(FragmentActivity activity, @IdRes int containerId);
}

View File

@@ -0,0 +1,87 @@
package com.mogo.och.sweeper;
import android.content.Context;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import com.mogo.och.sweeper.operate.fragment.SweeperFragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* 清扫车业务实现入口
*
* @author tongchenfei
*/
@Route(path = SweeperConst.PATH)
public class SweeperProvider implements IMogoOCH {
private static final String TAG = "SweeperProvider";
private SweeperFragment sweeperFragment;
private int containerId;
private FragmentActivity activity;
@Override
public void init(Context context) {
}
private void showFragment() {
FragmentManager supportFragmentManager = activity.getSupportFragmentManager();
if (sweeperFragment == null) {
CallerLogger.d(TAG, "准备add fragment======");
Fragment fragmentByTag = supportFragmentManager.findFragmentByTag(sweeperFragment.TAG);
if (fragmentByTag instanceof SweeperFragment) {
sweeperFragment = (SweeperFragment) fragmentByTag;
} else {
sweeperFragment = new SweeperFragment();
}
if(!sweeperFragment.isAdded()) {
supportFragmentManager.beginTransaction().add(containerId, sweeperFragment, sweeperFragment.TAG).commitAllowingStateLoss();
}
return;
}
CallerLogger.d(TAG, "准备show fragment");
supportFragmentManager.beginTransaction().show(sweeperFragment).commitAllowingStateLoss();
}
private void hideFragment() {
if (sweeperFragment != null) {
CallerLogger.d(TAG, "准备hide fragment");
activity.getSupportFragmentManager().beginTransaction().hide(sweeperFragment).commitAllowingStateLoss();
}
}
@Override
public void createCoverage(FragmentActivity activity, int containerId) {
}
@NotNull
@Override
public String getFunctionName() {
return null;
}
@Nullable
@Override
public Fragment createCoverage(@Nullable FragmentActivity fragmentActivity, @Nullable Integer integer) {
this.containerId = integer;
this.activity = fragmentActivity;
showFragment();
return null;
}
@Override
public void onDestroy() {
//若不调用finish, 设置中打开关闭UITouch,会造成och fragment 重叠
if (activity == null) return;
activity.finish();
}
}

View File

@@ -0,0 +1,73 @@
package com.mogo.och.sweeper.cloud.callback
import chassis.ChassisStatesOuterClass
import com.mogo.och.sweeper.common.database.bean.WeltDataBean
import com.zhjt.mogo.adas.data.AiCloudTask
import com.zhjt.mogo.adas.data.sweeper.bootable.SweeperBootable.IsBootableResp
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon.Code
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask
import com.zhjt.mogo.adas.data.sweeper.task.cloud.s_r.SweeperTaskCloudSuspendResume.ActionType
import com.zhjt.mogo.adas.data.sweeper.task.stop.SweeperTaskStop.StopTaskType
/**
* @author: wangmingjun
* @date: 2021/10/22
*/
interface ISweeperCloudTaskCallback {
/**
* 接取云端任务/正在执行中任务信息回调
*/
fun onSweeperCloudTask(messageType: AiCloudTask.MessageType, taskInfo: SweeperTask.TaskInfo?)
/**
* 云端下发子任务请求pad确认
*/
fun onSweeperCloudTaskConfirm(taskId:String,subTaskId:String,isPop:Boolean)
/**
* 云端下发子任务状态同步
*/
fun onSweeperCloudTaskStatus(taskId:String,subTaskId:String,subTaskStatus: SweeperCommon.TaskStatus)
/**
* 云端下发结束大任务指令
*/
fun onSweeperCloudTaskStop(taskId:String,stopTaskType: StopTaskType,isPop:Boolean)
/**
* 挂起/恢复
*/
fun onSweeperCloudTaskSuspendResume(messageType: AiCloudTask.MessageType,taskId:String,subTaskId:String,code:Code)
/**
* pad请求云端进入自驾
*/
fun onSweeperCloudBootable(taskId:String,subTaskId:String,isBootableResp: IsBootableResp)
/**
* 云端同步大任务状态
*/
fun onSweeperCloudBigTaskStatus(taskId:String, bigTaskStatus: SweeperCommon.TaskStatus, isPop:Boolean)
/**
* 云端同步大任务暂停 / 恢复 指令
*/
fun onSweeperCloudBigTaskSuspendResume(taskId:String, actionType: ActionType, isPop:Boolean)
/**
* 设置轨迹坐标点集合
*/
fun setRouteList(routeList: java.util.ArrayList<com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean.Result>)
/**
* 贴边数据回传
*/
fun setWeltDataToMap(weltDataBeans: ArrayList<WeltDataBean?>, isWeltData: Boolean, distance: String)
/**
* 底盘清扫模式数据回传
*/
fun onSweeperFutianCleanSystemState(cleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates)
}

View File

@@ -0,0 +1,17 @@
package com.mogo.och.sweeper.cloud.callback;
import com.mogo.eagle.core.data.map.MogoLocation;
/**
* Created on 2021/9/10
*
* Model->Presenter回调状态控制器监听accOn、adas ui show、voice ui show、push ui show、v2x ui show等等
*/
public interface ISweeperControllerStatusCallback {
// 是否vr map模式
void onVRModeChanged(boolean isVRMode);
// 自车定位
void onCarLocationChanged(MogoLocation location);
//自动驾驶状态
void onAutopilotState(int state);
}

View File

@@ -0,0 +1,8 @@
package com.mogo.och.sweeper.cloud.callback
interface SweeperCloudDialogClickListener {
fun onConfirm()
fun onRefuseOrEnd()
fun onCountDownStop()
fun onNext()
}

View File

@@ -0,0 +1,623 @@
package com.mogo.och.sweeper.cloud.fragment;
import static com.mogo.map.MogoMap.DEFAULT;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.FragmentTransaction;
import com.amap.api.maps.model.LatLng;
import com.google.protobuf.TextFormat;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.mvp.IView;
import com.mogo.commons.mvp.MvpFragment;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotRecordListenerManager;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxBubbleView;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxButtonView;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxListView;
import com.mogo.eagle.core.function.view.MapBizView;
import com.mogo.eagle.core.utilcode.mogo.view.OnPreventFastClickListener;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.map.listener.IMogoMapListener;
import com.mogo.map.listener.MogoMapListenerHandler;
import com.mogo.map.uicontroller.IMogoMapUIController;
import com.mogo.map.uicontroller.VisualAngleMode;
import com.mogo.och.common.module.manager.autopilotmanager.OCHAdasAbilityManager;
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil;
import com.mogo.och.sweeper.R;
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean;
import com.mogo.och.sweeper.common.callback.ISweeperTaskDataToFragmentCallback;
import com.mogo.och.sweeper.operate.callback.IWeltMapSwitchToSmallCallback;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import com.mogo.och.sweeper.cloud.util.AutopilotModeConfigManager;
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils;
import com.mogo.och.sweeper.cloud.view.SweeperTrafficDataView;
import com.mogo.och.sweeper.common.view.WeltSmallMapView;
import com.zhjt.mogo.adas.data.AiCloudTask;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import mogo.telematics.pad.MessagePad;
/**
* 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况
* <p>
* 部分业务放在了此处处理
*
* @author tongchenfei
*/
public abstract class BaseSweeperTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener,
IWeltMapSwitchToSmallCallback, View.OnTouchListener {
private static final String TAG = "BaseSweeperTabFragment";
//地图放大缩小
private ImageView mSwitchMapModeImage;
//设置信息面板
protected ImageView mSettingBtn;
//安全员问题上报面板
protected ImageView mCardBtn;
//道路状况上报面板
protected ImageView mAICollectBtn;
//任务列表面板
private FrameLayout flTaskListPanelContainer;
private MapBizView mapBizView;
private Group groupTestPanel;
//清扫车车辆基本信息 速度 档位 转向灯 红绿灯等
private SweeperTrafficDataView mTrafficDataView;
//作业任务贴边数据展示图
private WeltSmallMapView mMapWeltView;
//任务作业全览图
private WeltMapOverViewFragment mWeltMapOverViewFragment;
private FrameLayout mFlWeltMapOverView;
private DriverMsgBoxButtonView mViewDriverMsgBoxButton;
private DriverMsgBoxListView mViewDriverMsgBoxList;
private DriverMsgBoxBubbleView mViewDriverMsgBoxBubble;
private ArrayList<WeltDataBean> mWeltDataBeanList;//存储贴边数据
private ArrayList<LatLng> mSubTaskCoordinates;//存储当前大任务的所有子任务起点和终点
// 当前子任务的终点坐标
protected LatLng mCurrentTaskEndStation;
private ArrayList<SweeperRoutePlanningUpdateReqBean.Result> mRouteList;//存储任务的坐标轨迹
private String mProgress;
private ISweeperTaskDataToFragmentCallback mTaskDataToFragmentCallback;
@Override
protected int getLayoutId() {
return R.layout.sweeper_cloud_base_fragment;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
@Override
protected void initViews() {
mapBizView = findViewById(R.id.mapBizView);
groupTestPanel = findViewById(R.id.groupTestPanel);
flTaskListPanelContainer = findViewById(R.id.module_mogo_och_task_list_container);
mTrafficDataView = findViewById(R.id.sweeper_arc);
LayoutInflater.from(getContext()).inflate(getTaskListPanelViewId(), flTaskListPanelContainer);
mSwitchMapModeImage = findViewById(R.id.sweeper_switch_model_icon);
mSettingBtn = findViewById(R.id.sweeper_setting_model_icon);
mCardBtn = findViewById(R.id.sweeper_card_model_icon);
mAICollectBtn = findViewById(R.id.sweeper_collect_model_icon);
mMapWeltView = findViewById(R.id.sweeper_task_welt_small_map);
mFlWeltMapOverView = findViewById(R.id.sweeper_welt_map_overview);
initListener();
//设置消息盒子
setMessageBox();
//设置左下角四个按钮监听事件
setBottomBtnListener();
// 模拟 下发启动自驾命令
findViewById(R.id.btnStartAutopilot).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!FunctionBuildConfig.isDemoMode && !OCHAdasAbilityManager.getInstance().getAutopilotAbilityStatus()) {
ToastUtils.showLong(OCHAdasAbilityManager.getInstance().getAutopilotUnAbilityReason() + ", 请稍候重试");
return;
}
new AutopilotModeConfigManager(new AutopilotModeConfigManager.OnReadAutopilotModeConfigListener() {
@Override
public void onReadFailed(String err) {
ThreadUtils.runOnUiThread(() -> {
ToastUtils.showLong("读取失败=" + err);
});
}
@Override
public void onParseFailed(String err) {
ToastUtils.showLong("解析失败=" + err);
}
@Override
public void onParse(MessagePad.SetAutopilotModeReq bean) {
ToastUtils.showLong("下发命令\n" + TextFormat.printer().escapingNonAscii(false).printToString(bean));
AutopilotControlParameters parameters = new AutopilotControlParameters();
MessagePad.RouteInfo routeInfo = bean.getRouteInfo();
if (routeInfo.getRouteID() > 0) {
parameters.routeID = routeInfo.getRouteID();
}
parameters.routeName = routeInfo.getRouteName();
parameters.startName = routeInfo.getStartName();//拼音
parameters.endName = routeInfo.getEndName();//拼音
parameters.startLatLon = new AutopilotControlParameters
.AutoPilotLonLat(routeInfo.getStartLocation().getLatitude(), routeInfo.getStartLocation().getLongitude());
parameters.endLatLon = new AutopilotControlParameters
.AutoPilotLonLat(routeInfo.getEndLocation().getLatitude(), routeInfo.getEndLocation().getLongitude());
parameters.vehicleType = 10;
MessagePad.Line line = routeInfo.getLine();
parameters.autoPilotLine = new AutopilotControlParameters.AutoPilotLine(
line.getLineId(), line.getLineName(),
line.getTrajUrl(), line.getTrajMd5(),
line.getStopUrl(), line.getStopMd5(),
line.getTimestamp(), line.getVehicleModel(),
line.getTrajUrlDpqp(), line.getTrajMd5Dpqp(),
line.getStopUrlDpqp(), line.getStopMd5Dpqp(),
line.getTimestampDpqp());
CallerAutoPilotControlManager.INSTANCE.startAutoPilot(parameters);
}
}).read();
}
});
// 模拟 查询当前任务
findViewById(R.id.btnQueryCurrentTask).setOnClickListener(view ->
SweeperCloudTaskUtils.mockQueryCurrentTaskInfo()
);
// 模拟 云端发送任务
findViewById(R.id.btnSendTask).setOnClickListener(view -> {
SweeperCloudTaskUtils.mockSendCloudTaskInfo(AiCloudTask.MessageType.CloudPushTask);
UiThreadHandler.postDelayed(() -> {
debugEndSubTask();
}, 5000);
}
);
// 模拟 云端发送子任务确认
findViewById(R.id.btnSendSubTaskConfirm).setOnClickListener(view ->
SweeperCloudTaskUtils.mockSendCloudSubTaskConfirm()
);
// 模拟 云端请求结束大任务
findViewById(R.id.btnSendEndTask).setOnClickListener(view ->
SweeperCloudTaskUtils.mockSendCloudBigTaskEnd()
);
// 模拟 云端发送子任务状态
findViewById(R.id.btnSendSubtaskStatus).setOnClickListener(view ->
SweeperCloudTaskUtils.mockSendCloudUpdateSubTaskStatus()
);
// 模拟 云端发送大任务状态
findViewById(R.id.btnSendTaskStatus).setOnClickListener(view ->
SweeperCloudTaskUtils.mockSendCloudBigTaskStatus()
);
// 模拟 添加高精地图Marker
findViewById(R.id.btnAddHDMarker).setOnClickListener(view -> {
// 坐标地址:湖南省衡阳市雁峰区金龙坪街道科学城大道衡山科学城停车场
SweeperCloudTaskUtils.mockAddHdMarker("marker_sweeper_subtask_123_start",
112.57742887355035,
26.82068193910221,
true);
// 坐标地址:湖南省衡阳市雁峰区岳屏镇雁鸣路
SweeperCloudTaskUtils.mockAddHdMarker("marker_sweeper_subtask_123_end",
112.56666738544979,
26.822879976829867,
false);
}
);
// 模拟 移除高精地图Marker
findViewById(R.id.btnRemoveHDMarker).setOnClickListener(view ->
SweeperCloudTaskUtils.mockRemoveHDMarker()
);
mTrafficDataView.getSpeedImage().setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
debugTestBar();
return false;
}
});
}
private void updateSwitchMapIcon() {
IMogoMapUIController controller = CallerMapUIServiceManager.INSTANCE.getMapUIController(DEFAULT);
if (controller != null) {
if (controller.getCurrentMapVisualAngle().isLongSight()) {
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_big_selector);
} else if (controller.getCurrentMapVisualAngle().isMediumSight()) {
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_small_selector);
} else {
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_small_selector);
}
}
}
/**
* 设置任务进度
*
* @param progress
*/
protected void setTaskProgress(String progress) {
ThreadUtils.runOnUiThread(() -> {
mMapWeltView.setTaskProgress(progress);
mProgress = progress;
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setProgress(progress);
}
});
}
@Override
protected void initViews(Bundle savedInstanceState) {
super.initViews(savedInstanceState);
mapBizView.onCreate(savedInstanceState);
mMapWeltView.onCreateView(savedInstanceState);
}
public void setAutoState(int state) {
mTrafficDataView.setAutoState(state);
}
/**
* 消息盒子
*/
private void setMessageBox() {
mViewDriverMsgBoxButton = findViewById(R.id.viewDriverMsgBoxButton);
mViewDriverMsgBoxList = findViewById(R.id.viewDriverMsgBoxList);
mViewDriverMsgBoxBubble = findViewById(R.id.viewDriverMsgBoxBubble);
//消息盒子
mViewDriverMsgBoxButton.setClickListener(show -> {
if (show) {
mViewDriverMsgBoxList.setVisibility(View.VISIBLE);
mViewDriverMsgBoxList.notifyData();
mViewDriverMsgBoxBubble.setVisibility(View.GONE);
mViewDriverMsgBoxBubble.isShowData(false);
} else {
mViewDriverMsgBoxList.setVisibility(View.GONE);
mViewDriverMsgBoxBubble.setVisibility(View.VISIBLE);
mViewDriverMsgBoxBubble.isShowData(true);
}
});
}
@Override
public void onPause() {
super.onPause();
mMapWeltView.onPause();
}
@Override
public void onResume() {
super.onResume();
mapBizView.onResume();
mMapWeltView.onResume();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mapBizView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapBizView.onLowMemory();
}
@Override
public void onDestroyView() {
mapBizView.onDestroy();
super.onDestroyView();
CallerAutopilotRecordListenerManager.INSTANCE.removeListener(TAG);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setOnTouchListener(this);
}
private void initListener() {
MogoMapListenerHandler.Companion.getMogoMapListenerHandler().registerHostMapListener(TAG, this);
mMapWeltView.getSwitchToBig().setOnClickListener((v) -> {
showOrHideOverMapViewFragment(true);
});
}
/**
* 清扫车任务列表面板view在{@link #initViews()}时候添加到container中
*
* @return 站点面板view
*/
public abstract int getTaskListPanelViewId();
/**
* 迈速表实时更新
*
* @param newSpeed
*/
public void updateSpeedView(float newSpeed) {
int speed = (int) (Math.abs(newSpeed) * 3.6F); // 倒车时工控机反馈定位信息中speed为负值
if (mTrafficDataView != null) {
mTrafficDataView.updateSpeedWithValue(speed);
}
}
/**
* 车辆基本信息View
*
* @return
*/
public SweeperTrafficDataView getTrafficDataView() {
return mTrafficDataView;
}
@Override
public void onDestroy() {
super.onDestroy();
mMapWeltView.onDestroy();
MogoMapListenerHandler.Companion.getMogoMapListenerHandler().unregisterHostMapListener(TAG);
}
@Override
public void onMapVisualAngleChanged(VisualAngleMode visualAngleMode) {
if (visualAngleMode.isMediumSight()) {
mSwitchMapModeImage.setVisibility(View.VISIBLE);
} else if (visualAngleMode.isLongSight()) {
mSwitchMapModeImage.setVisibility(View.VISIBLE);
} else if (visualAngleMode.isCloseSight()) {
mSwitchMapModeImage.setVisibility(View.GONE);
}
}
/**
* sweeper调试面板打开关闭
*/
public void debugTestBar() {
if (groupTestPanel.getVisibility() == View.VISIBLE) {
groupTestPanel.setVisibility(View.GONE);
} else {
groupTestPanel.setVisibility(View.VISIBLE);
}
}
/**
* 底部四个按钮监听
* 1.地图放大缩小
* 2.设置面板
* 3.安全员问题上报面板
* 4.道路状况上报面板
*/
private void setBottomBtnListener() {
updateSwitchMapIcon();
mSwitchMapModeImage.setOnClickListener(new OnPreventFastClickListener() {
@Override
public void onClickImpl(View v) {
IMogoMapUIController controller = CallerMapUIServiceManager.INSTANCE.getMapUIController(DEFAULT);
if (controller != null) {
//切换地图的远近视图
if (controller.getCurrentMapVisualAngle().isLongSight()) {
// 2.11.0去掉
// MogoMarkerManager.getInstance(AbsMogoApplication.getApp()).visibleAllMarkers();
controller.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_small_selector);
} else if (controller.getCurrentMapVisualAngle().isMediumSight()) {
// 2.11.0去掉
// MogoMarkerManager.getInstance(AbsMogoApplication.getApp())
// .inVisibleWithoutMarkers(DataTypes.TYPE_MARKER_ADAS, BusConst.TYPE_MARKER_BUS_ORDER);
controller.changeMapVisualAngle(VisualAngleMode.MODE_LONG_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_big_selector);
} else {
// 2.11.0去掉
// MogoMarkerManager.getInstance(AbsMogoApplication.getApp()).visibleAllMarkers();
controller.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_big_selector);
}
}
}
});
mSettingBtn.setOnClickListener(v -> {
// TODO: 2021/12/9
CallerHmiManager.INSTANCE.showToolsView();
});
if (mCardBtn != null) {
CallerDevaToolsManager.INSTANCE.initBadCase(mCardBtn);
}
if (mAICollectBtn != null) {
CallerDevaToolsManager.INSTANCE.initAiCollect(mAICollectBtn);
}
}
/**
* 设置作业任务全览图隐藏或者显示
*
* @param isShow
*/
public void showOrHideOverMapViewFragment(boolean isShow) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (isShow) {
mFlWeltMapOverView.setVisibility(View.VISIBLE);
if (mWeltMapOverViewFragment == null) {
mWeltMapOverViewFragment = mWeltMapOverViewFragment.newInstance(
(IWeltMapSwitchToSmallCallback) this,
mCurrentTaskEndStation,
mWeltDataBeanList,
mSubTaskCoordinates,
mRouteList,
mProgress,
(SweeperFragment) this);
}
if (mWeltMapOverViewFragment.isHidden()) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(mWeltMapOverViewFragment).commitAllowingStateLoss();
return;
}
if (mWeltMapOverViewFragment.isAdded()) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(mWeltMapOverViewFragment).commitAllowingStateLoss();
return;
}
transaction
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(R.id.sweeper_welt_map_overview, mWeltMapOverViewFragment)
.show(mWeltMapOverViewFragment).commitAllowingStateLoss();
} else {
mFlWeltMapOverView.setVisibility(View.GONE);
if (mWeltMapOverViewFragment != null) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
.hide(mWeltMapOverViewFragment).commitAllowingStateLoss();
}
}
}
@Override
public void onWeltMapSwitchToSmall() {
showOrHideOverMapViewFragment(false);
}
/**
* 设置贴边数据到地图
*
* @param weltDataBeans
*/
public void setWeltDataToMap(ArrayList<WeltDataBean> weltDataBeans, Boolean isWeltData, String distance) {
mWeltDataBeanList = weltDataBeans;
ThreadUtils.getSinglePool().execute(new Runnable() {
@Override
public void run() {
if (mMapWeltView != null) {
mMapWeltView.setWeltData(weltDataBeans, isWeltData, distance);
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setWeltData(weltDataBeans, distance);
}
}
});
}
public void setTaskListCoordinatesLatLng(ArrayList<LatLng> subTaskCoordinates) {
this.mSubTaskCoordinates = subTaskCoordinates;
if (mMapWeltView != null) {
mMapWeltView.setTaskListCoordinatesLatLng(subTaskCoordinates);
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setTaskListCoordinatesLatLng(subTaskCoordinates);
}
}
/**
* 设置当前任务终点
*
* @param subTaskCoordinate
*/
public void setCurrentTaskEndMarker(LatLng subTaskCoordinate) {
if (mMapWeltView != null) {
mMapWeltView.setCurrentTaskCoordinatesLatLng(subTaskCoordinate);
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setCurrentTaskCoordinatesLatLng(subTaskCoordinate);
}
}
/**
* 清除marker标记和任务路线数据
*/
public void clearAllMarkerAndPolyline() {
if (mMapWeltView != null) {
mMapWeltView.clearAllMarkerAndPolyline();
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.clearAllMarkerAndPolyline();
}
}
/**
* 设置任务轨迹点数据
*/
public void setTaskRouteList(ArrayList<SweeperRoutePlanningUpdateReqBean.Result> routeList) {
mRouteList = routeList;
ThreadUtils.getSinglePool().execute(() -> {
mMapWeltView.setRouteList(routeList);
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setRouteList(routeList);
}
});
}
public static String format(double value) {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(2, RoundingMode.HALF_UP);
return bd.toString();
}
public void setTaskDataToFragmentCallback(ISweeperTaskDataToFragmentCallback mTaskDataToFragmentCallback) {
this.mTaskDataToFragmentCallback = mTaskDataToFragmentCallback;
}
/**
* 结束子任务
*/
public void debugEndSubTask() {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> resultArrayList = tempData();
setTaskRouteList(resultArrayList);
UiThreadHandler.postDelayed(() -> {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> routeList = tempData1();
ArrayList<WeltDataBean> weltDataBeans = new ArrayList<>();
ArrayList<LatLng> subTaskCoordinates = new ArrayList<>();
for (int i = 0; i < routeList.size(); i++) {
WeltDataBean weltDataBean = new WeltDataBean();
weltDataBean.setLocLon(routeList.get(i).longitude);
weltDataBean.setLocLat(routeList.get(i).latitude);
weltDataBean.setWeltDistance(Math.random() * 50);
weltDataBeans.add(weltDataBean);
subTaskCoordinates.add(new LatLng(routeList.get(i).latitude, routeList.get(i).longitude));
}
setTaskListCoordinatesLatLng(subTaskCoordinates);
setWeltDataToMap(weltDataBeans, true, format(Math.random() * 50));
}, 2000);
}
private ArrayList<SweeperRoutePlanningUpdateReqBean.Result> tempData() {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> results = new ArrayList<>();
results.add(getRoute(112.56970669282785, 26.8175674285121));
results.add(getRoute(112.57154905962457, 26.81762377130161));
results.add(getRoute(112.57723562917293, 26.819769721883098));
return results;
}
private ArrayList<SweeperRoutePlanningUpdateReqBean.Result> tempData1() {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> results = new ArrayList<>();
results.add(getRoute(112.56970669282785, 26.8175674285121));
results.add(getRoute(112.57154905962457, 26.81762377130161));
results.add(getRoute(112.57723562917293, 26.819769721883098));
return results;
}
private SweeperRoutePlanningUpdateReqBean.Result getRoute(double longitude, double latitude) {
LatLng latLng = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(AbsMogoApplication.getApp(), longitude, latitude);
SweeperRoutePlanningUpdateReqBean.Result result = new SweeperRoutePlanningUpdateReqBean.Result();
result.latitude = latLng.latitude;
result.longitude = latLng.longitude;
return result;
}
}

View File

@@ -0,0 +1,50 @@
package com.mogo.och.sweeper.cloud.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
/**
* @author: wangmingjun
* @date: 2021/9/9
*/
public abstract class BaseSweeperUIFragment extends Fragment {
private View mRootView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(getLayoutId(), container, false);
} else {
ViewGroup viewGroup = (ViewGroup) mRootView.getParent();
if (viewGroup != null) {
viewGroup.removeView(mRootView);
}
}
initViews(mRootView);
initViews(savedInstanceState);
return mRootView;
}
/**
* 布局资源
*
* @return
*/
protected abstract int getLayoutId();
protected abstract void initViews(View view);
protected void initViews(Bundle savedInstanceState) {
}
@Override
public void onDestroy() {
super.onDestroy();
mRootView = null;
}
}

View File

@@ -0,0 +1,230 @@
package com.mogo.och.sweeper.cloud.fragment;
import android.os.Bundle;
import android.view.View;
import com.amap.api.navi.AMapNaviViewListener;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.common.module.map.AmapNaviToDestinationModel;
import com.mogo.och.common.module.map.CommonAmapNaviVIew;
import com.mogo.och.common.module.map.ICommonNaviChangedCallback;
import com.mogo.och.sweeper.R;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI;
/**
* @author: wangmingjun
* @date: 2021/11/30
*/
public class SweeperAmapNaviFragment extends BaseSweeperUIFragment implements AMapNaviViewListener {
private CommonAmapNaviVIew mAMapNaviView;
private ICommonNaviChangedCallback mNaviToStartInfoCallback;
public static SweeperAmapNaviFragment newInstance() {
Bundle args = new Bundle();
SweeperAmapNaviFragment fragment = new SweeperAmapNaviFragment();
fragment.setArguments(args);
return fragment;
}
@Override
protected int getLayoutId() {
return R.layout.sweeper_amap_navi_view;
}
@Override
protected void initViews(View view) {
mAMapNaviView = view.findViewById(R.id.navi_view);
}
@Override
protected void initViews(Bundle savedInstanceState) {
super.initViews(savedInstanceState);
if (mAMapNaviView != null)
mAMapNaviView.onCreate(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
if (mAMapNaviView != null)
mAMapNaviView.onResume();
}
@Override
public void onPause() {
super.onPause();
if (mAMapNaviView != null)
mAMapNaviView.onPause();
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) { //不在最前端界面显示
if (mAMapNaviView != null){
mAMapNaviView.onPause();
}
} else { //重新显示到最前端
if (mAMapNaviView != null){
mAMapNaviView.onResume();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
AmapNaviToDestinationModel.getInstance(getContext()).setVoiceIsMute(false);
if (mAMapNaviView != null){
mAMapNaviView.onDestroy();
}
if (mNaviToStartInfoCallback != null){
mNaviToStartInfoCallback = null;
}
}
@Override
public void onNaviSetting() {
//底部导航设置点击回调
}
@Override
public void onNaviCancel() {
}
@Override
public void onNaviMapMode(int naviMode) {
//导航态车头模式0:车头朝上状态1:正北朝上模式。
}
@Override
public void onNaviTurnClick() {
//转弯view的点击回调
}
@Override
public void onNextRoadClick() {
//下一个道路View点击回调
}
@Override
public void onScanViewButtonClick() {
//全览按钮点击回调
}
@Override
public void onLockMap(boolean isLock) {
//锁地图状态发生变化时回调
}
@Override
public void onNaviViewLoaded() {
CallerLogger.d(M_TAXI + "wlx", "导航页面加载成功");
CallerLogger.d(M_TAXI + "wlx", "请不要使用AMapNaviView.getMap().setOnMapLoadedListener();会overwrite导航SDK内部画线逻辑");
}
@Override
public void onMapTypeChanged(int i) {
}
@Override
public void onNaviViewShowMode(int i) {
}
@Override
public boolean onNaviBackClick() {
return false;
}
/**
* 车道信息说明:
* <p>
* 0xFF, 无对应车道
* 0, 直行
* 1, 左转
* 2, 直行+左转
* 3, 右转
* 4, 直行+右转
* 5, 左掉头
* 6, 左转+右转
* 7, 直行+左转+右转
* 8, 右掉头
* 9, 直行+左掉头
* 10, 直行+右掉头
* 11, 左转+左掉头
* 12, 右转+右掉头
* 13, 直行+扩展
* 14, 左转+左掉头+扩展
* 15, 保留
* 16, 直行+左转+左掉头
* 17, 右转+左掉头
* 18, 左转+右转+左掉头
* 19, 直行+右转+左掉头
* 20, 左转+右掉头
* 21, 公交车道
* 22, 空车道
* 23 可变车道
*/
String[] array = {
"直行车道"
, "左转车道"
, "左转或直行车道"
, "右转车道"
, "右转或直行车道"
, "左掉头车道"
, "左转或者右转车道"
, " 左转或右转或直行车道"
, "右转掉头车道"
, "直行或左转掉头车道"
, "直行或右转掉头车道"
, "左转或左掉头车道"
, "右转或右掉头车道"
, "直行并且车道扩展"
, "左转+左掉头+扩展"
, "不可以选择该车道"
, "直行+左转+左掉头车道"
, "右转+左掉头"
, "左转+右转+左掉头"
, "直行+右转+左掉头"
, "左转+右掉头"
, "公交车道"
, "空车道"
, "可变车道"
};
String[] actions = {
"直行"
, "左转"
, "左转或直行"
, "右转"
, "右转或这行"
, "左掉头"
, "左转或者右转"
, " 左转或右转或直行"
, "右转掉头"
, "直行或左转掉头"
, "直行或右转掉头"
, "左转或左掉头"
, "右转或右掉头"
, "直行并且车道扩展"
, "左转+左掉头+扩展"
, "不可以选择"
, "直行+左转+左掉头"
, "右转+左掉头"
, "左转+右转+左掉头"
, "直行+右转+左掉头"
, "左转+右掉头"
, "公交车道"
, "空车道"
, "可变车道"
};
}

View File

@@ -0,0 +1,994 @@
package com.mogo.och.sweeper.cloud.fragment
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import chassis.ChassisStatesOuterClass
import com.amap.api.maps.model.LatLng
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener.Companion.STATUS_AUTOPILOT_ENABLE
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener.Companion.STATUS_AUTOPILOT_RUNNING
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager
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.ToastUtils
import com.mogo.map.overlay.core.Level
import com.mogo.map.overlay.point.Point
import com.mogo.och.common.module.utils.OCHThreadPoolManager
import com.mogo.och.common.module.voice.VoiceNotice.showNotice
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.constant.SweeperConst.Companion.TYPE_MARKER_SWEEPER_SUBTASK_START_END
import com.mogo.och.sweeper.common.database.MyDataBase
import com.mogo.och.sweeper.cloud.presenter.SweeperPresenter
import com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog
import com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudLoadingDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createConfirmStartSubTaskDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createReceivedBigTaskInfoDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createSweeperTaskEndDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createSweeperTaskExceptionEndDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createSweeperTaskNormalEndDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createSweeperTaskResumeDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.createSweeperTaskSuspendDialog
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.printMessage
import com.mogo.och.sweeper.cloud.view.SweeperCurrentTaskInfoView
import com.zhjt.mogo.adas.data.AiCloudTask
import com.zhjt.mogo.adas.data.sweeper.bootable.SweeperBootable
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon.TaskStatus
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.SubTaskInfo
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.TaskModel
import com.zhjt.mogo.adas.data.sweeper.task.cloud.s_r.SweeperTaskCloudSuspendResume
import com.zhjt.mogo.adas.data.sweeper.task.confirm.SweeperTaskConfirm
import com.zhjt.mogo.adas.data.sweeper.task.stop.SweeperTaskStop
import kotlinx.android.synthetic.main.fragment_och_sweeper_cloud.*
import kotlinx.android.synthetic.main.sweeper_cloud_no_data_common_view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
* 清扫车主界面
*/
class SweeperFragment : BaseSweeperTabFragment<SweeperFragment?, SweeperPresenter?>() {
companion object {
const val TAG = "SweeperFragment"
}
private var mSubMutableList: MutableList<SubTaskInfo>? = null
private var mCleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates? = null
private var mLocation: MogoLocation? = null
private var mTaskInfo: SweeperTask.TaskInfo? = null
private var mSubTaskType: TaskModel = TaskModel.DEFAULT_MODEL //1:自动驾驶子任务 2人工驾驶子任务 -1:未知任务类型
private var mCurrentRunningSubTaskInfo: SubTaskInfo? = null//当前正在执行的子任务
private var mCurrentSuspendSubTaskInfo: SubTaskInfo? = null//当前暂停的子任务
private val mLoadingDialog: SweeperCloudLoadingDialog by lazy {
SweeperCloudLoadingDialog(
requireContext()
)
}
private var mDialog: SweeperCloudDialog? = null
override fun getTagName(): String {
return "SweepersFragment"
}
override fun initViews() {
super.initViews()
initListener()
sweeper_cl_work_mode.setTrafficDataView(trafficDataView)
}
override fun createPresenter(): SweeperPresenter {
return SweeperPresenter(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mPresenter?.getCurrentTask()
}
override fun getTaskListPanelViewId(): Int {
return R.layout.fragment_och_sweeper_cloud
}
/**
* 设置当前任务信息列表是否显示
*/
private fun setShowCurrentTaskPanelView(isShow: Boolean) {
if (isShow) {
sweeper_current_task_view.visibility = View.VISIBLE
noTaskDataView.visibility = View.GONE
} else {
sweeper_current_task_view.visibility = View.GONE
noTaskDataView.visibility = View.VISIBLE
}
}
/**
* VR模式切换
*
* @param isVRMode
*/
fun onVRModeChanged(isVRMode: Boolean) {
if (mRootView != null) {
mRootView.visibility = if (isVRMode) View.VISIBLE else View.GONE
}
}
fun hideOchSweeper() {}
/**
* 设置各种监听事件
*/
private fun initListener() {
getCurrentTaskView().getAutoBtn().setOnClickListener {
//前置条件 (1)必须处于人工驾驶状态 2必须有正在执行的子任务且子任务类型为自动驾驶子任务 才能请求云端进入自驾
mCurrentRunningSubTaskInfo?.let {
if (mPresenter?.autopilotState == STATUS_AUTOPILOT_ENABLE
&& it.taskModel == TaskModel.AUTO
) {
mLoadingDialog.showLoading()
mPresenter?.sendSweeperAutopilotBootable(
mTaskInfo?.taskId,
it.subTaskId,
it.lineId.toLong()
)
}
}
}
}
override fun setAutoState(state: Int) {
super.setAutoState(state)
getCurrentTaskView().updateStartAutoPilotBtnByAutoPilotState(
state == STATUS_AUTOPILOT_ENABLE,
mCurrentRunningSubTaskInfo?.taskModel == TaskModel.AUTO,
mCurrentRunningSubTaskInfo?.taskStatus == TaskStatus.RUNNING
)
}
fun onSweeperFutianCleanSystemState(cleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates) {
mCleanSystemState = cleanSystemState
sweeper_cl_work_mode.setSweeperFutianCleanSystemState(mSubTaskType, cleanSystemState)
}
/**
* 当前位置经纬度
*/
fun setCurrentLocation(location: MogoLocation) {
this.mLocation = location
}
/**
* 大任务中所有子任务起终点集合
*/
private fun addTaskData() {
lifecycleScope.launch(Dispatchers.IO) {
mSubMutableList?.let { subList ->
if (subList.size <= 0) {
return@launch
}
val dataList = ArrayList<LatLng>()
for (index in subList.indices) {
val startLocation = subList[index].startLocation
val startLatLng = LatLng(startLocation.latitude, startLocation.longitude)
dataList.add(index, startLatLng)
val endLocation = subList[index].endLocation
val endLatLng = LatLng(endLocation.latitude, endLocation.longitude)
dataList.add(index + 1, endLatLng)
}
setTaskListCoordinatesLatLng(dataList)
}
}
}
/**
* 设置当前子任务终点
*/
private fun setEndStationMarker(position: Int) {
lifecycleScope.launch(Dispatchers.IO) {
mSubMutableList?.let { subList ->
val endPoint = subList[position - 1].endLocation
val endLatLng = LatLng(endPoint.latitude, endPoint.longitude)
super.mCurrentTaskEndStation = endLatLng
setCurrentTaskEndMarker(endLatLng)
}
}
}
/**
* 设置云端任务信息
*/
fun onSweeperCloudTask(
messageType: AiCloudTask.MessageType,
taskInfo: SweeperTask.TaskInfo?
) {
this.mTaskInfo = taskInfo
this.mSubMutableList = taskInfo?.subListList
CallerLogger.d(SceneConstant.M_SWEEPER + TAG, taskInfo?.let { printMessage(it) })
if (taskInfo == null || this.mSubMutableList == null) {
resetTaskInfoPanel()
return
}
//主动拉取云端正在执行的任务,有则直接在任务列表展示
if (messageType == AiCloudTask.MessageType.PadSendGetTaskReq) {
getCurrentTaskView().setData(taskInfo, getCurrentRunningSubTaskPosition())
setShowCurrentTaskPanelView(true)
addTaskData()
mPresenter?.isHasTaskInfo(true)
//更新自动驾驶按钮样式
updateStartAutoPilotBtnByAutoPilotState()
//恢复高精地图上的Marker
mCurrentRunningSubTaskInfo?.also {
addHDMapMarkerBySubTask(it)
}
} else {//云端下发的大任务信息需要弹窗提示V3.2.0开始不需要安全员确认接取任务,只能默认接收)
//是否需要弹窗确认
getCurrentTaskView().setData(taskInfo)
setShowCurrentTaskPanelView(true)
addTaskData()
mPresenter?.isHasTaskInfo(true)
//更新自动驾驶按钮样式
updateStartAutoPilotBtnByAutoPilotState()
if (taskInfo.isPop) {
showReceivedBigTaskDialog()
} else {
// 默认发送接收到消息的ACK回执
mPresenter?.sendSweeperStartTaskResp(
taskInfo.taskId,
SweeperTask.StartTaskCode.RECEIVED
)
}
//设置任务进度
this.mSubMutableList?.let {
setTaskProgress("0/${it.size}")
}
}
}
private fun updateStartAutoPilotBtnByAutoPilotState() {
getCurrentTaskView().updateStartAutoPilotBtnByAutoPilotState(
mPresenter?.autopilotState == STATUS_AUTOPILOT_ENABLE,
mCurrentRunningSubTaskInfo?.taskModel == TaskModel.AUTO,
mCurrentRunningSubTaskInfo?.taskStatus == TaskStatus.RUNNING
)
}
/**
* 根据云端同步的子任务状态,更新列表中任务状态
*/
fun onSweeperUpdateSubTaskStatus(
taskId: String,
subTaskId: String,
subTaskStatus: SweeperCommon.TaskStatus
) {
mLoadingDialog.hideLoading()
updateSubTaskInfo(taskId, subTaskId, subTaskStatus)
updateStartAutoPilotBtnByAutoPilotState()
}
/**
* 子任务确认时,更新子任务信息
*/
private fun updateSubTaskInfo(
taskId: String,
subTaskId: String,
subTaskStatus: SweeperCommon.TaskStatus
) {
mTaskInfo?.let {
//如果云端要更新的任务和本地缓存任务不是同一个,在无法更新子任务状态
if (taskId != it.taskId) {
return
}
mSubMutableList?.let { subTaskList ->
for (index in 0 until subTaskList.size) {
if (subTaskList[index].subTaskId == subTaskId) {
val subBuilder = subTaskList[index].toBuilder()
subBuilder.taskStatus = subTaskStatus
if (subTaskStatus == TaskStatus.RUNNING) {
mCurrentRunningSubTaskInfo = subBuilder.build()
//更新当前正在执行的任务状态
updateCurrentSubTaskInfo(index + 1)
// 先移除在添加 起终点 Marker
addHDMapMarkerBySubTask(subTaskList[index])
} else {
mCurrentRunningSubTaskInfo = null
}
if (subTaskStatus == TaskStatus.SUSPENDED) {
mCurrentSuspendSubTaskInfo = subBuilder.build()
} else {
mCurrentSuspendSubTaskInfo = null
}
//判断是否结束子任务
if (subTaskStatus == TaskStatus.FINISHED) {
//已完成的计入任务进度,未完成的不计入
ToastUtils.showLong("任务完成")
showNotice("任务完成")
removeHDMapMarkerBySubTask(subTaskList[index])
}
// 其他异常结束时也需要remove marker
if (subTaskStatus == TaskStatus.ABNORMAL ||
subTaskStatus == TaskStatus.CANCEL
) {
removeHDMapMarkerBySubTask(subTaskList[index])
}
val builder = it.toBuilder()
builder.setSubList(index, subBuilder.build())
mTaskInfo = builder.build()
mSubMutableList = mTaskInfo?.subListList
getCurrentTaskView().setData(mTaskInfo, index)
}
}
}
}
}
/**
* 云端请求结束大任务
*/
fun onSweeperCloudTaskStop(
taskId: String,
stopTaskType: SweeperTaskStop.StopTaskType,
isPop: Boolean
) {
mTaskInfo?.let {
//如果云端要更新的任务和本地缓存任务不是同一个,在无法结束任务
if (taskId != it.taskId) {
return
}
if (isPop) {
sweeperCloudTaskStopDialog(taskId, stopTaskType, it)
} else {
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperStopTaskResp(taskId, SweeperTaskStop.StopTaskCode.RECEIVED)
}
}
}
/**
* 中止任务弹窗处理
*/
private fun sweeperCloudTaskStopDialog(
taskId: String,
stopTaskType: SweeperTaskStop.StopTaskType,
taskInfo: SweeperTask.TaskInfo
) {
mLoadingDialog.hideLoading()
val timeoutHintString: String =
SweeperCloudTaskUtils.getTimeSpentString(taskInfo.taskEndTime)
//数据重复导致连续弹窗
if (mDialog != null && mDialog!!.isShowing) {
return
}
if (stopTaskType == SweeperTaskStop.StopTaskType.NORMAL) {
mDialog = sweeperCloudTaskNormalEndDialog()
showNotice("任务已结束")
ToastUtils.showLong("任务已结束")
} else {
// 产品确认有弹框时不需要同步弹toast, 只需有TTS
mDialog = createSweeperTaskEndDialog(context, object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// mPresenter?.sendSweeperStopTaskResp(taskId, SweeperTaskStop.StopTaskCode.MANUAL_CONFIRM)
// mLoadingDialog.showLoading()
// //ToastUtils.showLong("确认")
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperStopTaskResp(
taskId,
SweeperTaskStop.StopTaskCode.RECEIVED
)
}
override fun onRefuseOrEnd() {
// mPresenter?.sendSweeperStopTaskResp(taskId, SweeperTaskStop.StopTaskCode.MANUAL_REFUSE)
// mLoadingDialog.showLoading()
// //ToastUtils.showLong("拒绝")
}
override fun onCountDownStop() {
// mPresenter?.sendSweeperStopTaskResp(taskId, SweeperTaskStop.StopTaskCode.OVER_TIME)
// mLoadingDialog.showLoading()
// //ToastUtils.showLong("倒计时")
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperStopTaskResp(
taskId,
SweeperTaskStop.StopTaskCode.RECEIVED
)
}
override fun onNext() {
}
}, stopTaskType, timeoutHintString)
}
mDialog?.show()
}
/**
* 大任务结束弹窗
*/
private fun sweeperCloudTaskNormalEndDialog(): SweeperCloudDialog? {
return createSweeperTaskNormalEndDialog(context, object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// ToastUtils.showLong("任务已结束")
// resetTaskInfoPanel()
}
override fun onRefuseOrEnd() {
}
override fun onCountDownStop() {
// ToastUtils.showLong("任务已结束")
// resetTaskInfoPanel()
}
override fun onNext() {
}
})
}
/**
* 大任务异常结束弹窗(异常 / 取消)
*/
private fun sweeperCloudTaskExceptionEndDialog(isCancel: Boolean): SweeperCloudDialog? {
return createSweeperTaskExceptionEndDialog(
context,
object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// ToastUtils.showLong("任务已结束")
// resetTaskInfoPanel()
}
override fun onRefuseOrEnd() {
}
override fun onCountDownStop() {
// ToastUtils.showLong("任务已结束")
// resetTaskInfoPanel()
}
override fun onNext() {
}
},
isCancel
)
}
/**
* 云端同步大任务状态
*/
fun onSweeperCloudBigTaskStatus(
taskId: String,
bigTaskStatus: SweeperCommon.TaskStatus,
isPop: Boolean
) {
//隐藏loading
mLoadingDialog.hideLoading()
mTaskInfo?.let {
//如果云端要更新的任务和本地缓存任务不是同一个,在无法进行任务列表状态更新
if (it.taskId != taskId) {
return
}
//大任务处于取消状态/异常结束/正常结束 时都代表大任务结束了,任务列表切换为无任务列表状态
if (bigTaskStatus == TaskStatus.CANCEL
|| bigTaskStatus == TaskStatus.ABNORMAL
|| bigTaskStatus == TaskStatus.FINISHED
) {
mPresenter?.isHasTaskInfo(false)
if (isPop) {
mDialog = if (bigTaskStatus == TaskStatus.CANCEL
|| bigTaskStatus == TaskStatus.ABNORMAL
) {
sweeperCloudTaskExceptionEndDialog(bigTaskStatus == TaskStatus.CANCEL)
} else
sweeperCloudTaskNormalEndDialog()
mDialog?.show()
}
showNotice("任务已结束")
// 大任务这里也判断移除HD Marker, 双重判断
removeHDMapMarkerBySubTask(mCurrentRunningSubTaskInfo)
resetTaskInfoPanel()
} else if (bigTaskStatus == TaskStatus.SUSPENDED) {// 暂停
showNotice("任务已暂停")
ToastUtils.showLong("任务已暂停")
//更新任务列表中大任务状态
val builder = it.toBuilder()
builder.taskStatus = TaskStatus.SUSPENDED
mTaskInfo = builder.build()
getCurrentTaskView().setData(mTaskInfo, getCurrentRunningSubTaskPosition())
} else if (it.taskStatus == TaskStatus.SUSPENDED
&& bigTaskStatus == TaskStatus.RUNNING
) { //恢复
showNotice("任务已恢复")
ToastUtils.showLong("任务已恢复")
//更新任务列表中大任务状态
val builder = it.toBuilder()
builder.taskStatus = TaskStatus.RUNNING
mTaskInfo = builder.build()
getCurrentTaskView().setData(mTaskInfo, getCurrentRunningSubTaskPosition())
} else if (bigTaskStatus == TaskStatus.RUNNING) { //首次开始运行的时候,非恢复后的运行
//更新任务列表中大任务状态
val builder = it.toBuilder()
builder.taskStatus = TaskStatus.RUNNING
mTaskInfo = builder.build()
getCurrentTaskView().setData(mTaskInfo, getCurrentRunningSubTaskPosition())
}
}
}
/**
* 云端同步 暂停/恢复 大任务状态
*/
fun onSweeperCloudBigTaskSuspendOrResume(
taskId: String,
actionType: SweeperTaskCloudSuspendResume.ActionType,
isPop: Boolean
) {
//隐藏loading
mLoadingDialog.hideLoading()
mTaskInfo?.let {
//如果云端要更新的任务和本地缓存任务不是同一个,在无法进行任务列表状态更新
if (it.taskId != taskId) {
return
}
if (isPop) {
//数据重复导致连续弹窗
if (mDialog != null && mDialog!!.isShowing) {
return
}
when (actionType) {
SweeperTaskCloudSuspendResume.ActionType.SUSPENDED -> {
mDialog = sweeperCloudTaskSuspendDialog(it.taskId)
showNotice("云端暂停任务,注意车辆即将停车!")
}
else -> {
var ttsString = "云端恢复任务,注意车辆即将启动!"
mCurrentSuspendSubTaskInfo?.also { suspendSub ->
val isAutoPoilotState =
mPresenter?.autopilotState == STATUS_AUTOPILOT_RUNNING
when {
suspendSub.taskModel == TaskModel.MANUAL -> {
ttsString = "云端恢复任务,请继续手动驾驶完成任务!"
}
suspendSub.taskModel == TaskModel.AUTO && isAutoPoilotState -> {
ttsString = "云端恢复任务,注意车辆即将启动!"
}
suspendSub.taskModel == TaskModel.AUTO && !isAutoPoilotState -> {
ttsString = "云端恢复任务,请到达合适的位置后开启自动驾驶!"
}
}
}
showNotice(ttsString)
mDialog = sweeperCloudTaskResumeDialog(ttsString, it.taskId)
}
}
mDialog?.show()
} else {
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperSuspendResumeTaskResp(
taskId,
SweeperTaskCloudSuspendResume.Code.RECEIVED
)
}
}
}
/**
* pad请求云端进入自动驾驶回调
*/
fun onSweeperCloudAutopilotBootable(
taskId: String,
subTaskId: String,
isBootableResp: SweeperBootable.IsBootableResp
) {
mLoadingDialog.hideLoading()
//如果云端要更新的任务和本地缓存任务不是同一个,在无法进行任务列表状态更新
mTaskInfo?.let {
if (it.taskId != taskId) {
return
}
if (isBootableResp.code == SweeperCommon.Code.SUCCEED) {
ToastUtils.showLong("上报云控成功")
showNotice("上报云控成功")
} else {
ToastUtils.showLong("上报云控失败")
showNotice("上报云控失败")
}
}
}
/**
* 重置任务面板为无数据状态
*/
private fun resetTaskInfoPanel() {
//清除任务缓存数据
mPresenter?.clearRouteList()
lifecycleScope.launch(Dispatchers.IO) {
//清除任务地图绘制标记和作业路线
clearAllMarkerAndPolyline()
//清除贴边数据缓存
MyDataBase.getInstance().weltDataDao.deleteAllWeltData()
}
setShowCurrentTaskPanelView(false)
mTaskInfo = null
mCurrentRunningSubTaskInfo = null
mCurrentSuspendSubTaskInfo = null
mSubMutableList = null
mSubTaskType = TaskModel.DEFAULT_MODEL
setTaskProgress("0")
}
/**
*
* 获取当前正在执行的子任务在列表中的位置
*/
private fun getCurrentRunningSubTaskPosition(): Int {
mSubMutableList = mTaskInfo?.subListList
mSubMutableList?.apply {
for (index in 0 until size) {
if (get(index).taskStatus == TaskStatus.RUNNING) {
mCurrentRunningSubTaskInfo = get(index)
updateCurrentSubTaskInfo(index + 1)
return index
}
}
}
return -1
}
/**
* 根据子任务id获取子任务信息
*/
private fun getSubTaskInfoBySubId(subTaskId: String): SubTaskInfo? {
mSubMutableList = mTaskInfo?.subListList
mSubMutableList?.apply {
for (index in 0 until size) {
if (get(index).subTaskId == subTaskId) {
return get(index)
}
}
}
return null
}
/**
* 更新当前子任务信息
*/
private fun updateCurrentSubTaskInfo(position: Int) {
mCurrentRunningSubTaskInfo?.let {
mSubTaskType = it.taskModel
mPresenter?.setSubtask(it.subTaskId, it.taskModel, it.lineId)
setEndStationMarker(position)
//设置任务进度
this.mSubMutableList?.apply {
setTaskProgress("${position}/${size}")
}
}
}
/**
* 设置路线轨迹
*/
override fun setTaskRouteList(routeList: ArrayList<com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean.Result>) {
super.setTaskRouteList(routeList)
}
private fun getCurrentTaskView(): SweeperCurrentTaskInfoView = sweeper_current_task_view
fun handleSubTaskInfoDialog(taskId: String, subTaskId: String, isPop: Boolean) {
if (mTaskInfo == null) return
if (mTaskInfo?.taskId != taskId) return
val subInfo = getSubTaskInfoBySubId(subTaskId)
subInfo?.let {
updateSubTaskInfo(taskId, subTaskId, TaskStatus.RUNNING)
if (isPop) {
showSubTaskConfirmDialog(taskId, subTaskId, it)
} else {
// 发送接收到消息的ACK回执
mPresenter?.sendSweeperTaskConfirmResp(
taskId,
subTaskId,
SweeperTaskConfirm.TaskConfirmCode.RECEIVED
)
}
}
}
/**
* 云控下发子任务确认弹窗
*/
private fun showSubTaskConfirmDialog(
taskId: String,
subTaskId: String,
subTaskInfo: SubTaskInfo
) {
mLoadingDialog.hideLoading()
if (mTaskInfo == null) return
if (mTaskInfo?.taskId != taskId) return
//数据重复导致连续弹窗
if (mDialog != null && mDialog!!.isShowing) {
return
}
mDialog =
createConfirmStartSubTaskDialog(context, object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// //ToastUtils.showLong("确认")
// mPresenter?.sendSweeperTaskConfirmResp(taskId, subTaskId, SweeperTaskConfirm.TaskConfirmCode.MANUAL_CONFIRM)
// //mPresenter?.setPreLineId(-1)
// updateSubTaskInfo(taskId, subTaskId, SweeperCommon.TaskStatus.RUNNING)
// mLoadingDialog.showLoading()
// 发送接收到消息的ACK回执
mPresenter?.sendSweeperTaskConfirmResp(
taskId,
subTaskId,
SweeperTaskConfirm.TaskConfirmCode.RECEIVED
)
}
override fun onRefuseOrEnd() {
// //ToastUtils.showLong("结束")
// mPresenter?.sendSweeperTaskConfirmResp(taskId, subTaskId, SweeperTaskConfirm.TaskConfirmCode.MANUAL_REFUSE_ALL)
// mLoadingDialog.showLoading()
}
override fun onCountDownStop() {
// //ToastUtils.showLong("倒计时结束")
// mPresenter?.sendSweeperTaskConfirmResp(taskId, subTaskId, SweeperTaskConfirm.TaskConfirmCode.OVER_TIME)
// mLoadingDialog.showLoading()
// 发送接收到消息的ACK回执
mPresenter?.sendSweeperTaskConfirmResp(
taskId,
subTaskId,
SweeperTaskConfirm.TaskConfirmCode.RECEIVED
)
}
override fun onNext() {
// //ToastUtils.showLong("下一个")
// mPresenter?.sendSweeperTaskConfirmResp(taskId, subTaskId, SweeperTaskConfirm.TaskConfirmCode.MANUAL_REFUSE_SINGLE)
// mLoadingDialog.showLoading()
}
}, subTaskInfo)
mDialog?.show()
if (subTaskInfo.taskModel == TaskModel.MANUAL) {
showNotice("请确认并执行子任务")
} else { //自动驾驶子任务需要提醒在N档且拉起手刹做好准备工作
showNotice("请确认并执行子任务, 保持N档拉手刹再进入自动驾驶")
}
}
/**
* 接取云端大任务弹窗提示
* V3.2.0 开始不需要确认/拒绝/下一个 操作了,安全员只能被动接取,弹框只是一个提示)
*/
private fun showReceivedBigTaskDialog() {
mTaskInfo?.let {
//数据重复导致连续弹窗
if (mDialog != null && mDialog!!.isShowing) {
return
}
mDialog =
createReceivedBigTaskInfoDialog(context, object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// ToastUtils.showLong("确认")
// mPresenter?.sendSweeperStartTaskResp(it.taskId, SweeperTask.StartTaskCode.MANUAL_CONFIRM)
// setShowCurrentTaskPanelView(true)
// getCurrentTaskView().setData(it)
// mLoadingDialog.showLoading()
// addTaskData()
// mPresenter?.isHasTaskInfo(true)
// 默认发送接收到消息的ACK回执
mPresenter?.sendSweeperStartTaskResp(
it.taskId,
SweeperTask.StartTaskCode.RECEIVED
)
}
override fun onRefuseOrEnd() {
// ToastUtils.showLong("拒绝")
// mPresenter?.sendSweeperStartTaskResp(it.taskId, SweeperTask.StartTaskCode.MANUAL_REFUSE)
// mLoadingDialog.showLoading()
// resetTaskInfoPanel()
}
override fun onCountDownStop() {
// ToastUtils.showLong("倒计时结束")
// resetTaskInfoPanel()
// mPresenter?.sendSweeperStartTaskResp(it.taskId, SweeperTask.StartTaskCode.OVER_TIME)
// mLoadingDialog.showLoading()
// 默认发送接收到消息的ACK回执
mPresenter?.sendSweeperStartTaskResp(
it.taskId,
SweeperTask.StartTaskCode.RECEIVED
)
}
override fun onNext() {
}
}, it)
mDialog?.show()
showNotice("已为您接取任务")
}
}
/**
* 大任务暂停弹框
*/
private fun sweeperCloudTaskSuspendDialog(taskId: String): SweeperCloudDialog? {
return createSweeperTaskSuspendDialog(context, object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperSuspendResumeTaskResp(
taskId,
SweeperTaskCloudSuspendResume.Code.RECEIVED
)
}
override fun onRefuseOrEnd() {
}
override fun onCountDownStop() {
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperSuspendResumeTaskResp(
taskId,
SweeperTaskCloudSuspendResume.Code.RECEIVED
)
}
override fun onNext() {
}
})
}
/**
* 大任务恢复弹框
*/
private fun sweeperCloudTaskResumeDialog(
contentStr: String,
taskId: String
): SweeperCloudDialog? {
return createSweeperTaskResumeDialog(context, object : com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener {
override fun onConfirm() {
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperSuspendResumeTaskResp(
taskId,
SweeperTaskCloudSuspendResume.Code.RECEIVED
)
}
override fun onRefuseOrEnd() {
}
override fun onCountDownStop() {
// 发送接收到信息的ACK回执
mPresenter?.sendSweeperSuspendResumeTaskResp(
taskId,
SweeperTaskCloudSuspendResume.Code.RECEIVED
)
}
override fun onNext() {
}
}, contentStr)
}
/**
* 添加高精地图中的 子任务 起终点 Marker
*/
private fun addHDMapMarkerBySubTask(subTask: SubTaskInfo) {
if (subTask.startLocation == null
|| subTask.endLocation == null
|| subTask.subTaskId == null
) {
return
}
val startPoint = subTask.startLocation
val endPoint = subTask.endLocation
val hdMarkerIdStart = "marker_sweeper_subtask_${subTask.subTaskId}_start"
val hdMarkerIdEnd = "marker_sweeper_subtask_${subTask.subTaskId}_end"
val setStartMarkerRunnable = createSetHDMapMakerRunnable(
hdMarkerIdStart,
startPoint.wgsLongitude,
startPoint.wgsLatitude,
true
)
val setEndMarkerRunnable = createSetHDMapMakerRunnable(
hdMarkerIdEnd,
endPoint.wgsLongitude,
endPoint.wgsLatitude,
false
)
val removeMarkerRunnable = createRemoveHDMapMakerRunnable()
val addSubTaskHDMarkRunnable = Runnable {
// 先移除高精地图中的 子任务 起终点 Marker, 避免重复添加
removeMarkerRunnable.run()
setStartMarkerRunnable.run()
setEndMarkerRunnable.run()
}
OCHThreadPoolManager.getsInstance().execute(addSubTaskHDMarkRunnable)
}
/**
* 移除高精地图中的 子任务 起终点 Marker
*/
private fun removeHDMapMarkerBySubTask(subTaskInfo: SubTaskInfo?) {
val removeMarkerRunnable = createRemoveHDMapMakerRunnable()
OCHThreadPoolManager.getsInstance().execute(removeMarkerRunnable)
}
/**
* 设置高精地图里的Marker Runnable
*/
private fun createSetHDMapMakerRunnable(
uuid: String,
longitude: Double,
latitude: Double,
isStartMarker: Boolean
): Runnable {
//开启线程执行起终点marker设置
val setMapMarkerRunnable = Runnable {
CallerLogger.d(
SceneConstant.M_SWEEPER + "-" + TAG + "-setMapMaker",
"threadName=${Thread.currentThread().name}, uuid=$uuid, latitude=$latitude, longitude=$longitude"
)
val builder =
Point.Options.Builder(TYPE_MARKER_SWEEPER_SUBTASK_START_END, Level.MAP_MARKER)
.setId(uuid)
.anchor(0.5f, 0.5f)
.set3DMode(true)
.isUseGps(true)
.controlAngle(false)
.icon3DRes(if (isStartMarker) R.raw.star_marker else R.raw.end_marker)
.longitude(longitude)
.latitude(latitude)
val overlayManager = CallerMapUIServiceManager.getOverlayManager()
overlayManager?.showOrUpdatePoint(builder.build())
}
return setMapMarkerRunnable
}
/**
* 移除高精地图里的Marker Runnable
*/
private fun createRemoveHDMapMakerRunnable(): Runnable {
//开启线程移除起终点marker设置
val removeMapMarkerRunnable = Runnable {
CallerLogger.d(
SceneConstant.M_SWEEPER + "-" + TAG + "-RemoveMapMaker",
"threadName=${Thread.currentThread().name}"
)
val overlayManager = CallerMapUIServiceManager.getOverlayManager()
overlayManager?.removeAllPointsInOwner(TYPE_MARKER_SWEEPER_SUBTASK_START_END)
}
return removeMapMarkerRunnable
}
}

View File

@@ -0,0 +1,128 @@
package com.mogo.och.sweeper.cloud.fragment
import android.os.Bundle
import com.amap.api.maps.model.LatLng
import com.mogo.commons.mvp.BaseFragment
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.callback.ISweeperTaskDataToFragmentCallback
import com.mogo.och.sweeper.common.database.bean.WeltDataBean
import com.mogo.och.sweeper.operate.callback.IWeltMapSwitchToSmallCallback
import kotlinx.android.synthetic.main.fragment_welt_map_overview.*
import kotlinx.android.synthetic.main.sweeper_welt_map_overview.*
/**
* 作业任务全览图
*/
class WeltMapOverViewFragment : BaseFragment(), ISweeperTaskDataToFragmentCallback {
private var mIWeltMapSwitchToSmallCallBack: IWeltMapSwitchToSmallCallback? = null
private var mFragment: SweeperFragment? = null
override fun getLayoutId(): Int = R.layout.fragment_welt_map_overview
override fun getTagName(): String {
return "WeltMapOverViewFragment"
}
override fun initViews() {
}
fun setWeltMapSwitchToSmallCallBack(iWeltMapSwitchToSmallCallback: IWeltMapSwitchToSmallCallback){
this.mIWeltMapSwitchToSmallCallBack=iWeltMapSwitchToSmallCallback
}
fun setSweeperFragment(fragment: SweeperFragment){
this.mFragment=fragment
mFragment?.setTaskDataToFragmentCallback(this)
}
override fun initViews(savedInstanceState: Bundle?) {
super.initViews(savedInstanceState)
weltMapOverView?.onCreateView(savedInstanceState)
sweeperSwitchToSmall.setOnClickListener {
mIWeltMapSwitchToSmallCallBack?.onWeltMapSwitchToSmall()
}
val bundle = arguments
if (bundle != null) {
val latLngs = bundle.getSerializable("subTaskCoordinates") as? ArrayList<LatLng>
val latLng = bundle.getParcelable<LatLng>("subTaskEndCoordinates")
val weltDataList = bundle.getSerializable("weltDataList") as? ArrayList<WeltDataBean>
val routeList = bundle.getSerializable("routeList") as? ArrayList<com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean.Result>
val progress = bundle.getString("progress")
routeList?.let {
weltMapOverView?.setRouteList(it)
}
latLng?.let {
setCurrentTaskCoordinatesLatLng(it)
}
latLngs?.let {
setTaskListCoordinatesLatLng(it)
}
weltDataList?.let {
setWeltData(it,"0.0cm")
}
progress?.let {
weltMapOverView?.setProgress(progress)
}
}
}
companion object {
@JvmStatic
fun newInstance(
mIWeltMapSwitchToSmallCallBack: IWeltMapSwitchToSmallCallback,
mCurrentTaskEndStation:LatLng?,
weltDataList: ArrayList<WeltDataBean>?,
latLngs: ArrayList<LatLng>?,
routeList: ArrayList<com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean.Result>?,
progress:String?,
sweeperFragment: SweeperFragment
): WeltMapOverViewFragment {
val args = Bundle()
args.putSerializable("weltDataList", weltDataList)
args.putSerializable("subTaskCoordinates", latLngs)
args.putParcelable("subTaskEndCoordinates", mCurrentTaskEndStation)
args.putSerializable("routeList", routeList)
args.putString("progress", progress)
val fragment = WeltMapOverViewFragment()
fragment.setWeltMapSwitchToSmallCallBack(mIWeltMapSwitchToSmallCallBack)
fragment.setSweeperFragment(sweeperFragment)
fragment.arguments = args
return fragment
}
}
override fun onPause() {
super.onPause()
weltMapOverView?.onPause()
}
override fun onResume() {
super.onResume()
weltMapOverView?.onResume()
}
override fun onDestroy() {
super.onDestroy()
weltMapOverView?.onDestroy()
}
override fun setTaskListCoordinatesLatLng(coordinatesLatLng: java.util.ArrayList<LatLng>) {
weltMapOverView?.setTaskListCoordinatesLatLng(coordinatesLatLng)
}
override fun setCurrentTaskCoordinatesLatLng(coordinatesLatLng: LatLng) {
weltMapOverView?.setCurrentTaskCoordinatesLatLng(coordinatesLatLng)
}
override fun clearAllMarkerAndPolyline() {
weltMapOverView?.clearAllMarkerAndPolyline()
}
override fun setProgress(progress: String) {
weltMapOverView?.setProgress(progress)
}
override fun setRouteList(routeList: java.util.ArrayList<com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean.Result>) {
weltMapOverView?.setRouteList(routeList)
}
override fun setWeltData(weltDatas: java.util.ArrayList<WeltDataBean>?, distance: String) {
weltMapOverView?.setWeltData(weltDatas,true,distance)
}
}

View File

@@ -0,0 +1,709 @@
package com.mogo.och.sweeper.cloud.model;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_SWEEPER;
import static com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils.printMessage;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.amap.api.maps.model.LatLng;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.module.status.IMogoStatusChangedListener;
import com.mogo.commons.module.status.MogoStatusManager;
import com.mogo.commons.module.status.StatusDescriptor;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningRottingListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoSweeperFutianCleanSystemListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoSweeperFutianCloudTaskListener;
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.CallerChassisLocationGCJ02ListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerSweeperFutianCleanSystemListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerSweeperFutianCloudTaskListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.common.module.manager.autopilotmanager.OCHAdasAbilityManager;
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil;
import com.mogo.och.common.module.utils.NumberFormatUtil;
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean;
import com.mogo.och.sweeper.cloud.callback.ISweeperCloudTaskCallback;
import com.mogo.och.sweeper.cloud.callback.ISweeperControllerStatusCallback;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import com.mogo.och.sweeper.common.database.MyDataBase;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import com.mogo.och.sweeper.cloud.util.SweeperCloudTaskUtils;
import com.zhjt.mogo.adas.data.AiCloudTask;
import com.zhjt.mogo.adas.data.sweeper.bootable.SweeperBootable;
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask;
import com.zhjt.mogo.adas.data.sweeper.task.big.SweeperBigTaskStatus;
import com.zhjt.mogo.adas.data.sweeper.task.cloud.s_r.SweeperTaskCloudSuspendResume;
import com.zhjt.mogo.adas.data.sweeper.task.confirm.SweeperTaskConfirm;
import com.zhjt.mogo.adas.data.sweeper.task.s_r.SweeperTaskSuspendResume;
import com.zhjt.mogo.adas.data.sweeper.task.status.SweeperTaskStatus;
import com.zhjt.mogo.adas.data.sweeper.task.stop.SweeperTaskStop;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import chassis.ChassisStatesOuterClass;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.functions.Consumer;
import io.reactivex.plugins.RxJavaPlugins;
import mogo.telematics.pad.MessagePad;
import mogo_msg.MogoReportMsg;
import planning.RoboSweeperTaskIndexOuterClass;
import system_master.SsmInfo;
import system_master.SystemStatusInfo;
/**
* @author congtaowang
* @since 2021/3/23
* <p>
* 清扫车任务管理
*/
public class SweeperTaskModel implements IMoGoSweeperFutianCloudTaskListener, IMoGoAutopilotStatusListener, IMoGoSweeperFutianCleanSystemListener {
private final String TAG = SweeperTaskModel.class.getSimpleName();
private static volatile SweeperTaskModel sInstance;
public double mLongitude = 0;
public double mLatitude = 0;
private Context mContext;
private ISweeperControllerStatusCallback mControllerStatusCallback; //Model->PresenterVR mode等
private ISweeperCloudTaskCallback mSweeperTaskCallback;
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> points = new ArrayList<>();//全路径信息
private SweeperTask.TaskModel mSubTaskType = SweeperTask.TaskModel.DEFAULT_MODEL; //1自动驾驶 2.人工驾驶 0:无效值
private int mCurrentLineId = 0; //当前路线id
private int mPreLineId = -1;//上一次存储的路线id
//自动驾驶状态
private int mAutopilotState = IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE;
//当前子任务id
private String mSubTaskId = "";
// 清扫模式回调时间间隔
private static final long VEHICLE_STATE_INTERVAL_MILLIS = 500L;
// 清扫模式当前时间戳
private long mVehicleStateCurrentTimeMillis;
// 贴边数据回调时间间隔
private static final long WELT_DATA_INTERVAL_MILLIS = 1000L;
// 贴边数据当前时间戳
private long mWeltDataCurrentTimeMillis;
// 轨迹数据回调时间间隔
private static final long ROUTE_DATA_INTERVAL_MILLIS = 1000L;
// 轨迹数据当前时间戳
private long mRouteDataCurrentTimeMillis;
private String longitude;//经度
private String latitude;//纬度
private boolean isHasTaskInfo = false;//判断是否有任务数据
//用于对应messageType和reqNo绑定在一起保证请求的reqNo和响应的reqNo一致
private HashMap<Integer, String> msgTypeAndReqNo = new HashMap<>();
public static SweeperTaskModel getInstance() {
if (sInstance == null) {
synchronized (SweeperTaskModel.class) {
if (sInstance == null) {
sInstance = new SweeperTaskModel();
}
}
}
return sInstance;
}
private SweeperTaskModel() {
}
public void init() {
mContext = AbsMogoApplication.getApp();
// 定位监听
CallerChassisLocationGCJ02ListenerManager.INSTANCE.addListener(TAG, mMapLocationListener);
MogoStatusManager.getInstance().registerStatusChangedListener(TAG, StatusDescriptor.VR_MODE, mMogoStatusChangedListener);
//自动驾驶路线规划接口
CallerPlanningRottingListenerManager.INSTANCE.addListener(TAG, moGoAutopilotPlanningListener);
//添加清扫车云控任务回调监听
CallerSweeperFutianCloudTaskListenerManager.INSTANCE.addListener(TAG, this);
//自动驾驶状态监听
CallerAutoPilotStatusListenerManager.INSTANCE.addListener(TAG, this);
//清扫车模式和贴边数据回调监听
CallerSweeperFutianCleanSystemListenerManager.INSTANCE.addListener(TAG, this);
//2022.1.28
// 调用Disposable.dispose() 时候会出现InterruptedException 导致出现崩溃
// The exception could not be delivered to the consumer because it has already canceled/disposed
// the flow or the excTeption has nowhere to go to begin with
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) {
if (e instanceof UndeliverableException) {
e = e.getCause();
CallerLogger.d(M_SWEEPER + TAG, "UndeliverableException");
}
if ((e instanceof IOException)) {//
// fine, irrelevant network problem or API that throws on cancellation
CallerLogger.d(M_SWEEPER + TAG, "IOException");
return;
}
if (e instanceof InterruptedException) {
// fine, some blocking code was interrupted by a dispose call
CallerLogger.d(M_SWEEPER + TAG, "InterruptedException");
return;
}
if ((e instanceof NullPointerException) || (e instanceof IllegalArgumentException)) {
// that's likely a bug in the application
CallerLogger.d(M_SWEEPER + TAG, "NullPointerException or IllegalArgumentException");
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
return;
}
if (e instanceof IllegalStateException) {
// that's a bug in RxJava or in a custom operator
CallerLogger.d(M_SWEEPER + TAG, "IllegalStateException");
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
return;
}
CallerLogger.d(M_SWEEPER + TAG, "Undeliverable exception");
}
});
}
public void setControllerStatusCallback(ISweeperControllerStatusCallback callback) {
this.mControllerStatusCallback = callback;
}
public void setSweeperTaskCallback(ISweeperCloudTaskCallback callback) {
this.mSweeperTaskCallback = callback;
}
private final IMoGoPlanningRottingListener moGoAutopilotPlanningListener = new IMoGoPlanningRottingListener() {
@Override
public void onAutopilotRotting(MessagePad.GlobalPathResp routeList) {
if (null != routeList && routeList.getWayPointsList() != null && routeList.getWayPointsList().size() > 0) {
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotRotting: " + SweeperCloudTaskUtils.printMessage(routeList));
// MAP返回路线信息在1秒内返回了2次这里加上数据过滤
long current = System.currentTimeMillis();
if (current - mRouteDataCurrentTimeMillis <= ROUTE_DATA_INTERVAL_MILLIS) {
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotRotting: interval <=" + ROUTE_DATA_INTERVAL_MILLIS + " data is abandon");
return;
}
mRouteDataCurrentTimeMillis = current;
if (mCurrentLineId != mPreLineId) {//判断是否同一条路线
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotRotting: mCurrentLineId" + mCurrentLineId + "mPreLineId" + mPreLineId);
mPreLineId = mCurrentLineId;
points.addAll(coordinateConverterWgsToGcjList(mContext, routeList.getWayPointsList()));
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.setRouteList(points);
}
}
}
}
};
public void setSubtask(String subTaskId, SweeperTask.TaskModel subTaskType, int currentLineId) {
this.mSubTaskId = subTaskId;
this.mSubTaskType = subTaskType;
this.mCurrentLineId = currentLineId;
}
/**
* 清除路线缓存数据
*/
public void clearRouteList() {
points.clear();
mSubTaskType = SweeperTask.TaskModel.UNRECOGNIZED;
mCurrentLineId = 0;
mPreLineId = -1;
}
/**
* 重新设置上一次的lineId防止同一个轨迹同一个lineid时不绘制轨迹
*/
public void setPreLineId(int preLineId) {
this.mPreLineId = preLineId;
}
/**
* 判断是否有任务数据
*/
public void isHasTaskInfo(boolean isHasTaskInfo) {
this.isHasTaskInfo = isHasTaskInfo;
}
/**
* 获取自动开始状态
*/
public int getAutopilotState() {
return this.mAutopilotState;
}
public static List<SweeperRoutePlanningUpdateReqBean.Result> coordinateConverterWgsToGcjList(
Context mContext,
List<MessagePad.Location> mogoLatLngList) {
List<SweeperRoutePlanningUpdateReqBean.Result> points = new ArrayList<>();
for (MessagePad.Location m : mogoLatLngList) {
LatLng mogoLatLng = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(mContext, m);
SweeperRoutePlanningUpdateReqBean.Result result = new SweeperRoutePlanningUpdateReqBean.Result();
result.latitude = mogoLatLng.latitude;
result.longitude = mogoLatLng.longitude;
points.add(result);
}
return points;
}
public void release() {
MogoStatusManager.getInstance().unregisterStatusChangedListener(TAG, StatusDescriptor.VR_MODE, mMogoStatusChangedListener);
// 注销定位监听
CallerChassisLocationGCJ02ListenerManager.INSTANCE.removeListener(TAG);
//自动驾驶路线规划接口
CallerPlanningRottingListenerManager.INSTANCE.removeListener(moGoAutopilotPlanningListener);
OCHAdasAbilityManager.getInstance().setAdasStartFailureCallback(null);
}
private Object readResolve() {
// 阻止反序列化,必须实现 Serializable 接口
return sInstance;
}
private final IMogoStatusChangedListener mMogoStatusChangedListener = new IMogoStatusChangedListener() {
// VR mode变更回调
@Override
public void onStatusChanged(StatusDescriptor descriptor, boolean isTrue) {
if (StatusDescriptor.VR_MODE == descriptor) {
if (mControllerStatusCallback != null) {
mControllerStatusCallback.onVRModeChanged(isTrue);
}
}
}
};
// 自车定位
private final IMoGoChassisLocationGCJ02Listener mMapLocationListener = new IMoGoChassisLocationGCJ02Listener() {
@Override
public void onChassisLocationGCJ02(@Nullable MogoLocation gnssInfo) {
if (null == gnssInfo) return;
if (mControllerStatusCallback != null) {
mControllerStatusCallback.onCarLocationChanged(gnssInfo);
}
if (mSubTaskType == SweeperTask.TaskModel.MANUAL) {//收集人工驾驶子任务坐标点
addCoordinates(gnssInfo);
}
}
};
/**
* 根据当前定位收集人工驾驶子任务轨迹点
*
* @param mogoLatLng
*/
private void addCoordinates(MogoLocation mogoLatLng) {
String tempLongitude = NumberFormatUtil.cutOutNumber(mogoLatLng.getLongitude(), 5);
String tempLatitude = NumberFormatUtil.cutOutNumber(mogoLatLng.getLatitude(), 5);
//用于过滤车是否停在原地,经纬度相同的情况
if (!tempLongitude.equals(mLongitude) && !tempLatitude.equals(mLatitude)) {
mLongitude = mogoLatLng.getLongitude();
mLatitude = mogoLatLng.getLatitude();
SweeperRoutePlanningUpdateReqBean.Result result = new SweeperRoutePlanningUpdateReqBean.Result();
result.latitude = mogoLatLng.getLatitude();
result.longitude = mogoLatLng.getLongitude();
points.add(result);
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.setRouteList(points);
}
}
}
@Override
public void onSweeperFutianCloudTask(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperTask.TaskInfo taskInfo) {
CallerLogger.d(M_SWEEPER + TAG,
"messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " onSweeperFutianCloudTask" + (taskInfo == null ? "null" : SweeperCloudTaskUtils.printMessage(taskInfo)) + " mSweeperTaskCallback" + mSweeperTaskCallback);
if (mSweeperTaskCallback != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudTask(messageType, taskInfo);
}
}
@Override
public void onSweeperFutianCloudTaskConfirm(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperTaskConfirm.TaskConfirm taskConfirm) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudTaskConfirm" + SweeperCloudTaskUtils.printMessage(taskConfirm));
if (mSweeperTaskCallback != null && taskConfirm != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudTaskConfirm(taskConfirm.getTaskId(), taskConfirm.getSubTaskId(), taskConfirm.getIsPop());
}
}
@Override
public void onSweeperFutianCloudTaskStatus(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperTaskStatus.TaskStatusPush taskStatusPush) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudTaskStatus" + SweeperCloudTaskUtils.printMessage(taskStatusPush));
if (mSweeperTaskCallback != null && taskStatusPush != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudTaskStatus(taskStatusPush.getTaskId(), taskStatusPush.getSubTaskId(), taskStatusPush.getTaskStatus());
}
}
@Override
public void onSweeperFutianCloudTaskStop(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperTaskStop.StopTaskReq stopTaskReq) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudTaskStop" + SweeperCloudTaskUtils.printMessage(stopTaskReq));
if (mSweeperTaskCallback != null && stopTaskReq != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudTaskStop(stopTaskReq.getTaskId(), stopTaskReq.getType(), stopTaskReq.getIsPop());
}
}
@Override
public void onSweeperFutianCloudTaskSuspendResume(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperTaskSuspendResume.SuspendResumeTaskResp suspendResumeTaskResp) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudTaskSuspendResume" + SweeperCloudTaskUtils.printMessage(suspendResumeTaskResp));
if (mSweeperTaskCallback != null && suspendResumeTaskResp != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudTaskSuspendResume(messageType, suspendResumeTaskResp.getTaskId(),
suspendResumeTaskResp.getSubTaskId(), suspendResumeTaskResp.getCode());
}
}
@Override
public void onSweeperFutianCloudBootable(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperBootable.IsBootableResp isBootableResp) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudBootable" + SweeperCloudTaskUtils.printMessage(isBootableResp));
if (mSweeperTaskCallback != null && isBootableResp != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudBootable(isBootableResp.getTaskId(), isBootableResp.getSubTaskId(), isBootableResp);
}
}
@Override
public void onSweeperFutianCloudBigTaskStatus(@NonNull AiCloudTask.MessageType messageType, String reqNo, long sysTime,
SweeperBigTaskStatus.BigTaskStatusPush bigTaskStatusPush) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudBigTaskStatus" + SweeperCloudTaskUtils.printMessage(bigTaskStatusPush));
if (mSweeperTaskCallback != null && bigTaskStatusPush != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudBigTaskStatus(bigTaskStatusPush.getTaskId(), bigTaskStatusPush.getTaskStatus(), bigTaskStatusPush.getIsPop());
}
}
@Override
public void onSweeperFutianCloudTaskCloudSuspendResume(@NonNull AiCloudTask.MessageType messageType, @Nullable String reqNo, long sysTime,
@Nullable SweeperTaskCloudSuspendResume.BigTaskActionPush bigTaskActionPush) {
CallerLogger.d(M_SWEEPER + TAG, "messageType: " + messageType.getNumber() + " sysTime" + sysTime + " reqNo" + reqNo + " " +
"onSweeperFutianCloudTaskCloudSuspendResume" + SweeperCloudTaskUtils.printMessage(bigTaskActionPush));
if (mSweeperTaskCallback != null && bigTaskActionPush != null) {
msgTypeAndReqNo.put(messageType.getNumber(), reqNo);
mSweeperTaskCallback.onSweeperCloudBigTaskSuspendResume(bigTaskActionPush.getTaskId(), bigTaskActionPush.getAction(), bigTaskActionPush.getIsPop());
}
}
@Override
public void onSweeperFutianCleanSystemState(@NonNull ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState) {
if (cleanSystemState == null) {
return;
}
long current = System.currentTimeMillis();
if (current - mVehicleStateCurrentTimeMillis <= VEHICLE_STATE_INTERVAL_MILLIS) {
return;
}
mVehicleStateCurrentTimeMillis = current;
// CallerLogger.d(M_SWEEPER + TAG, "onSweeperFutianCleanSystemState" + printMessage(cleanSystemState));
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.onSweeperFutianCleanSystemState(cleanSystemState);
}
}
@Override
public void onSweeperFutianTaskIndexData(@NonNull RoboSweeperTaskIndexOuterClass.RoboSweeperTaskIndex roboSweeperTaskIndex) {
// 20230728 因小地图不展示了,贴边数据先不处理了,浪费资源
// if (roboSweeperTaskIndex == null) {
// return;
// }
//
// long current = System.currentTimeMillis();
// if (current - mWeltDataCurrentTimeMillis <= WELT_DATA_INTERVAL_MILLIS) {
// return;
// }
// mWeltDataCurrentTimeMillis = current;
//
// //没有任务数据时,不处理贴边
// if (!isHasTaskInfo) {
// return;
// }
//
// CallerLogger.INSTANCE.d(M_SWEEPER + TAG,
// "mSubTaskType" + mSubTaskType + "+ onSweeperFutianTaskIndexData" + printMessage(roboSweeperTaskIndex));
// handleWeltData(roboSweeperTaskIndex);
}
/**
* 贴边数据处理
*/
private void handleWeltData(RoboSweeperTaskIndexOuterClass.RoboSweeperTaskIndex roboSweeperTaskIndex) {
String tempLongitude = NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLon(), 6);
String tempLatitude = NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLat(), 6);
//用于过滤车是否停在原地,经纬度相同的情况
if (!tempLongitude.equals(longitude) && !tempLatitude.equals(latitude)) {
latitude = NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLat(), 6);
longitude = NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLon(), 6);
//保存贴边数据到数据库中
WeltDataBean weltDataBean = new WeltDataBean();
//把wgs坐标系坐标转换成gcj坐标
LatLng latLng = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(AbsMogoApplication.getApp(), roboSweeperTaskIndex.getLocLon(),
roboSweeperTaskIndex.getLocLat());
weltDataBean.setLocLon(latLng.longitude);
weltDataBean.setLocLat(latLng.latitude);
int edgeCleanState = roboSweeperTaskIndex.getEdgeCleanState();
if (edgeCleanState == 0 ||
(mSubTaskType == SweeperTask.TaskModel.MANUAL)) {
//人工子任务 当前因为云控需求时MAP不能获取任务状态在人工子任务时还是返回的上一个任务的贴边数据
// 所以 20230529 暂时过滤掉待后面MAP修改后这里可删除这里人工子任务默认都是非贴边
weltDataBean.setWeltDistance(SweeperConst.NONWELT);
} else {
weltDataBean.setWeltDistance(roboSweeperTaskIndex.getDistToRefEdgePoint());
}
weltDataBean.setCleanMode(roboSweeperTaskIndex.getCleanMode());
weltDataBean.setCleanDirection(roboSweeperTaskIndex.getCleanDirection());
weltDataBean.setCleanIntensity(roboSweeperTaskIndex.getCleanIntensity());
weltDataBean.setSubTaskId(mSubTaskId);
MyDataBase.getInstance().getWeltDataDao().insert(weltDataBean);
String distance;
if (roboSweeperTaskIndex.getDistToRefEdgePoint() >= 1.0) {//大于等于1m
distance = format(roboSweeperTaskIndex.getDistToRefEdgePoint()) + "m";
} else {//小于1m
distance = Math.round(roboSweeperTaskIndex.getDistToRefEdgePoint() * 100) + "cm";//m->cm 四舍五入到整数
}
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.setWeltDataToMap((ArrayList<WeltDataBean>) MyDataBase.getInstance().getWeltDataDao().loadAllWeltDataInfo(),
true, distance);
}
}
}
private static String format(double value) {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(2, RoundingMode.HALF_UP);
return bd.toString();
}
/**
* 获取当前正在执行的任务
*/
public void getCurrentTask() {
SweeperTask.GetTaskReq.Builder builder = SweeperTask.GetTaskReq.newBuilder();
builder.setSn(getDriverSn());
String reqNo = SweeperCloudTaskUtils.getRequestId();
SweeperTask.GetTaskReq getTaskReq = builder.build();
CallerLogger.d(M_SWEEPER + TAG,
"getCurrentTask:" + SweeperCloudTaskUtils.printMessage(getTaskReq) + " reqNo:" + reqNo + " messageType:" + AiCloudTask.MessageType.PadSendGetTaskReq.getNumber());
getAutoPilotControlManager().sendSweeperGetTaskReq(reqNo, getTaskReq);
}
/**
* 接取任务响应
*/
public void sendSweeperStartTaskResp(String taskId, SweeperTask.StartTaskCode code) {
SweeperTask.StartTaskResp.Builder builder = SweeperTask.StartTaskResp.newBuilder();
builder.setSn(getDriverSn());
builder.setTaskId(taskId);
builder.setCode(code);
SweeperTask.StartTaskResp startTaskResp = builder.build();
String reqNo = msgTypeAndReqNo.get(AiCloudTask.MessageType.CloudPushTask.getNumber());
getAutoPilotControlManager().sendSweeperStartTaskResp(reqNo, startTaskResp);
CallerLogger.d(M_SWEEPER + TAG,
"sendSweeperStartTaskResp:" + SweeperCloudTaskUtils.printMessage(startTaskResp) + " reqNo:" + reqNo + " messageType:" + AiCloudTask.MessageType.CloudPushTask.getNumber());
}
/**
* 子任务开始确认
*/
public void sendSweeperTaskConfirmResp(String taskId, String subTaskId, SweeperTaskConfirm.TaskConfirmCode code) {
SweeperTaskConfirm.TaskConfirmResp.Builder builder = SweeperTaskConfirm.TaskConfirmResp.newBuilder();
builder.setSn(getDriverSn());
builder.setTaskId(taskId);
builder.setSubTaskId(subTaskId);
builder.setCode(code);
SweeperTaskConfirm.TaskConfirmResp taskConfirmResp = builder.build();
String reqNo = msgTypeAndReqNo.get(AiCloudTask.MessageType.CloudPushTaskConfirm.getNumber());
CallerLogger.d(M_SWEEPER + TAG,
"sendSweeperTaskConfirmResp" + SweeperCloudTaskUtils.printMessage(taskConfirmResp) + " reqNo:" + reqNo + " messageType:" + AiCloudTask.MessageType.CloudPushTaskConfirm.getNumber());
getAutoPilotControlManager().sendSweeperTaskConfirmResp(reqNo, taskConfirmResp);
}
/**
* 大任务结束确认
*
* @param taskId
* @param code
*/
public void sendSweeperStopTaskResp(String taskId, SweeperTaskStop.StopTaskCode code) {
SweeperTaskStop.StopTaskResp.Builder builder = SweeperTaskStop.StopTaskResp.newBuilder();
builder.setSn(getDriverSn());
builder.setTaskId(taskId);
builder.setCode(code);
SweeperTaskStop.StopTaskResp stopTaskResp = builder.build();
String reqNo = msgTypeAndReqNo.get(AiCloudTask.MessageType.CloudPushTaskStop.getNumber());
CallerLogger.d(M_SWEEPER + TAG,
"sendSweeperStopTaskResp" + SweeperCloudTaskUtils.printMessage(stopTaskResp) + " reqNo:" + reqNo + " messageType:" + AiCloudTask.MessageType.CloudPushTaskStop.getNumber());
getAutoPilotControlManager().sendSweeperStopTaskResp(reqNo, stopTaskResp);
}
/**
* pad上报是否可以进自驾
*
* @param taskId
* @param subTaskId
* @param lineId
*/
public void sendSweeperAutopilotBootable(String taskId, String subTaskId, long lineId) {
SweeperBootable.IsBootable.Builder builder = SweeperBootable.IsBootable.newBuilder();
builder.setSn(getDriverSn());
builder.setTaskId(taskId);
builder.setSubTaskId(subTaskId);
builder.setLineId(lineId);
SweeperBootable.IsBootable isBootable = builder.build();
String reqNo = SweeperCloudTaskUtils.getRequestId();
CallerLogger.d(M_SWEEPER + TAG,
"sendSweeperAutopilotBootable" + SweeperCloudTaskUtils.printMessage(isBootable) + " reqNo:" + reqNo + " messageType:" + AiCloudTask.MessageType.PadSendBootable.getNumber());
getAutoPilotControlManager().sendSweeperAutopilotBootable(reqNo, isBootable);
//SweeperCloudTaskUtils.mockSendCloudBootable();
}
/**
* 大任务暂停/恢复 接收到指令到 回复
* @param taskId
* @param code
*/
public void sendSweeperSuspendResumeTaskResp(String taskId, SweeperTaskCloudSuspendResume.Code code) {
SweeperTaskCloudSuspendResume.BigTaskActionResp.Builder builder = SweeperTaskCloudSuspendResume.BigTaskActionResp.newBuilder();
builder.setSn(getDriverSn());
builder.setTaskId(taskId);
builder.setCode(code);
SweeperTaskCloudSuspendResume.BigTaskActionResp resp = builder.build();
String reqNo = msgTypeAndReqNo.get(AiCloudTask.MessageType.CloudPushTaskSuspendResume.getNumber());
CallerLogger.d(M_SWEEPER + TAG,
"sendSweeperSuspendResumeTaskResp" + SweeperCloudTaskUtils.printMessage(resp) + " reqNo:" + reqNo + " messageType:" + AiCloudTask.MessageType.CloudPushTaskSuspendResume);
getAutoPilotControlManager().sendSweeperCloudSuspendResumeTaskResp(reqNo, resp);
}
public CallerAutoPilotControlManager getAutoPilotControlManager() {
return CallerAutoPilotControlManager.INSTANCE;
}
public String getDriverSn() {
return MoGoAiCloudClientConfig.getInstance().getSn();
}
@Override
public void onAutopilotStatusResponse(@NonNull AutopilotStatusInfo autoPilotStatusInfo) {
}
@Override
public void onAutopilotArriveAtStation(@Nullable MessagePad.ArrivalNotification arrivalNotification) {
}
@Override
public void onAutopilotSNRequest() {
}
@Override
public void onAutopilotGuardian(@Nullable MogoReportMsg.MogoReportMessage guardianInfo) {
}
@Override
public void onAutopilotIpcConnectStatusChanged(int status, @Nullable String reason) {
if (status == 0x00) {//判断工控机重连后,获取当前正在执行的子任务,延时10秒防止工控机连接上时SN为空
CallerAutoPilotControlManager.INSTANCE.setIsSubscribeBackCameraVideoVideo(0, false);//TODO 当前3.2.0版本产品未规划后摄像头展示功能
UiThreadHandler.postDelayed(() -> {
getCurrentTask();
}, 10000);
}
//CallerLogger.d(M_SWEEPER + TAG, "onAutopilotIpcConnectStatusChanged status:" + status + " reason" + reason);
}
@Override
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
// CallerLogger.d(M_SWEEPER + TAG, "onAutopilotStatusRespByQuery status:" + status.getSysState().getNumber());
}
@Override
public void onSystemStatus(@NonNull SsmInfo.SsmStatusInf statusInf) {
}
@Override
public void onAutopilotRouteLineId(long lineId) {
}
/**
* 云控数据异常或者工控机传递数据异常
*
* @param bytes
*/
@Override
public void onSweeperFutianCloudTaskUnknown(@NonNull byte[] bytes) {
String string = new String(bytes, StandardCharsets.UTF_8);
CallerLogger.d(M_SWEEPER + TAG, "onSweeperFutianCloudTaskUnknown:" + string);
}
@Override
public void onAutopilotStatusResponse(int state) {
//CallerLogger.d(M_SWEEPER + TAG, "onAutopilotStatusResponse state:" + state);
this.mAutopilotState = state;
if (mControllerStatusCallback != null) {
mControllerStatusCallback.onAutopilotState(state);
}
switch (state) {
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE://不可自动驾驶
break;
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE://人工驾驶
break;
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING://自动驾驶中
break;
case IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING://平行驾驶
break;
default:
break;
}
}
@Override
public void onAutopilotDockerInfo(@NonNull String dockerVersion) {
}
}

View File

@@ -0,0 +1,242 @@
package com.mogo.och.sweeper.cloud.presenter;
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.call.autopilot.CallerSweeperFutianCleanSystemListenerManager;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.och.common.module.manager.autopilotmanager.OCHAdasAbilityManager;
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean;
import com.mogo.och.sweeper.cloud.callback.ISweeperCloudTaskCallback;
import com.mogo.och.sweeper.cloud.callback.ISweeperControllerStatusCallback;
import com.mogo.och.sweeper.common.callback.ISweeperTaskRouteCallback;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import com.mogo.och.sweeper.cloud.fragment.SweeperFragment;
import com.mogo.och.sweeper.cloud.model.SweeperTaskModel;
import com.zhjt.mogo.adas.data.AiCloudTask;
import com.zhjt.mogo.adas.data.sweeper.bootable.SweeperBootable;
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon;
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask;
import com.zhjt.mogo.adas.data.sweeper.task.cloud.s_r.SweeperTaskCloudSuspendResume;
import com.zhjt.mogo.adas.data.sweeper.task.confirm.SweeperTaskConfirm;
import com.zhjt.mogo.adas.data.sweeper.task.stop.SweeperTaskStop;
import java.util.ArrayList;
import chassis.ChassisStatesOuterClass;
/**
* 网约车小巴
*
* @author tongchenfei
*/
public class SweeperPresenter extends Presenter<SweeperFragment>
implements ISweeperControllerStatusCallback,
ISweeperCloudTaskCallback, ISweeperTaskRouteCallback {
private static final String TAG = "SweeperPresenter";
public SweeperPresenter(SweeperFragment view) {
super(view);
SweeperTaskModel.getInstance().init();
OCHAdasAbilityManager.getInstance().init(AbsMogoApplication.getApp());
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
super.onCreate(owner);
initModelListener();
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
super.onDestroy(owner);
SweeperTaskModel.getInstance().release();
releaseListener();
}
public void initModelListener() {
SweeperTaskModel.getInstance().setControllerStatusCallback(this);
SweeperTaskModel.getInstance().setSweeperTaskCallback(this);
}
public void releaseListener() {
SweeperTaskModel.getInstance().setControllerStatusCallback(null);
SweeperTaskModel.getInstance().setSweeperTaskCallback(null);
CallerSweeperFutianCleanSystemListenerManager.INSTANCE.removeListener(TAG);
}
@Override
public void onVRModeChanged(boolean isVRMode) {
ThreadUtils.runOnUiThread(() -> mView.onVRModeChanged(isVRMode));
}
@Override
public void onCarLocationChanged(MogoLocation location) {
if (null != location) {
ThreadUtils.runOnUiThread(() -> mView.updateSpeedView((float) location.getGnssSpeed()));
ThreadUtils.runOnUiThread(() -> mView.setCurrentLocation(location));
}
}
@Override
public void onAutopilotState(int state) {
ThreadUtils.runOnUiThread(() -> mView.setAutoState(state));
}
/**
* 获取自动驾驶状态
*
* @return
*/
public int getAutopilotState() {
return SweeperTaskModel.getInstance().getAutopilotState();
}
/**
* 清除路线缓存数据
*/
public void clearRouteList() {
SweeperTaskModel.getInstance().clearRouteList();
}
/**
* 设置当前子任务信息
*/
public void setSubtask(String subTaskId, SweeperTask.TaskModel subTaskType, int currentLineId) {
SweeperTaskModel.getInstance().setSubtask(subTaskId, subTaskType, currentLineId);
}
/**
* 判断是否有任务数据
*/
public void isHasTaskInfo(boolean isHasTaskInfo) {
SweeperTaskModel.getInstance().isHasTaskInfo(isHasTaskInfo);
}
/**
* 设置上一个任务的lineId
* @param preLineId
*/
public void setPreLineId(int preLineId) {
SweeperTaskModel.getInstance().setPreLineId(preLineId);
}
/**
* 获取当前正在执行的任务
*/
public void getCurrentTask() {
SweeperTaskModel.getInstance().getCurrentTask();
}
/**
* 接取任务弹窗响应
*
* @param taskId
* @param code
*/
public void sendSweeperStartTaskResp(String taskId, SweeperTask.StartTaskCode code) {
SweeperTaskModel.getInstance().sendSweeperStartTaskResp(taskId, code);
}
/**
* 子任务弹窗确认
*
* @param taskId
* @param subTaskId
* @param code
*/
public void sendSweeperTaskConfirmResp(String taskId, String subTaskId, SweeperTaskConfirm.TaskConfirmCode code) {
SweeperTaskModel.getInstance().sendSweeperTaskConfirmResp(taskId, subTaskId, code);
}
/**
* 大任务结束弹窗确认
*
* @param taskId
* @param code
*/
public void sendSweeperStopTaskResp(String taskId, SweeperTaskStop.StopTaskCode code) {
SweeperTaskModel.getInstance().sendSweeperStopTaskResp(taskId, code);
}
/**
* pad上报是否可以进自驾
*
* @param taskId
* @param subTaskId
* @param lineId
*/
public void sendSweeperAutopilotBootable(String taskId, String subTaskId, long lineId) {
SweeperTaskModel.getInstance().sendSweeperAutopilotBootable(taskId, subTaskId, lineId);
}
/**
* 大任务暂停/恢复 接收到指令到 回复
* @param taskId
* @param code
*/
public void sendSweeperSuspendResumeTaskResp(String taskId, SweeperTaskCloudSuspendResume.Code code) {
SweeperTaskModel.getInstance().sendSweeperSuspendResumeTaskResp(taskId, code);
}
@Override
public void setRouteList(@NonNull ArrayList<SweeperRoutePlanningUpdateReqBean.Result> routeList) {
mView.setTaskRouteList(routeList);
}
@Override
public void onSweeperCloudTask(@NonNull AiCloudTask.MessageType messageType, SweeperTask.TaskInfo taskInfo) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperCloudTask(messageType, taskInfo));
}
@Override
public void onSweeperCloudTaskConfirm(@NonNull String taskId, @NonNull String subTaskId, boolean isPop) {
ThreadUtils.runOnUiThread(() -> mView.handleSubTaskInfoDialog(taskId, subTaskId, isPop));
}
@Override
public void onSweeperCloudTaskStatus(@NonNull String taskId, @NonNull String subTaskId, SweeperCommon.TaskStatus subTaskStatus) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperUpdateSubTaskStatus(taskId, subTaskId, subTaskStatus));
}
@Override
public void onSweeperCloudTaskStop(@NonNull String taskId, SweeperTaskStop.StopTaskType stopTaskType, boolean isPop) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperCloudTaskStop(taskId, stopTaskType, isPop));
}
@Override
public void onSweeperCloudTaskSuspendResume(@NonNull AiCloudTask.MessageType messageType, @NonNull String taskId,
@NonNull String subTaskId, SweeperCommon.Code code) {
}
@Override
public void onSweeperCloudBootable(@NonNull String taskId, @NonNull String subTaskId, SweeperBootable.IsBootableResp isBootableResp) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperCloudAutopilotBootable(taskId, subTaskId, isBootableResp));
}
@Override
public void onSweeperCloudBigTaskStatus(@NonNull String taskId, SweeperCommon.TaskStatus bigTaskStatus, boolean isPop) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperCloudBigTaskStatus(taskId, bigTaskStatus, isPop));
}
@Override
public void onSweeperCloudBigTaskSuspendResume(@NonNull String taskId, @NonNull SweeperTaskCloudSuspendResume.ActionType actionType, boolean isPop) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperCloudBigTaskSuspendOrResume(taskId, actionType, isPop));
}
@Override
public void setWeltDataToMap(@NonNull ArrayList<WeltDataBean> weltDataBeans, boolean isWeltData, @NonNull String distance) {
ThreadUtils.runOnUiThread(() -> mView.setWeltDataToMap(weltDataBeans, isWeltData, distance));
}
@Override
public void onSweeperFutianCleanSystemState(@NonNull ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState) {
ThreadUtils.runOnUiThread(() -> mView.onSweeperFutianCleanSystemState(cleanSystemState));
}
}

View File

@@ -0,0 +1,132 @@
package com.mogo.och.sweeper.cloud.ui.dialog
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.content.Context
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.eagle.core.utilcode.util.ClickUtils
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.cloud.view.CountDownView
/**
* 清扫车云控任务dialog基类
*/
class SweeperCloudDialog : BaseFloatDialog, LifecycleObserver {
private var commonTitle: TextView? = null //标题
private var commonLeft: TextView? = null//底部左边按钮
private var commonMiddle: TextView? = null //底部中间按钮
private var commonRight: TextView? = null //底部右边按钮
private var commonCountDown: CountDownView? = null //倒计时
private var commonContent: TextView? = null //内容
private var commonTip: TextView? = null //文本提示
private var countDownImage: ImageView? = null //内容
private var objectAnimator: ObjectAnimator? = null
constructor(builder: Builder, context: Context) : super(context) {
commonTitle?.text = builder.titleStr
commonContent?.text = builder.contentStr
if (builder.tipStr == "") {
commonTip?.visibility = View.GONE
} else {
commonTip?.text = builder.tipStr
}
if (builder.leftStr == "") {
commonLeft?.visibility = View.GONE
} else {
commonLeft?.text = builder.leftStr
}
if (builder.middleStr == "") {
commonMiddle?.visibility = View.GONE
} else {
commonMiddle?.text = builder.middleStr
}
if (builder.rightStr == "") {
commonRight?.visibility = View.GONE
} else {
commonRight?.text = builder.rightStr
}
if (builder.countDownTime == 0) {
commonCountDown?.visibility = View.GONE
} else {
commonCountDown?.startCountDown(builder.countDownTime)
}
commonLeft?.setOnClickListener {
//防止重复点击
if(ClickUtils.isFastClick()){
builder.listener?.onConfirm()
objectAnimator?.cancel()
commonCountDown?.stopCountDown()
dismiss()
}
}
commonMiddle?.setOnClickListener {
//防止重复点击
if(ClickUtils.isFastClick()){
builder.listener?.onNext()
objectAnimator?.cancel()
commonCountDown?.stopCountDown()
dismiss()
}
}
commonRight?.setOnClickListener {
//防止重复点击
if(ClickUtils.isFastClick()){
builder.listener?.onRefuseOrEnd()
objectAnimator?.cancel()
commonCountDown?.stopCountDown()
dismiss()
}
}
commonCountDown?.setCountDownListener(object : CountDownView.CountDownListener {
override fun stop() {
builder.listener?.onCountDownStop()
objectAnimator?.cancel()
commonCountDown?.stopCountDown()
dismiss()
}
})
countDownImage?.let { startAnima(it) }
}
init {
setContentView(R.layout.dialog_sweeper_cloud_view)
setCanceledOnTouchOutside(false)
commonTitle = findViewById(R.id.sweeper_cloud_title)
commonContent = findViewById(R.id.sweeper_cloud_content)
commonTip = findViewById(R.id.sweeper_cloud_tip)
commonLeft = findViewById(R.id.sweeper_cloud_left)
commonMiddle = findViewById(R.id.sweeper_cloud_middle)
commonRight = findViewById(R.id.sweeper_cloud_right)
countDownImage = findViewById(R.id.sweeper_cloud_imageview)
commonCountDown = findViewById(R.id.sweeper_cloud_countdown)
}
class Builder {
var titleStr: String = ""
var contentStr: String = ""
var tipStr: String = ""
var leftStr: String = ""
var middleStr: String = ""
var rightStr: String = ""
var countDownTime: Int = 0
var listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener? = null
fun build(context: Context): SweeperCloudDialog {
return SweeperCloudDialog(this, context)
}
}
/**
* 启动动画
*/
private fun startAnima(imageView: ImageView) {
objectAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 359f)
objectAnimator?.repeatCount = ValueAnimator.INFINITE
objectAnimator?.duration = 1500
objectAnimator?.interpolator = LinearInterpolator()
objectAnimator?.start()
}
}

View File

@@ -0,0 +1,75 @@
package com.mogo.och.sweeper.cloud.ui.dialog
import android.animation.ObjectAnimator
import android.content.Context
import android.view.animation.LinearInterpolator
import androidx.lifecycle.LifecycleObserver
import com.elegant.utils.UiThreadHandler
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.dialog_sweeper_cloud_loading.*
/**
* loading
*/
class SweeperCloudLoadingDialog : BaseFloatDialog, LifecycleObserver {
private var objectAnimator: ObjectAnimator? = null
private val mLoadingView by lazy { dialog_loading_view }
private val mLoadingText by lazy { dialog_loading_text }
private var mRunnable:Runnable= Runnable {
ToastUtils.showLong("超时未响应,操作失败")
hideLoading()
}
constructor(context: Context) : super(context)
init {
setContentView(R.layout.dialog_sweeper_cloud_loading)
setCanceledOnTouchOutside(false)
}
/**
* 开始旋转
*/
private fun startRotation() {
objectAnimator = ObjectAnimator.ofFloat(mLoadingView, "rotation", -720f, 0f)
objectAnimator?.let {
it.duration = 3000
it.repeatCount = -1
it.interpolator = LinearInterpolator()
it.start()
}
}
/**
* 停止旋转
*/
private fun stopRotation() {
objectAnimator?.let {
if (it.isRunning) {
it.end()
objectAnimator = null
}
}
}
/**
* 显示dialog
*/
fun showLoading() {
mLoadingText.text = "加载中..."
startRotation()
show()
UiThreadHandler.getsUiHandler().postDelayed(mRunnable, 15000L)
}
/**
* 隐藏dialog
*/
fun hideLoading() {
UiThreadHandler.getsUiHandler().removeCallbacks(mRunnable)
stopRotation()
dismiss()
}
}

View File

@@ -0,0 +1,70 @@
package com.mogo.och.sweeper.cloud.ui.dialog
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.och.sweeper.R
/**
* 不带带有title, tip,confirm,cancel的dialog
*/
class SweeperManualDrivingDialog: BaseFloatDialog, LifecycleObserver {
private var commonConfirm : TextView? = null
private var commonTips : TextView? = null
private var clickListener: ClickListener? = null
constructor(builder: Builder, context: Context) : super(context) {
commonTips?.text = builder.tipsStr
commonConfirm?.text = builder.confirmStr
}
init{
setContentView(R.layout.dialog_sweeper_manual_driving)
setCanceledOnTouchOutside(false)
commonConfirm = findViewById(R.id.sweeper_common_confirm)
commonTips = findViewById(R.id.sweeper_common_tips)
commonConfirm?.setOnClickListener{
clickListener?.confirm()
dismiss()
}
}
fun setClickListener(clickListener: ClickListener) {
this.clickListener = clickListener
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
interface ClickListener{
fun confirm()
}
class Builder{
var tipsStr:String = ""
var confirmStr:String = ""
fun tips(tips: String) : Builder {
this.tipsStr = tips
return this
}
fun confirmStr(commit: String) : Builder {
this.confirmStr = commit
return this
}
fun build(context: Context): SweeperManualDrivingDialog? {
return SweeperManualDrivingDialog(this,context)
}
}
}

View File

@@ -0,0 +1,85 @@
package com.mogo.och.sweeper.cloud.ui.dialog
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.och.sweeper.R
/**
* 不带带有title, tip,confirm,cancel的dialog
*/
class SweeperNoTitleCommonDialog: BaseFloatDialog, LifecycleObserver {
private var commonConfirm : TextView? = null
private var commonCancel : TextView? = null
private var commonTips : TextView? = null
private var clickListener: ClickListener? = null
constructor(builder: Builder, context: Context) : super(context) {
commonTips?.text = builder.tipsStr
commonCancel?.text = builder.cancelStr
commonConfirm?.text = builder.confirmStr
}
init{
setContentView(R.layout.dialog_sweeper_no_title)
setCanceledOnTouchOutside(true)
commonConfirm = findViewById(R.id.sweeper_common_confirm)
commonCancel = findViewById(R.id.sweeper_common_cancel)
commonTips = findViewById(R.id.sweeper_common_tips)
commonConfirm?.setOnClickListener{
clickListener?.confirm()
dismiss()
}
commonCancel?.setOnClickListener {
clickListener?.cancel()
dismiss()
}
}
fun setClickListener(clickListener: ClickListener) {
this.clickListener = clickListener
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
interface ClickListener{
fun confirm()
fun cancel()
}
class Builder{
var tipsStr:String = ""
var confirmStr:String = ""
var cancelStr:String = ""
fun tips(tips: String) : Builder {
this.tipsStr = tips
return this
}
fun confirmStr(commit: String) : Builder {
this.confirmStr = commit
return this
}
fun cancelStr(cancel: String) : Builder {
this.cancelStr = cancel
return this
}
fun build(context: Context): SweeperNoTitleCommonDialog? {
return SweeperNoTitleCommonDialog(this,context)
}
}
}

View File

@@ -0,0 +1,59 @@
package com.mogo.och.sweeper.cloud.ui.popwindow
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.PopupWindow
import chassis.ChassisStatesOuterClass
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.constant.OperateStateEnum
import com.mogo.och.sweeper.cloud.ui.SweeperOperatePanelView
import com.mogo.och.sweeper.common.callback.ICleaningModeStateCallback
/**
* 清扫车面板浮窗
*/
class SweeperOperatePanelPopWindow : PopupWindow{
private var mOperatePanelView: SweeperOperatePanelView? = null
constructor(context: Context) : super(context) {
init(context)
}
private fun init(context: Context) {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
mOperatePanelView = LayoutInflater.from(context).inflate(R.layout.sweeper_cloud_popwindow_operate_panel, null) as SweeperOperatePanelView?
width = ViewGroup.LayoutParams.WRAP_CONTENT
height = ViewGroup.LayoutParams.WRAP_CONTENT
contentView = mOperatePanelView
}
/**
* 设置清扫模式数据
*/
fun setCleanSystemState(
cleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates?,
cleaningModeStateCallback: ICleaningModeStateCallback
) {
mOperatePanelView?.setSweeperFutianCleanSystemState(cleanSystemState, cleaningModeStateCallback)
}
/**.
*
* 设置是否让popWindow消失
*/
fun setIsOutsideTouchable(isOutsideTouchable:Boolean){
this.isFocusable = isOutsideTouchable
this.isOutsideTouchable = isOutsideTouchable
this.isTouchable=isOutsideTouchable
}
/**
* 设置是否展示状态同步中
*/
fun showSyncing(operateState: OperateStateEnum){
mOperatePanelView?.showSyncing(operateState)
}
}

View File

@@ -0,0 +1,127 @@
package com.mogo.och.sweeper.cloud.util;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Pair;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import mogo.telematics.pad.MessagePad;
/**
* 读取启动自动驾驶配置
*/
public class AutopilotModeConfigManager {
private final static String TAG = AutopilotModeConfigManager.class.getSimpleName();
private final AtomicBoolean isReadConfig = new AtomicBoolean(false);
private OnReadAutopilotModeConfigListener listener;
public interface OnReadAutopilotModeConfigListener {
void onReadFailed(String err);//文件不存在或读取失败
void onParseFailed(String err);//配置文件解析失败
void onParse(MessagePad.SetAutopilotModeReq bean);//解析完成
}
public AutopilotModeConfigManager(OnReadAutopilotModeConfigListener listener) {
this.listener = listener;
}
public void registerListener(OnReadAutopilotModeConfigListener listener) {
this.listener = listener;
}
public void unregisterListener() {
listener = null;
}
/**
* 读取配置文件
*
* @return 是否调用成功
*/
public boolean read() {
if (!isReadConfig.get()) {
isReadConfig.set(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
Pair<String, String> config = readFilesToString(new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "AutopilotModeConfig.json"));
if (TextUtils.isEmpty(config.first)) {
if (listener != null) {
listener.onReadFailed(config.second);
}
} else {
MessagePad.SetAutopilotModeReq.Builder builder = MessagePad.SetAutopilotModeReq.newBuilder();
String err = null;
try {
JsonFormat.parser().ignoringUnknownFields().merge(config.first, builder);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
err = e.getMessage();
}
if (TextUtils.isEmpty(err)) {
MessagePad.SetAutopilotModeReq bean = builder.build();
if (listener != null) {
listener.onParse(bean);
}
} else {
if (listener != null) {
listener.onParseFailed(err);
}
}
}
isReadConfig.set(false);
}
};
new Thread(runnable).start();
return true;
}
return false;
}
/**
* @param file
* @return first读取到的内容 second失败信息
*/
private Pair<String, String> readFilesToString(File file) {
InputStream inputStream = null;
String err = null;
try {
inputStream = new FileInputStream(file);
byte[] buffer = new byte[1024];
int length = -1;
StringBuilder stringBuilder = new StringBuilder();
while ((length = inputStream.read(buffer)) != -1) {
stringBuilder.append(new String(buffer, 0, length));
}
return new Pair<String, String>(stringBuilder.toString(), err);
} catch (FileNotFoundException e) {
e.printStackTrace();
err = e.toString();
} catch (IOException e) {
e.printStackTrace();
err = e.toString();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return new Pair<String, String>(null, err);
}
}

View File

@@ -0,0 +1,149 @@
package com.mogo.och.sweeper.cloud.util;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_SWEEPER;
import android.os.Build;
import android.text.TextUtils;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.commons.utils.MogoAnalyticUtils;
import com.mogo.eagle.core.data.app.AppConfigInfo;
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.util.DateTimeUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import java.util.HashMap;
/**
* OCH sweeper埋点工具
*
* Created on 2022/3/24
*/
public class SweeperAnalyticsManager {
private static final class SingletonHolder {
private static final SweeperAnalyticsManager INSTANCE = new SweeperAnalyticsManager();
}
public static SweeperAnalyticsManager getInstance() {
return SweeperAnalyticsManager.SingletonHolder.INSTANCE;
}
private String mStartAutopilotKey;
private HashMap<String, Object> mStartAutopilotParams = new HashMap<>();
private Runnable startAutopilotRunnable = () -> {
// 15s内未开启上报失败埋点
triggerStartAutopilotFailureEvent("", "15s后app等待超时");
};
public void triggerStartAutopilotFailureEventByAdas(String failCode, String failMsg){
removeWaitingCallback();
triggerStartAutopilotFailureEvent(failCode, failMsg);
}
private void triggerStartAutopilotFailureEvent(String failCode, String failMsg){
if (mStartAutopilotParams.isEmpty()) return;
CallerLogger.e( M_SWEEPER + "triggerStartAutopilotFailureEvent", failMsg );
if (CallerAutoPilotStatusListenerManager.INSTANCE.getAutoPilotStatusInfo().getState() !=
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_CODE, failCode);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_MSG, failMsg);
}
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_RESULT
, CallerAutoPilotStatusListenerManager.INSTANCE.getAutoPilotStatusInfo().getState() ==
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING);
MogoAnalyticUtils.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
clearStartAutopilotParams();//清空参数数据,防止误传
}
private void removeWaitingCallback() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (startAutopilotRunnable != null) {
UiThreadHandler.removeCallbacks(startAutopilotRunnable);
}
}
}
public void clearStartAutopilotFailureMSG(){
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_CODE, "");
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_MSG, "");
}
private void clearStartAutopilotParams(){
mStartAutopilotParams.clear();
}
/**
* 触发'开启自动驾驶'埋点流程
* 开启自动驾驶15s内成功则发送成功埋点否则发送失败埋点
* @param restart false点击'滑动出发'启动)/true接管后点击'自动驾驶'按钮启动)
* @param send 是否直接发送埋点15s内开启成功则直接发送成功埋点
*/
public void triggerStartAutopilotEvent(
boolean restart, boolean send, String startName, String endName, int lineId) {
mStartAutopilotKey = restart ?
SweeperConst.EVENT_KEY_RESTART_AUTOPILOT : SweeperConst.EVENT_KEY_START_SERVICE;
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
String plateNum = AppConfigInfo.INSTANCE.getPlateNumber();
String dateTime = DateTimeUtils.getTimeText(
System.currentTimeMillis(), DateTimeUtils.yyyy_MM_dd_HH_mm_ss);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_SN, sn);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_TIME, dateTime);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_NAME, startName);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_END_NAME, endName);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_LINE_ID, lineId);
if (send) {
if (mStartAutopilotParams.isEmpty()) return;
// 开启成功,上报埋点
clearStartAutopilotFailureMSG();
removeWaitingCallback();
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_RESULT, true);
MogoAnalyticUtils.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
clearStartAutopilotParams();//清空参数数据,防止误传
} else {
UiThreadHandler.postDelayed(startAutopilotRunnable, SweeperConst.LOOP_PERIOD_15S);
}
}
/**
* 触发"无法开启自驾已知异常"埋点
* @param startName
* @param endName
* @param lineId
*/
public void triggerUnableStartAPReasonEvent(String startName, String endName, int lineId,
String reason) {
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
String plateNum = AppConfigInfo.INSTANCE.getPlateNumber();
String dateTime = DateTimeUtils.getTimeText(
System.currentTimeMillis(), DateTimeUtils.yyyy_MM_dd_HH_mm_ss);
HashMap<String, Object> params = new HashMap<>();
params.put(SweeperConst.EVENT_PARAM_SN, sn);
params.put(SweeperConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
params.put(SweeperConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
params.put(SweeperConst.EVENT_PARAM_TIME, dateTime);
params.put(SweeperConst.EVENT_PARAM_START_NAME, startName);
params.put(SweeperConst.EVENT_PARAM_END_NAME, endName);
params.put(SweeperConst.EVENT_PARAM_LINE_ID, lineId);
params.put(SweeperConst.EVENT_PARAM_UNABLE_START_REASON, reason);
MogoAnalyticUtils.INSTANCE.track(SweeperConst.EVENT_KEY_AP_UNABLE_START_REASON, params);
}
}

View File

@@ -0,0 +1,491 @@
package com.mogo.och.sweeper.cloud.util
import android.content.Context
import android.util.Log
import com.elegant.utils.UiThreadHandler
import com.google.protobuf.MessageOrBuilder
import com.google.protobuf.TextFormat
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.function.call.map.CallerMapUIServiceManager
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.map.overlay.core.Level
import com.mogo.map.overlay.point.Point
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.common.module.utils.OCHThreadPoolManager
import com.mogo.och.common.module.voice.VoiceNotice
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener
import com.mogo.och.sweeper.common.constant.SweeperConst
import com.mogo.och.sweeper.cloud.model.SweeperTaskModel
import com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog
import com.zhjt.mogo.adas.data.AiCloudTask
import com.zhjt.mogo.adas.data.sweeper.bootable.SweeperBootable
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.Location
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.SubTaskInfo
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.TaskInfo
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.TaskModel
import com.zhjt.mogo.adas.data.sweeper.task.big.SweeperBigTaskStatus
import com.zhjt.mogo.adas.data.sweeper.task.confirm.SweeperTaskConfirm.TaskConfirm
import com.zhjt.mogo.adas.data.sweeper.task.status.SweeperTaskStatus
import com.zhjt.mogo.adas.data.sweeper.task.stop.SweeperTaskStop
import java.util.*
object SweeperCloudTaskUtils {
const val TAG = "SweeperCloudTaskUtils"
/**
* 模拟发送查询当前任务的请求
*/
@JvmStatic
fun mockQueryCurrentTaskInfo() {
// UiThreadHandler.getsUiHandler().postDelayed({
// mockSendCloudTaskInfo(MessageType.PadSendGetTaskReq)
// },1000)
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().getCurrentTask()
}
/**
* 模拟云控发送任务给pad
*/
@JvmStatic
fun mockSendCloudTaskInfo(messageType: AiCloudTask.MessageType) {
val builder = TaskInfo.newBuilder()
builder.sn = getDriverSn()
builder.taskId = "10"
builder.taskName = "烟台清扫车作业任务"
builder.currentTime = System.currentTimeMillis()
builder.taskStartTime = 1683507615000
builder.taskEndTime = 1683540015000
//第一个子任务
val subBuilder0 = SubTaskInfo.newBuilder()
subBuilder0.subTaskId = "1"
subBuilder0.subTaskName = "海水浴场-佛兴禅寺(前岛贴右)"
subBuilder0.taskModel = TaskModel.AUTO
subBuilder0.taskStatus = SweeperCommon.TaskStatus.TO_START
val startLocation0 = Location.newBuilder()
startLocation0.siteName = "自动驾驶子任务起点0"
startLocation0.wgsLongitude = 112.56970262448544
startLocation0.wgsLatitude = 26.817567832504274
startLocation0.longitude = 112.57493487730413
startLocation0.latitude = 26.81397095451884
subBuilder0.startLocation = startLocation0.build()
val endLocation0 = Location.newBuilder()
endLocation0.siteName = "自动驾驶子任务终点0"
endLocation0.wgsLongitude = 112.57723562874442
endLocation0.wgsLatitude = 26.819769725304003
endLocation0.longitude = 112.5824598556873
endLocation0.latitude = 26.81616501438377
subBuilder0.endLocation = endLocation0.build()
subBuilder0.lineId = 123
subBuilder0.lineName = "测试路线0"
builder.addSubList(subBuilder0.build())
//第二个子任务
// val subBuilder1 = SubTaskInfo.newBuilder()
// subBuilder1.subTaskId = "2"
// subBuilder1.subTaskName = "佛兴禅寺-海水浴场(前岛贴左)"
// subBuilder1.taskModel = TaskModel.AUTO
// subBuilder1.taskStatus = SweeperCommon.TaskStatus.TO_START
// val startLocation1 = Location.newBuilder()
// startLocation1.siteName = "自动驾驶子任务起点1"
// startLocation1.wgsLongitude = 112.57723562874442
// startLocation1.wgsLatitude = 26.819769725304003
// startLocation1.longitude = 112.5824598556873
// startLocation1.latitude = 26.81616501438377
// subBuilder1.startLocation = startLocation1.build()
// val endLocation1 = Location.newBuilder()
// endLocation1.siteName = "自动驾驶子任务终点1"
// endLocation1.wgsLongitude = 112.56970262448544
// endLocation1.wgsLatitude = 26.817567832504274
// endLocation1.longitude = 112.57493487730413
// endLocation1.latitude = 26.81397095451884
// subBuilder1.endLocation = endLocation1.build()
// subBuilder1.lineId = 124
// subBuilder1.lineName = "测试路线1"
// builder.addSubList(subBuilder1.build())
// //第三个子任务
// val subBuilder2 = SubTaskInfo.newBuilder()
// subBuilder2.subTaskId = "3"
// subBuilder2.subTaskName = "海水浴场-佛兴禅寺(前岛贴左)"
// subBuilder2.taskModel = TaskModel.MANUAL
// subBuilder2.taskStatus = SweeperCommon.TaskStatus.TO_START
// val startLocation2 = Location.newBuilder()
// startLocation2.siteName = "人工驾驶子任务起点2"
// startLocation2.wgsLongitude = 121.31344761929978
// startLocation2.wgsLatitude = 37.53205755535642
// startLocation2.longitude = 121.3185679517558
// startLocation2.latitude = 37.5329694887952
// subBuilder2.startLocation = startLocation2.build()
// val endLocation2 = Location.newBuilder()
// endLocation2.siteName = "人工驾驶子任务终点2"
// endLocation2.wgsLongitude = 121.4551205070834
// endLocation2.wgsLatitude = 37.47936696980237
// endLocation2.longitude = 121.46030960742117
// endLocation2.latitude = 37.48032689641474
// subBuilder2.endLocation = endLocation2.build()
// subBuilder2.lineId = 125
// subBuilder2.lineName = "测试路线2"
// builder.addSubList(subBuilder2.build())
// //第四个子任务
// val subBuilder3 = SubTaskInfo.newBuilder()
// subBuilder3.subTaskId = "4"
// subBuilder3.subTaskName = "海水浴场-佛兴禅寺(前岛贴左第四个任务)"
// subBuilder3.taskModel = TaskModel.MANUAL
// subBuilder3.taskStatus = SweeperCommon.TaskStatus.TO_START
// val startLocation3 = Location.newBuilder()
// startLocation3.siteName = "人工驾驶子任务起点3"
// startLocation3.wgsLongitude = 121.31344761929978
// startLocation3.wgsLatitude = 37.53205755535642
// startLocation3.longitude = 121.3185679517558
// startLocation3.latitude = 37.5329694887952
// subBuilder3.startLocation = startLocation3.build()
// val endLocation3 = Location.newBuilder()
// endLocation3.siteName = "人工驾驶子任务终点3"
// endLocation3.wgsLongitude = 121.4551205070834
// endLocation3.wgsLatitude = 37.47936696980237
// endLocation3.longitude = 121.46030960742117
// endLocation3.latitude = 37.48032689641474
// subBuilder3.endLocation = endLocation3.build()
// subBuilder3.lineId = 126
// subBuilder3.lineName = "测试路线3"
// builder.addSubList(subBuilder3.build())
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().onSweeperFutianCloudTask(
messageType,
"${System.currentTimeMillis()}",
System.currentTimeMillis(),
builder.build()
)
CallerLogger.d(
SceneConstant.M_SWEEPER + TAG,
"messageType:" + messageType.number + "taskInfo:" + printMessage(builder.build())
)
}
/**
* 模拟云控发送子任务开始确认信息
*/
@JvmStatic
fun mockSendCloudSubTaskConfirm() {
val builder = TaskConfirm.newBuilder()
builder.sn = getDriverSn()
builder.taskId = "10"
builder.subTaskId = "1"
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().onSweeperFutianCloudTaskConfirm(
AiCloudTask.MessageType.CloudPushTaskConfirm, "${System.currentTimeMillis()}",
System.currentTimeMillis(), builder.build()
)
}
/**
* 模拟下发更新子任务状态指令
*/
@JvmStatic
fun mockSendCloudUpdateSubTaskStatus() {
val builder = SweeperTaskStatus.TaskStatusPush.newBuilder()
builder.sn = getDriverSn()
builder.taskId = "10"
builder.subTaskId = "1"
builder.taskStatus = SweeperCommon.TaskStatus.RUNNING
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().onSweeperFutianCloudTaskStatus(
AiCloudTask.MessageType.CloudPushTaskStatus, "${System.currentTimeMillis()}",
System.currentTimeMillis(), builder.build()
)
}
/**
* 模拟云端发送大任务结束
*/
@JvmStatic
fun mockSendCloudBigTaskEnd() {
val builder = SweeperTaskStop.StopTaskReq.newBuilder()
builder.sn = getDriverSn()
builder.taskId = "10"
builder.type = SweeperTaskStop.StopTaskType.ADVANCE
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().onSweeperFutianCloudTaskStop(
AiCloudTask.MessageType.CloudPushTaskStop, "${System.currentTimeMillis()}",
System.currentTimeMillis(), builder.build()
)
}
/**
* 模拟云端发送大任务状态给pad
*/
@JvmStatic
fun mockSendCloudBigTaskStatus() {
val builder = SweeperBigTaskStatus.BigTaskStatusPush.newBuilder()
builder.sn = getDriverSn()
builder.taskId = "10"
builder.taskStatus = SweeperCommon.TaskStatus.FINISHED
builder.systemTime = System.currentTimeMillis()
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().onSweeperFutianCloudBigTaskStatus(
AiCloudTask.MessageType.CloudPushBigTaskStatus, "${System.currentTimeMillis()}",
System.currentTimeMillis(), builder.build()
)
}
/**
* 模拟云端发送是否可以启动自驾
*/
@JvmStatic
fun mockSendCloudBootable() {
val builder = SweeperBootable.IsBootableResp.newBuilder()
builder.sn = getDriverSn()
builder.taskId = "10"
builder.subTaskId = "1"
builder.code = SweeperCommon.Code.SUCCEED
UiThreadHandler.getsUiHandler().postDelayed({
com.mogo.och.sweeper.cloud.model.SweeperTaskModel.getInstance().onSweeperFutianCloudBootable(
AiCloudTask.MessageType.PadSendBootable, "${System.currentTimeMillis()}",
System.currentTimeMillis(), builder.build()
)
}, 10000)
}
/**
* 模式添加高精地图Marker
*/
@JvmStatic
fun mockAddHdMarker(uuid: String, longitude: Double, latitude: Double, isStartMarker: Boolean) {
//开启线程执行起终点marker设置
val setMapMarkerRunnable = Runnable {
val builder =
Point.Options.Builder(
com.mogo.och.sweeper.common.constant.SweeperConst.TYPE_MARKER_SWEEPER_SUBTASK_START_END,
Level.MAP_MARKER
)
.setId(uuid)
.anchor(0.5f, 0.5f)
.set3DMode(true)
.isUseGps(true)
.controlAngle(false)
.icon3DRes(if (isStartMarker) R.raw.star_marker else R.raw.end_marker)
.longitude(longitude)
.latitude(latitude)
val overlayManager = CallerMapUIServiceManager.getOverlayManager()
Log.d(TAG, "CallerMapUIServiceManager.getOverlayManager()=$overlayManager")
overlayManager?.showOrUpdatePoint(builder.build())
}
OCHThreadPoolManager.getsInstance().execute(setMapMarkerRunnable)
}
/**
* 模拟 移除高精地图Marker
*/
@JvmStatic
fun mockRemoveHDMarker() {
//开启线程移除起终点marker设置
val removeMapMarkerRunnable = Runnable {
val overlayManager = CallerMapUIServiceManager.getOverlayManager()
overlayManager?.removeAllPointsInOwner(com.mogo.och.sweeper.common.constant.SweeperConst.TYPE_MARKER_SWEEPER_SUBTASK_START_END)
}
OCHThreadPoolManager.getsInstance().execute(removeMapMarkerRunnable)
}
private fun getDriverSn(): String? {
return MoGoAiCloudClientConfig.getInstance().sn
}
/**
* 创建接收大任务弹窗
*/
@JvmStatic
fun createReceivedBigTaskInfoDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?,
taskInfo: TaskInfo
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
val startCalendar = DateTimeUtil.formatLongToCalendar(taskInfo.taskStartTime)
val endCalendar = DateTimeUtil.formatLongToCalendar(taskInfo.taskEndTime)
builder.titleStr = "任务接取"
builder.contentStr = "已为您接取任务${taskInfo.taskName}"
builder.tipStr =
"(任务时间${DateTimeUtil.formatCalendarToString(startCalendar, DateTimeUtil.HH_mm)}-${
DateTimeUtil.formatCalendarToString(
endCalendar,
DateTimeUtil.HH_mm
)
}"
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
/**
* 创建确认开始子任务弹窗
*/
@JvmStatic
fun createConfirmStartSubTaskDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?,
subTaskInfo: SubTaskInfo
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
builder.titleStr = "子任务确认"
builder.contentStr = "请确认并执行子任务 ${subTaskInfo.subTaskName}"
builder.tipStr =
if (subTaskInfo.taskModel == TaskModel.MANUAL) "[需手动驾驶至终点 ${subTaskInfo.endLocation.siteName}]" else "[自动驾驶至终点 ${subTaskInfo.endLocation.siteName}请保持N档拉手刹再进入自动驾驶]"
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
/**
* 创建任务中止弹窗
*/
@JvmStatic
fun createSweeperTaskEndDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?,
stopTaskType: SweeperTaskStop.StopTaskType,
timeoutStr: String
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
val titleStr = when (stopTaskType) {
SweeperTaskStop.StopTaskType.ADVANCE -> "任务中止"
SweeperTaskStop.StopTaskType.EXCEPTION -> "任务异常结束"
else -> "任务结束"
}
builder.titleStr = titleStr
when (stopTaskType) {
//任务提前结束
SweeperTaskStop.StopTaskType.ADVANCE -> {
builder.contentStr = "云端中止任务,注意车辆即将停车!"
builder.tipStr = ""
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
VoiceNotice.showNotice("云端中止任务")
}
//任务异常结束
SweeperTaskStop.StopTaskType.EXCEPTION -> {
builder.contentStr = "云端异常结束任务!"
builder.tipStr = ""
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
VoiceNotice.showNotice("云端异常结束任务")
}
}
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
/**
* 任务正常结束弹窗
*/
@JvmStatic
fun createSweeperTaskNormalEndDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
builder.titleStr = "任务结束"
builder.contentStr = "任务已完成,干的漂亮!"
builder.tipStr = ""
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
/**
* 任务异常结束弹窗 (异常 / 取消)
*/
@JvmStatic
fun createSweeperTaskExceptionEndDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?,
isCancel: Boolean
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
builder.titleStr = "任务结束"
builder.contentStr = if (isCancel) "任务已取消!" else "任务已异常结束!"
builder.tipStr = ""
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
@JvmStatic
fun createSweeperTaskSuspendDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
builder.titleStr = "任务暂停"
builder.contentStr = "云端暂停任务,注意车辆即将停车!"
builder.tipStr = "【云端任务恢复前不可启动自动驾驶】"
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
@JvmStatic
fun createSweeperTaskResumeDialog(
context: Context?,
listener: com.mogo.och.sweeper.cloud.callback.SweeperCloudDialogClickListener?,
contentStr: String
): com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog? {
val builder: com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder = com.mogo.och.sweeper.cloud.ui.dialog.SweeperCloudDialog.Builder()
builder.titleStr = "任务恢复"
builder.contentStr = contentStr
builder.tipStr = "【注意安全,坐稳扶好】"
builder.leftStr = "确认"
builder.middleStr = ""
builder.rightStr = ""
builder.countDownTime = 10
builder.listener = listener
return context?.let { it1 -> builder.build(it1) }
}
@JvmStatic
fun printMessage(message: MessageOrBuilder): String {
return "\n" + TextFormat.printer().escapingNonAscii(false).printToString(message)
}
@JvmStatic
fun getRequestId(): String {
return UUID.randomUUID().toString()
}
/**
* 计算任务超时时间
*/
@JvmStatic
fun getTimeSpentString(taskEndTime: Long): String {
val timeLag: Long = System.currentTimeMillis() - taskEndTime
//天
val day: Long = timeLag / (24 * 60 * 60 * 1000)
//小时
val hour = (timeLag / (60 * 60 * 1000) - day * 24)
//分钟
val minute = ((timeLag / (60 * 1000)) - day * 24 * 60 - hour * 60)
if (day >= 1) {
return "${day}${hour}${minute}分钟"
}
if (hour >= 1) {
return "${hour}${minute}分钟"
}
return "${minute}分钟"
}
}

View File

@@ -0,0 +1,65 @@
package com.mogo.och.sweeper.cloud.view
import android.content.Context
import android.os.Handler
import android.os.Message
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
/**
* 倒计时View
*/
class CountDownView : AppCompatTextView, Handler.Callback {
private val mHandler = Handler(this)
private var mCountDownTime = DEFAULT_COUNT_DOWN_TIME
private var listener: CountDownListener? = null
constructor(context: Context?) : super(context!!) {}
constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs) {}
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context!!, attrs, defStyleAttr) {}
override fun handleMessage(message: Message): Boolean {
if (message.what == MSG_COUNT_DOWN) {
mCountDownTime--
if (mCountDownTime > 0) {
text = "$mCountDownTime"
mHandler.sendEmptyMessageDelayed(MSG_COUNT_DOWN, DEFAULT_COUNT_DOWN_DELAY)
} else {
stopCountDown()
listener?.stop()
}
}
return false
}
fun setCountDownListener(listener: CountDownListener?) {
this.listener = listener
}
/**
* 启动倒计时
*/
fun startCountDown(countDownTime:Int) {
mHandler.removeMessages(MSG_COUNT_DOWN)
text = "$countDownTime"
mCountDownTime=countDownTime
mHandler.sendEmptyMessageDelayed(MSG_COUNT_DOWN, DEFAULT_COUNT_DOWN_DELAY)
}
/**
* 停止倒计时
*/
fun stopCountDown() {
mHandler.removeMessages(MSG_COUNT_DOWN)
text = null
}
interface CountDownListener {
fun stop()
}
companion object {
private const val DEFAULT_COUNT_DOWN_TIME = 15
private const val MSG_COUNT_DOWN = 1001
private const val DEFAULT_COUNT_DOWN_DELAY = 1000L
}
}

View File

@@ -0,0 +1,47 @@
package com.mogo.och.sweeper.cloud.view;
import android.content.Context;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontAdaptionTextView extends TextView {
public FontAdaptionTextView(Context context) {
super(context);
}
public FontAdaptionTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FontAdaptionTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (getMeasuredWidth() != 0) {
//判断View字体行数
if (getLayout().getLineCount() > 1) {
//获取view内部间隔
int l = getPaddingLeft();
int r = getPaddingRight();
float pp = 0;
//计算字符串长度
for (int i = 0; i < getLayout().getLineCount(); i++) {
pp = pp + l + r + getLayout().getLineWidth(i);
}
//计算view的宽度与字符串长度的比例
float f = getMeasuredWidth() / pp;
//获取缩放后的字体高度
float s = getTextSize() * f;
//设置控件字体大小
setTextSize(TypedValue.COMPLEX_UNIT_PX, s);
}
}
}
}

View File

@@ -0,0 +1,53 @@
package com.mogo.och.sweeper.cloud.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.och.sweeper.R
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.SubTaskInfo
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.TaskModel
import kotlinx.android.synthetic.main.sweeper_cloud_subtask_view.view.*
/**
* 子任务View
*/
class SubTaskView : ConstraintLayout {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_cloud_subtask_view, this)
}
/**
* 设置子任务信息
*/
fun setData(taskInfo: SubTaskInfo, isLastTask: Boolean = false) {
tvSubTaskName.text = taskInfo.subTaskName
tvSubTaskName.setTextColor(
if (taskInfo.taskStatus == SweeperCommon.TaskStatus.RUNNING)
Color.parseColor("#3BD2FF")
else
Color.parseColor("#FFFFFF")
)
if (taskInfo.taskModel == TaskModel.MANUAL) {//人工驾驶子任务
ivManualDriving.visibility = View.VISIBLE
} else {
ivManualDriving.visibility = View.GONE
}
if (taskInfo.taskStatus == SweeperCommon.TaskStatus.RUNNING) {
ivSubTask.setImageResource(R.drawable.sweeper_icon_select_subtask)
} else {
ivSubTask.setImageResource(R.drawable.sweeper_icon_not_select_subtask)
}
ivRightDownArrow.visibility = if (isLastTask) View.GONE else View.VISIBLE
}
}

View File

@@ -0,0 +1,208 @@
package com.mogo.och.sweeper.cloud.view
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.sweeper.R
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon
import com.zhjt.mogo.adas.data.sweeper.common.SweeperCommon.TaskStatus
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.SubTaskInfo
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.TaskInfo
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.currentSubTask
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.lastSubTask
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.preSubTask
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.tvStartAuto
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.tvTaskName
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.tvTaskState
import kotlinx.android.synthetic.main.sweeper_cloud_current_task_info.view.tvTaskTime
/**
* 清扫车当前任务信息展示
*/
class SweeperCurrentTaskInfoView : ConstraintLayout {
private val TAG: String = "SweeperCurrentTaskInfoView"
//当前任务操作菜单
private var listTask: List<SubTaskInfo>? = null
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
}
constructor(
context: Context,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_cloud_current_task_info, this)
updateTaskStateText(TaskStatus.TO_START)
}
fun getAutoBtn(): TextView {
return tvStartAuto
}
/**
* 根据当前自动驾驶状态,更新【请求进入自动驾驶】按钮样式
*/
fun updateStartAutoPilotBtnByAutoPilotState(autopilotState: Int) {
when (autopilotState) {
//不可自动驾驶
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE -> {
tvStartAuto.setTextColor(Color.parseColor("#66FFFFFF"))
tvStartAuto.isSelected = false
}
//人工驾驶
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE -> {
tvStartAuto.setTextColor(Color.parseColor("#FFFFFFFF"))
tvStartAuto.isSelected = true
}
//自动驾驶中
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING -> {
tvStartAuto.setTextColor(Color.parseColor("#66FFFFFF"))
tvStartAuto.isSelected = false
}
//平行驾驶
IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING -> {
tvStartAuto.setTextColor(Color.parseColor("#66FFFFFF"))
tvStartAuto.isSelected = false
}
}
}
/**
* 根据 当前自动驾驶状态-是否是自动驾驶子任务-子任务是否在运行,更新【请求进入自动驾驶】按钮样式
*/
fun updateStartAutoPilotBtnByAutoPilotState(
isManualAutoPilotState: Boolean,
isAutoSubTask: Boolean,
isSubTaskRunning: Boolean
) {
if (isManualAutoPilotState && isAutoSubTask && isSubTaskRunning) {
tvStartAuto.setTextColor(Color.parseColor("#FFFFFFFF"))
tvStartAuto.isSelected = true
} else {
tvStartAuto.setTextColor(Color.parseColor("#66FFFFFF"))
tvStartAuto.isSelected = false
}
}
/**
* 设置当前任务数据
*/
@SuppressLint("SetTextI18n")
fun setData(
taskInfo: TaskInfo?,
currentPosition: Int = -1
) {
// 更新 任务名称、任务时间
taskInfo?.apply {
this@SweeperCurrentTaskInfoView.listTask = subListList
tvTaskName.text = taskName
val calendarStart = DateTimeUtil.formatLongToCalendar(taskStartTime)
val calendarEnd = DateTimeUtil.formatLongToCalendar(taskEndTime)
tvTaskTime.text =
"${DateTimeUtil.formatCalendarToString(calendarStart, DateTimeUtil.HH_mm)}-${
DateTimeUtil.formatCalendarToString(
calendarEnd,
DateTimeUtil.HH_mm
)
}"
}
// 更新大任务状态
updateTaskStateText(taskInfo?.taskStatus ?: TaskStatus.TO_START)
// 更新子任务列表
listTask?.let {
//特殊处理当前暂无执行中任务的情况
if (currentPosition == -1) {
setCurrentData(currentPosition + 1)
} else {
setCurrentData(currentPosition)
}
}
}
/**
* 设置任务的状态
*/
private fun updateTaskStateText(taskStatus: SweeperCommon.TaskStatus) {
when (taskStatus) {
SweeperCommon.TaskStatus.RUNNING -> {
tvTaskState.text = "任务执行中"
tvTaskState.setBackgroundResource(R.drawable.bg_shape_task_state_working)
}
SweeperCommon.TaskStatus.SUSPENDED -> {
tvTaskState.text = "任务已暂停"
tvTaskState.setBackgroundResource(R.drawable.bg_shape_task_state_not_ready)
}
else -> {
tvTaskState.text = "任务待开始"
tvTaskState.setBackgroundResource(R.drawable.bg_shape_task_state_not_ready)
}
}
}
/**
* 填充数据
*/
private fun setCurrentData(currentPosition: Int) {
listTask?.let {
if (it.size == 1) {
preSubTask.setData(it[currentPosition], isLastTask = true)
currentSubTask.visibility = View.INVISIBLE
lastSubTask.visibility = View.INVISIBLE
} else if (it.size == 2) {
if (currentPosition == 0) {
preSubTask.setData(it[currentPosition])
currentSubTask.setData(it[1], isLastTask = true)
} else {
preSubTask.setData(it[currentPosition - 1])
currentSubTask.setData(it[currentPosition], isLastTask = true)
}
preSubTask.visibility = View.VISIBLE
currentSubTask.visibility = View.VISIBLE
lastSubTask.visibility = View.GONE
} else {
preSubTask.visibility = View.VISIBLE
currentSubTask.visibility = View.VISIBLE
lastSubTask.visibility = View.VISIBLE
//当前正在执行的任务是第一个子任务
if (currentPosition == 0) {
preSubTask.setData(it[currentPosition])
currentSubTask.setData(it[1])
lastSubTask.setData(it[2], isLastTask = true)
//当前正在执行的任务是最后一个子任务
} else if (currentPosition == it.size - 1) {
preSubTask.setData(it[currentPosition - 2])
currentSubTask.setData(it[currentPosition - 1])
lastSubTask.setData(it[currentPosition], isLastTask = true)
} else {
preSubTask.setData(it[currentPosition - 1])
currentSubTask.setData(it[currentPosition])
lastSubTask.setData(it[currentPosition + 1], isLastTask = true)
}
}
}
}
}

View File

@@ -0,0 +1,196 @@
package com.mogo.och.sweeper.cloud.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import chassis.Chassis.GearPosition
import chassis.Chassis.LightSwitch
import chassis.ChassisStatesOuterClass.BMSSystemStates
import chassis.ChassisStatesOuterClass.SweeperFuTianTaskSystemStates
import com.elegant.utils.UiThreadHandler
import com.mogo.eagle.core.function.api.autopilot.*
import com.mogo.eagle.core.function.call.autopilot.*
import com.mogo.eagle.core.function.hmi.ui.widget.TapPositionView
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.ThreadUtils
import com.mogo.och.sweeper.R
import planning.RoboSweeperTaskIndexOuterClass
import kotlin.math.roundToInt
/**
* 车辆基本信息:方向盘下方的档位 转向灯 限速 速度 电量 水量
*/
class SweeperTrafficDataView : ConstraintLayout,
IMoGoBatteryManagementSystemListener,
IMoGoChassisLamplightListener,
IMoGoChassisGearStateListener,
IMoGoSweeperFutianCleanSystemListener {
private var tapPositionView //方向盘下方的档位
: TapPositionView? = null
private var speedImage //速度图标
: ImageView? = null
private var speedTextView //速度值
: TextView? = null
private var sweeperAutoState //自动驾驶状态
: TextView? = null
private var tvBattery //电量百分比展示
: TextView? = null
private var ivBgWaterWarning //水位预警背景图
: ImageView? = null
private var ivWater //水位图标
: ImageView? = null
private val TAG = "SweeperTrafficDataView"
// 底盘数据回调时间间隔
private val VEHICLE_STATE_INTERVAL_MILLIS = 500L
// 当前时间戳
private var mCurrentTimeWaterMillis: Long = 0
private var mCurrentTimeBatteryMillis: Long = 0
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_cloud_traffic_data, this)
tapPositionView = findViewById(R.id.sweeperTrafficPosition)
speedImage = findViewById(R.id.sweeperSpeedImage)
speedTextView = findViewById(R.id.sweeperSpeedText)
sweeperAutoState = findViewById(R.id.sweeperAutoState)
tvBattery = findViewById(R.id.tvBattery)
ivBgWaterWarning = findViewById(R.id.sweeperIvBgWaterWarning)
ivWater = findViewById(R.id.sweeperIvWater)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
//电量
CallerBatteryManagementSystemListenerManager.addListener(TAG, this)
//转向灯
CallerChassisLamplightListenerManager.addListener(TAG, this)
//档位
CallerChassisGearStateListenerManager.addListener(TAG, this)
//清扫车相关数据接口
CallerSweeperFutianCleanSystemListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerBatteryManagementSystemListenerManager.removeListener(TAG)
CallerChassisLamplightListenerManager.removeListener(TAG)
CallerChassisGearStateListenerManager.removeListener(TAG)
CallerSweeperFutianCleanSystemListenerManager.removeListener(TAG)
}
override fun onSweeperFutianTaskIndexData(roboSweeperTaskIndex: RoboSweeperTaskIndexOuterClass.RoboSweeperTaskIndex) {}
override fun onSweeperFutianCleanSystemState(cleanSystemState: SweeperFuTianTaskSystemStates) {
val current = System.currentTimeMillis()
if (current - mCurrentTimeWaterMillis <= VEHICLE_STATE_INTERVAL_MILLIS) {
return
}
mCurrentTimeWaterMillis = current
d(SceneConstant.M_SWEEPER + TAG, "水位:${cleanSystemState.secuCleanWaterTankLow}")
UiThreadHandler.post {
if (cleanSystemState.secuCleanWaterTankLow) { //清水箱水位低不能清洗作业报警信号
ivBgWaterWarning?.visibility = VISIBLE
ivWater?.isSelected = true
} else {
ivBgWaterWarning?.visibility = GONE
ivWater?.isSelected = false
}
}
}
@SuppressLint("SetTextI18n")
override fun onBatteryManagementSystemStates(states: BMSSystemStates) {
val current = System.currentTimeMillis()
if (current - mCurrentTimeBatteryMillis <= VEHICLE_STATE_INTERVAL_MILLIS) {
return
}
mCurrentTimeBatteryMillis = current
d(SceneConstant.M_SWEEPER + TAG, "电量:${states.bmsSoc}")
UiThreadHandler.post {
tvBattery?.text = "${states.bmsSoc.roundToInt()}%"
}
}
/**
* 车辆转向灯
* @param directionLight
*/
override fun onAutopilotLightSwitchData(lightSwitch: LightSwitch?) {
//转向灯状态 0是正常 1是左转 2是右转
}
/**
* 设置自动驾驶状态
*/
fun setAutoState(state: Int) {
when (state) {
//不可自动驾驶
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE -> {
sweeperAutoState?.text="自动驾驶"
sweeperAutoState?.setBackgroundResource(R.drawable.icon_not_auto)
}
//人工驾驶
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE -> {
sweeperAutoState?.text="人工驾驶"
sweeperAutoState?.setBackgroundResource(R.drawable.icon_auto)
}
//自动驾驶中
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING -> {
sweeperAutoState?.text="自动驾驶"
sweeperAutoState?.setBackgroundResource(R.drawable.icon_auto)
}
//平行驾驶
IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING -> {
sweeperAutoState?.text="平行驾驶"
sweeperAutoState?.setBackgroundResource(R.drawable.icon_auto)
}
}
}
/**
* 刹车灯
* @param brakeLight
*/
override fun onAutopilotBrakeLightData(brakeLight: Boolean) {
d(TAG, "刹车灯:$brakeLight")
}
/**
* 方向盘下方的档位
* @param gear
*/
override fun onAutopilotGearData(gear: GearPosition) {
d(TAG, "司机屏档位$gear")
ThreadUtils.runOnUiThread {
tapPositionView?.updateWithGear(gear)
}
}
fun getSpeedImage(): ImageView? {
return speedImage
}
/**
* 速度设置
*/
fun updateSpeedWithValue(newSpeed: Int) {
speedTextView?.text = newSpeed.toString()
speedImage?.setBackgroundResource(if (newSpeed > 60) R.drawable.sweeper_traffic_data_speed_warning else R.drawable.sweeper_bg_traffic_data_speed)
}
}

View File

@@ -0,0 +1,148 @@
package com.mogo.och.sweeper.cloud.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import com.mogo.eagle.core.data.enums.DataSourceType
import com.mogo.eagle.core.data.enums.TrafficLightEnum
import com.mogo.eagle.core.function.api.datacenter.union.IMoGoTrafficLightListener
import com.mogo.eagle.core.function.call.v2x.CallerTrafficLightListenerManager
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.UiThreadHandler
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.sweeper_traffic_light_view.view.*
/**
* 清扫车红绿灯view-
*
* Created on 2022/3/29
*/
class SweeperTrafficLightView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr), IMoGoTrafficLightListener {
companion object {
private const val TAG = "SweeperTrafficLightView"
}
private var mCurrentLightId = TrafficLightEnum.BLACK
init {
init(context)
}
private fun init(context: Context?) {
LayoutInflater.from(context).inflate(R.layout.sweeper_traffic_light_view, this, true)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerTrafficLightListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerTrafficLightListenerManager.removeListener(TAG)
}
/**
* 展示红绿灯预警
*
* @param checkLightId 0-都是默认1-红2-黄3-绿
* @param lightSource 1:云端下发2:自车感知
*/
override fun showTrafficLight(checkLightId: TrafficLightEnum, lightSource: DataSourceType) {
mCurrentLightId = checkLightId
updateTrafficLightIcon(checkLightId)
CallerLogger.d(SceneConstant.M_SWEEPER + TAG,"灯态类型:"+checkLightId.name +" 灯态来源:"+DataSourceType.getName(lightSource))
}
/**
* 关闭红绿灯预警展示,并重制灯态
*/
override fun disableTrafficLight() {
UiThreadHandler.post {
mCurrentLightId = TrafficLightEnum.BLACK
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_gay_nor)
sweeper_traffic_light_time_tv.text = ""
this@SweeperTrafficLightView.visibility = VISIBLE
CallerLogger.d(SceneConstant.M_SWEEPER + TAG,"灯态类型disableTrafficLight")
}
}
/**
* @param redNum 红灯倒计时
* @param yellowNum 黄灯倒计时
* @param greenNum 绿灯倒计时
*/
override fun changeCountdownTrafficLightNum(redNum: Int, yellowNum: Int, greenNum: Int) {
when (mCurrentLightId) {
TrafficLightEnum.RED -> changeCountdownRed(redNum)
TrafficLightEnum.YELLOW -> changeCountdownYellow(yellowNum)
TrafficLightEnum.GREEN -> changeCountdownGreen(greenNum)
else -> UiThreadHandler.post { sweeper_traffic_light_time_tv.text = "" }
}
}
override fun changeCountdownRed(redNum: Int) {
UiThreadHandler.post {
if (redNum > 0) {
sweeper_traffic_light_time_tv.text = redNum.toString()
} else {
sweeper_traffic_light_time_tv.text = ""
}
}
}
override fun changeCountdownGreen(greenNum: Int) {
UiThreadHandler.post {
if (greenNum > 0) {
sweeper_traffic_light_time_tv.text = greenNum.toString()
} else {
sweeper_traffic_light_time_tv.text = ""
}
}
}
override fun changeCountdownYellow(yellowNum: Int) {
UiThreadHandler.post {
if (yellowNum > 0) {
sweeper_traffic_light_time_tv.text = yellowNum.toString()
} else {
sweeper_traffic_light_time_tv.text = ""
}
}
}
/**
* 更新红绿灯icon
*
* @param lightId 0-都是默认1-红2-黄3-绿
*/
private fun updateTrafficLightIcon(lightId: TrafficLightEnum) {
UiThreadHandler.post {
when (lightId) {
TrafficLightEnum.RED -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_red_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
TrafficLightEnum.YELLOW -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_lightyellow_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
TrafficLightEnum.GREEN -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_green_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
else -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_gay_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
}
}
}
}

View File

@@ -0,0 +1,178 @@
package com.mogo.och.sweeper.cloud.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import chassis.ChassisStatesOuterClass.SweeperFuTianTaskSystemStates
import com.elegant.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.constant.OperateStateEnum
import com.mogo.och.sweeper.common.util.SweeperFutianCmdUtil
import com.zhjt.mogo.adas.data.sweeper.task.SweeperTask.TaskModel
import kotlinx.android.synthetic.main.sweeper_work_mode.view.*
/**
* 清扫车模式信息展示
*/
class SweeperWorkModeView : ConstraintLayout {
private var isSelectPureSweepMode: Boolean = false
private val TAG = "SweeperWorkModeView"
//清扫模式选择面板
private var mOperatePanelPopWindow: com.mogo.och.sweeper.cloud.ui.popwindow.SweeperOperatePanelPopWindow? = null
private var operateState: OperateStateEnum=OperateStateEnum.SYNCING_STATUS
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_work_mode, this)
mOperatePanelPopWindow = com.mogo.och.sweeper.cloud.ui.popwindow.SweeperOperatePanelPopWindow(context)
setShowOrHideCleanSystemState(OperateStateEnum.SYNCING_STATUS,null)
}
/**
* 设置view
*/
fun setTrafficDataView(trafficDataView: com.mogo.och.sweeper.cloud.view.SweeperTrafficDataView){
//清扫模式选择面板打开关闭处理
ivOpenOperatePanel.setOnClickListener {
if (mOperatePanelPopWindow?.isShowing != true) {
mOperatePanelPopWindow?.showAsDropDown(
trafficDataView,
resources.getDimension(R.dimen.dp_600).toInt(),
0
)
UiThreadHandler.postDelayed({
d(SceneConstant.M_SWEEPER + TAG, "operateState"+operateState.code)
mOperatePanelPopWindow?.showSyncing(operateState)
},300)
} else {
mOperatePanelPopWindow?.dismiss()
}
}
}
/**
* 设置清扫模式面板
*/
fun setSweeperFutianCleanSystemState(taskType: TaskModel, cleanSystemState: SweeperFuTianTaskSystemStates?) {
// TODO:传递清扫车底盘数据给上装面板
//mOperatePanelPopWindow?.setCleanSystemState(cleanSystemState, this@SweeperWorkModeView)
if (this.operateState.code==OperateStateEnum.STARTING_STATUS.code){
return
}
//如果状态是同步中,在底盘首次回调时把状态强制修改成成功
if (this.operateState.code==OperateStateEnum.SYNCING_STATUS.code){
this.operateState=OperateStateEnum.SUCCESS_STATUS
d(SceneConstant.M_SWEEPER + TAG, "SystemState operateState"+operateState.code)
}
//清扫车暂未选择清扫模式或者任务类型是人工驾驶子任务,则暂无清扫模式
if (SweeperFutianCmdUtil.checkIfCleanMode(cleanSystemState) || taskType==TaskModel.MANUAL||taskType==TaskModel.DEFAULT_MODEL) {
setShowOrHideCleanSystemState(OperateStateEnum.NO_STATUS, cleanSystemState)
} else {
setShowOrHideCleanSystemState(OperateStateEnum.SUCCESS_STATUS, cleanSystemState)
}
}
/**
* 设置清扫模式状态显示和隐藏
*/
private fun setShowOrHideCleanSystemState(operateState: OperateStateEnum, cleanSystemState: SweeperFuTianTaskSystemStates?) {
when (operateState.code) {
OperateStateEnum.SYNCING_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "状态同步中,请稍后"
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
OperateStateEnum.FAIL_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "上装启动失败"
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
OperateStateEnum.STARTING_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "上装启动中..."
mOperatePanelPopWindow?.setIsOutsideTouchable(false)
}
OperateStateEnum.NO_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "暂无"
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
else -> {
groupWorkModelPanel?.visibility = View.VISIBLE
tvNoDataDesc?.visibility = View.GONE
setCleanModeData(cleanSystemState)
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
}
}
fun cleaningModeState(operateState: OperateStateEnum, cleanSystemState: SweeperFuTianTaskSystemStates?, isSelectPureSweepMode: Boolean) {
this.isSelectPureSweepMode = isSelectPureSweepMode
this.operateState=operateState
setShowOrHideCleanSystemState(operateState, cleanSystemState)
}
/**
* 设置清扫模式数据
*/
private fun setCleanModeData(cleanSystemState: SweeperFuTianTaskSystemStates?) {
if (isSelectPureSweepMode) {
tvCleaningMode?.text = "纯扫模式"
groupWorkModelPanel?.visibility = View.VISIBLE
tvNoDataDesc?.visibility = View.GONE
} else {
if (SweeperFutianCmdUtil.checkIfCleanMode(cleanSystemState)) {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "暂无"
return
} else {
groupWorkModelPanel?.visibility = View.VISIBLE
tvNoDataDesc?.visibility = View.GONE
// 作业模式
if (cleanSystemState?.secuModWashSweepSts == true) tvCleaningMode?.text = "洗扫模式"
if (cleanSystemState?.secuModWashSts == true) tvCleaningMode?.text = "纯洗模式"
if (cleanSystemState?.secuWorkTonSts == true) tvCleaningMode?.text = "纯吸模式"
if (SweeperFutianCmdUtil.checkIfCleanModePureSweep(cleanSystemState)) tvCleaningMode?.text = "纯扫模式"
}
}
//清扫方向
if (SweeperFutianCmdUtil.checkIfCleanDirection(cleanSystemState)) {
tvCleaningDirection.visibility = View.GONE
} else {
tvCleaningDirection.visibility = View.VISIBLE
// 左侧
if (cleanSystemState?.secuWorkLeftSts == true) tvCleaningDirection?.text = "左侧"
// 右侧
if (cleanSystemState?.secuWorkRightSts == true) tvCleaningDirection?.text = "右侧"
// 两侧
if (cleanSystemState?.secuWorkOnBothsidesSts == true) tvCleaningDirection?.text = "两侧"
}
if (SweeperFutianCmdUtil.checkIfCleanIntensity(cleanSystemState)) {
tvCleaningIntensity.visibility = View.GONE
} else {
tvCleaningIntensity.visibility = View.VISIBLE
// 作业强度
if (cleanSystemState?.secuWorkStandSts == true) tvCleaningIntensity?.text = "标准"
if (cleanSystemState?.secuWorkStrongSts == true) tvCleaningIntensity?.text = "强力"
}
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.sweeper.common.bean;
import java.io.Serializable;
import java.util.ArrayList;
/**
* Created by pangfan on 2021/8/19
*
* 订单状态更新请求数据结构
*/
public class SweeperRoutePlanningUpdateReqBean implements Serializable {
public String sn;
public int lineId;
public int startSiteId;
public int endSiteId;
public ArrayList<Result> points;
public static class Result implements Serializable{
public Double latitude;
public Double longitude;
}
public SweeperRoutePlanningUpdateReqBean(String sn, int lineId, int startSiteId
, int endSiteId, ArrayList<Result> points) {
this.sn = sn;
this.lineId = lineId;
this.startSiteId = startSiteId;
this.endSiteId = endSiteId;
this.points = points;
}
}

View File

@@ -0,0 +1,12 @@
package com.mogo.och.sweeper.common.callback;
import com.mogo.och.sweeper.common.constant.OperateStateEnum;
import chassis.ChassisStatesOuterClass;
/**
* 上装状态回调
*/
public interface ICleaningModeStateCallback {
void cleaningModeState(OperateStateEnum cleaningModeState, ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState,boolean isSelectPureSweepMode);
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.sweeper.common.callback;
/**
* Created on 2021/9/8
*
* Model->Presenter回调ADAS相关自动驾驶状态回调到达终点等等
*/
public interface ISweeperADASStatusCallback {
//自驾返回失败
void onStartAdasFailure();
}

View File

@@ -0,0 +1,15 @@
package com.mogo.och.sweeper.common.callback
import com.amap.api.maps.model.LatLng
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean
import com.mogo.och.sweeper.common.database.bean.WeltDataBean
import java.util.ArrayList
interface ISweeperTaskDataToFragmentCallback {
fun setRouteList(routeList: ArrayList<SweeperRoutePlanningUpdateReqBean.Result>)
fun setWeltData(weltDatas: ArrayList<WeltDataBean>?, distance: String)
fun clearAllMarkerAndPolyline()
fun setProgress(progress:String)
fun setTaskListCoordinatesLatLng(coordinatesLatLng: ArrayList<LatLng>)
fun setCurrentTaskCoordinatesLatLng(coordinatesLatLng: LatLng)
}

View File

@@ -0,0 +1,8 @@
package com.mogo.och.sweeper.common.callback
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean
import java.util.ArrayList
interface ISweeperTaskRouteCallback {
fun setRouteList(routeList:ArrayList<SweeperRoutePlanningUpdateReqBean.Result>)
}

View File

@@ -0,0 +1,12 @@
package com.mogo.och.sweeper.common.constant
/**
* 上装面板操作状态
*/
enum class OperateStateEnum(val code: Int) {
SYNCING_STATUS( 1),//清扫车底盘状态同步中
STARTING_STATUS( 2),//上装中
FAIL_STATUS( 3),//上装失败
SUCCESS_STATUS(4),//上装成功
NO_STATUS(-1);//暂无模式
}

View File

@@ -0,0 +1,70 @@
package com.mogo.och.sweeper.common.constant
import com.mogo.commons.debug.DebugConfig
/**
* Created on 2021/12/6
*/
class SweeperConst {
companion object {
// OCH arouter 路由path
const val PATH = "/driver/api"
// 测试用的广播
const val BROADCAST_TEST_SWEEPER_CONTROL_TYPE_EXTRA_KEY = "sceneType"
// 上报心跳轮询ms
const val LOOP_PERIOD_60S = 60 * 1000L
// 开始服务启动自动驾驶等待时间(埋点上传)
const val LOOP_PERIOD_15S = 15 * 1000L
const val LOOP_PERIOD_1S = 1 * 1000L
const val LOOP_DELAY = 100L
// 下发给MEC轨迹信息间隔时间 10秒
const val LOOP_PERIOD_10S = 10 * 1000L
// 尝试下发给MEC轨迹最多10次
const val LOOP_SEND_TRAJ_TIMES = 10
//起点UUID
const val SWEEPER_START_MAP_MAKER = "sweeper_start_map_maker";
//终点UUID
const val SWEEPER_END_MAP_MAKER = "sweeper_end_map_maker";
// 埋点key接管后点击'自动驾驶'按钮启动
const val EVENT_KEY_RESTART_AUTOPILOT = "event_key_och_sweeper_restart_autopilot"
// 埋点key开始服务开启自动驾驶成功/失败)
const val EVENT_KEY_START_SERVICE = "event_key_och_sweeper_start_service"
const val EVENT_PARAM_SN = "sn"
const val EVENT_PARAM_TIME = "time"
const val EVENT_PARAM_START_NAME = "start_name"
const val EVENT_PARAM_END_NAME = "end_name"
const val EVENT_PARAM_LINE_ID = "line_id"
const val EVENT_PARAM_START_RESULT = "start_autopilot" // true/false
const val EVENT_PARAM_START_FAILURE_CODE = "start_autopilot_failure_code" // 启动自驾失败code
const val EVENT_PARAM_START_FAILURE_MSG = "start_autopilot_failure_msg" // 启动自驾失败原因
const val EVENT_PARAM_PLATE_NUM = "plate_number" // 车牌号
const val EVENT_PARAM_ENV_ONLINE = "env_online" // 是否线上环境true/false
// 埋点key开启自动驾驶前已识别的异常会导致无法开启自驾
const val EVENT_KEY_AP_UNABLE_START_REASON = "event_key_och_bus_ap_unable_start_reason"
const val EVENT_PARAM_UNABLE_START_REASON = "unable_start_reason";
/**
* 子任务起终点Marker类型
*/
const val TYPE_MARKER_SWEEPER_SUBTASK_START_END = "TYPE_MARKER_SWEEPER_SUBTASK_START_END"
const val TIMER_START_AUTOPILOT_INTERVAL = 20 * 1000L
//围栏到站 暂定10米
const val ARRIVE_AT_END_STATION_DISTANCE = 10
//非贴边
const val NONWELT = -10000.0
}
}

View File

@@ -0,0 +1,29 @@
package com.mogo.och.sweeper.common.database;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.och.sweeper.common.database.dao.WeltDataDao;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
//注解Database告诉系统这是Room数据库对象
//entities指定该数据库有哪些表多张表就逗号分隔
//version指定数据库版本号升级时需要用到
//数据库继承自RoomDatabase
@Database(entities = {WeltDataBean.class}, version = 1)
public abstract class MyDataBase extends RoomDatabase {
private static final String DATABASE_NAME = "weltData_db";
//结合单例模式完成数据库实例创建
public static MyDataBase getInstance() {
return SingleTon.instance;
}
private static class SingleTon {
private static final MyDataBase instance =
Room.databaseBuilder(AbsMogoApplication.getApp().getApplicationContext(), MyDataBase.class, DATABASE_NAME).build();
}
public abstract WeltDataDao getWeltDataDao();
}

View File

@@ -0,0 +1,127 @@
package com.mogo.och.sweeper.common.database.bean;
import java.io.Serializable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.PrimaryKey;
@Entity(tableName = WeltDataBean.WeltDataTable, indices = {@Index(value = "id", unique = true)})
public class WeltDataBean implements Serializable {
public static final String WeltDataTable = "welt_data_table";
//ColumnInfo用于指定该字段存储在表中的名字并指定类型
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = "id", typeAffinity = ColumnInfo.INTEGER)
private int id;
@ColumnInfo(name = "subTaskId", typeAffinity = ColumnInfo.TEXT)//子任务id
private String subTaskId;
@ColumnInfo(name = "locLon", typeAffinity = ColumnInfo.REAL)
private double locLon;//自车RTK定位经度
@ColumnInfo(name = "locLat", typeAffinity = ColumnInfo.REAL)
private double locLat;//自车RTK定位纬度
@ColumnInfo(name = "weltDistance", typeAffinity = ColumnInfo.REAL)
private double weltDistance;//贴边距离
@ColumnInfo(name = "cleanMode", typeAffinity = ColumnInfo.INTEGER)
private int cleanMode;//清扫作业模式 1纯扫 2--洗扫, 3--纯洗, 4--纯吸
@ColumnInfo(name = "cleanDirection", typeAffinity = ColumnInfo.INTEGER)
private int cleanDirection;//清扫方向 1--两侧, 2--左侧, 3--右侧
@ColumnInfo(name = "cleanIntensity", typeAffinity = ColumnInfo.INTEGER)
private int cleanIntensity;//清扫强度 1--两侧, 2--左侧, 3--右侧
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getSubTaskId() {
return subTaskId;
}
public void setSubTaskId(String subTaskId) {
this.subTaskId = subTaskId;
}
public double getLocLon() {
return locLon;
}
public void setLocLon(double locLon) {
this.locLon = locLon;
}
public double getLocLat() {
return locLat;
}
public void setLocLat(double locLat) {
this.locLat = locLat;
}
public double getWeltDistance() {
return weltDistance;
}
public void setWeltDistance(double weltDistance) {
this.weltDistance = weltDistance;
}
public int getCleanMode() {
return cleanMode;
}
public void setCleanMode(int cleanMode) {
this.cleanMode = cleanMode;
}
public int getCleanDirection() {
return cleanDirection;
}
public void setCleanDirection(int cleanDirection) {
this.cleanDirection = cleanDirection;
}
public int getCleanIntensity() {
return cleanIntensity;
}
public void setCleanIntensity(int cleanIntensity) {
this.cleanIntensity = cleanIntensity;
}
@Override
public String toString() {
return "WeltDataBean{" +
"id=" + id +
", subTaskId='" + subTaskId + '\'' +
", locLon=" + locLon +
", locLat=" + locLat +
", weltDistance=" + weltDistance +
", cleanMode=" + cleanMode +
", cleanDirection=" + cleanDirection +
", cleanIntensity=" + cleanIntensity +
'}';
}
}

View File

@@ -0,0 +1,27 @@
package com.mogo.och.sweeper.common.database.dao;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import java.util.List;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import static com.mogo.och.sweeper.common.database.bean.WeltDataBean.WeltDataTable;
@Dao
public interface WeltDataDao {
//插入数据
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(WeltDataBean fileInfo);
//删除所有数据
@Query("DELETE FROM " + WeltDataBean.WeltDataTable)
int deleteAllWeltData();
//查询所有数据
@Query("SELECT * FROM " + WeltDataBean.WeltDataTable)
List<WeltDataBean> loadAllWeltDataInfo();
}

View File

@@ -0,0 +1,42 @@
package com.mogo.och.sweeper.common.receiver;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_BUS;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.sweeper.common.constant.SweeperConst;
/**
* 测试小巴车的场景
*
* @author donghongyu
* @date 4/26/21 12:08 PM
*/
public class TestSweeperBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "TestBusBroadcastReceiver";
private Context mContext;
@Override
public void onReceive(Context context, Intent intent) {
try {
this.mContext = context;
int sceneType = intent.getIntExtra(SweeperConst.BROADCAST_TEST_SWEEPER_CONTROL_TYPE_EXTRA_KEY, 0);
CallerLogger.d(M_BUS + TAG, "sceneType:" + sceneType);
// 分发场景
dispatchSceneTest(sceneType);
} catch (Exception e) {
e.printStackTrace();
}
}
private void dispatchSceneTest(int sceneType) {
}
}

View File

@@ -0,0 +1,202 @@
package com.mogo.och.sweeper.common.util;
import chassis.ChassisStatesOuterClass;
import chassis.SpecialVehicleTaskCmdOuterClass;
/**
* 清扫车-福田,构建业务命令数据的工具类
*/
public class SweeperFutianCmdUtil {
public static final int CLEAN_WORK_OPEN = 1; //清扫作业-开启
public static final int CLEAN_WORK_CLOSE = 2;//清扫作业-关闭
public static final int CLEAN_MODE_PURE_SWEEP = 1;//作业模式-纯扫
public static final int CLEAN_MODE_WASH_SWEEP = 2;//作业模式-洗扫
public static final int CLEAN_MODE_PURE_WASH = 3;//作业模式-纯洗
public static final int CLEAN_MODE_PURE_DRAW = 4;//作业模式-纯吸
public static final int CLEAN_MODE_CLOSE = 5;//作业模式-关闭
public static final int CLEAN_DIRECTION_BOTH_SIDE = 1;//清扫方向-两侧
public static final int CLEAN_DIRECTION_LEFT_SIDE = 2;//清扫方向-左侧
public static final int CLEAN_DIRECTION_RIGHT_SIDE = 3;//清扫方向-右侧
public static final int CLEAN_DIRECTION_CLOSE = 4;//清扫方向-关闭
public static final int CLEAN_INTENSITY_STRAND = 1;//作业强度-标准
public static final int CLEAN_INTENSITY_STRONG = 2;//作业强度-加强
/**
* 清扫作业:打开
*
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanWorkStartCmd() {
return buildCleanWorkCmd(CLEAN_WORK_OPEN);
}
/**
* 清扫作业:关闭
*
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanWorkStopCmd() {
return buildCleanWorkCmd(CLEAN_WORK_CLOSE);
}
private static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanWorkCmd(int startOrStop) {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanOpenRequirement(startOrStop);
return buildTaskCmd(builder.build());
}
/**
* 作业模式:传入具体的模式对应的值
*
* @param value
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanModeCmd(int value) {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanModeRequirement(value);
return buildTaskCmd(builder.build());
}
/**
* 作业模式:纯吸
*
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanModePureDrawCmd() {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanModeRequirement(CLEAN_MODE_PURE_DRAW);
//不用设置作业方向,自动设置作业强度为:标准
builder.setCleanIntensityRequirement(CLEAN_INTENSITY_STRAND);
return buildTaskCmd(builder.build());
}
/**
* 作业模式:关闭作业模式
*
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanModeCloseCmd() {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanModeRequirement(CLEAN_MODE_CLOSE);
//关闭清扫方向,待下次在选择
builder.setCleanDirectionRequirement(CLEAN_DIRECTION_CLOSE);
//自动设置作业强度为:标准
builder.setCleanIntensityRequirement(CLEAN_INTENSITY_STRAND);
return buildTaskCmd(builder.build());
}
/**
* 作业方向:根据具体的方向传入具体的值
*
* @param value
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanDirectionCmd(int value) {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanDirectionRequirement(value);
builder.setCleanIntensityRequirement(CLEAN_INTENSITY_STRAND);
// 同时作业强度默认:标准
return buildTaskCmd(builder.build());
}
/**
* 作业方向:关闭作业方向
*
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanDirectionCloseCmd() {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanDirectionRequirement(CLEAN_DIRECTION_CLOSE);
//重置作业强度为标准
builder.setCleanIntensityRequirement(CLEAN_INTENSITY_STRAND);
return buildTaskCmd(builder.build());
}
/**
* 作业强度:传入具体的值
*
* @param value
* @return
*/
public static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildCleanIntensityCmd(int value) {
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.Builder builder = SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd.newBuilder();
builder.setCleanIntensityRequirement(value);
return buildTaskCmd(builder.build());
}
private static SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd buildTaskCmd(
SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianCleanCmd fuTianCleanCmd) {
return SpecialVehicleTaskCmdOuterClass.RoboSweeperFuTianTaskCmd.newBuilder()
.setRoboSweeperFutianCleanCmd(fuTianCleanCmd).build();
}
/**
* 判断是否有作业模式
* @param cleanSystemState
* @return true:没有作业模式 false有作业模式
*/
public static boolean checkIfCleanMode(ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState){
return (!cleanSystemState.getSecuModWashSweepSts()&&!cleanSystemState.getSecuModWashSts()&&!cleanSystemState.getSecuWorkTonSts())&&
(!cleanSystemState.getSecuWorkLeftSts()&&!cleanSystemState.getSecuWorkRightSts()&&!cleanSystemState.getSecuWorkOnBothsidesSts());
}
/**
* 判断是否有清扫方向
* @param cleanSystemState
* @return true:没有清扫方向 false有清扫方向
*/
public static boolean checkIfCleanDirection(ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState){
return !cleanSystemState.getSecuWorkLeftSts()&&!cleanSystemState.getSecuWorkRightSts()&&!cleanSystemState.getSecuWorkOnBothsidesSts();
}
/**
* 判断是否有作业强度
* @param cleanSystemState
* @return true:没有作业强度 false有作业强度
*/
public static boolean checkIfCleanIntensity(ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState){
return !cleanSystemState.getSecuWorkStandSts()&&!cleanSystemState.getSecuWorkStrongSts();
}
/**
* 判断是否纯扫模式
*
* @param cleanSystemState
* @return
*/
public static boolean checkIfCleanModePureSweep(ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState) {
// 作业模式状态
// 洗扫
boolean clean_mode_wash_sweep = cleanSystemState.getSecuModWashSweepSts();
// 纯洗
boolean clean_mode_pure_wash = cleanSystemState.getSecuModWashSts();
// 纯吸
boolean clean_mode_pure_draw = cleanSystemState.getSecuWorkTonSts();
// 清扫方向状态
// 左侧
boolean clean_direction_left_side = cleanSystemState.getSecuWorkLeftSts();
// 右侧
boolean clean_direction_right_side = cleanSystemState.getSecuWorkRightSts();
// 两侧
boolean clean_direction_both_side = cleanSystemState.getSecuWorkOnBothsidesSts();
// 纯扫 模式判断不是另外其他3个模式同时有清扫方向说明开启了纯扫模式
boolean clean_mode_pure_sweep = (clean_direction_left_side || clean_direction_right_side || clean_direction_both_side)
&& (!clean_mode_wash_sweep && !clean_mode_pure_wash && !clean_mode_pure_draw);
return clean_mode_pure_sweep;
}
/**
* 构建底盘Mock数据
*
* @return
*/
public static ChassisStatesOuterClass.SweeperFuTianTaskSystemStates buildSweeperFuTionCleanSystemStateMockData() {
ChassisStatesOuterClass.SweeperFuTianTaskSystemStates.Builder builder = ChassisStatesOuterClass.SweeperFuTianTaskSystemStates.newBuilder();
builder.setSecuMotWorkSts(true);
builder.setSecuModWashSts(true);
builder.setSecuWorkOnBothsidesSts(true);
builder.setSecuWorkStrongSts(true);
return builder.build();
}
}

View File

@@ -0,0 +1,61 @@
package com.mogo.och.sweeper.common.util;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
/**
* @author donghongyu
* @date 12/18/20 5:37 PM
*/
public class SweeperMapAssetStyleUtil {
public static byte[] getAssetsStyle(Context context,String fileName) {
byte[] buffer1 = null;
InputStream is1 = null;
try {
is1 = context.getResources().getAssets().open(fileName); //eg. over_view_style.data
int lenght1 = is1.available();
buffer1 = new byte[lenght1];
is1.read(buffer1);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is1 != null) {
is1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return buffer1;
}
public static byte[] getAssetsExtraStyle(Context context, String fileName) {
byte[] buffer1 = null;
InputStream is1 = null;
try {
is1 = context.getResources().getAssets().open(fileName); //eg. over_view_style_extra.data
int lenght1 = is1.available();
buffer1 = new byte[lenght1];
is1.read(buffer1);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is1 != null) {
is1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return buffer1;
}
}

View File

@@ -0,0 +1,32 @@
package com.mogo.och.sweeper.common.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.sweeper_item_legend.view.*
/**
* 任务路线全览图图例
*/
class LegendItemView:LinearLayout {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_item_legend, this)
}
/**
* 设置数据
*/
fun setData(resId:Int,text:String){
sweeperLegendIcon.setImageResource(resId)
sweeperLegendText.text = text
}
}

View File

@@ -0,0 +1,45 @@
package com.mogo.och.sweeper.common.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.constraintlayout.widget.ConstraintLayout;
/**
* 强制拦截所有touch时间的约束布局
*
* @author tongchenfei
*/
public class NoTouchConstraintLayout extends ConstraintLayout {
private boolean interceptTouchEvent = false;
public NoTouchConstraintLayout(Context context) {
super(context);
}
public NoTouchConstraintLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoTouchConstraintLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (interceptTouchEvent) {
return true;
}
return false;
}
/**
* 设置事件拦截
*
* @param interceptTouchEvent
*/
public void setInterceptTouchEvent(boolean interceptTouchEvent) {
this.interceptTouchEvent = interceptTouchEvent;
}
}

View File

@@ -0,0 +1,30 @@
package com.mogo.och.sweeper.common.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
* 强制拦截所有touch时间的约束布局
*
* @author tongchenfei
*/
public class NoTouchFrameLayout extends FrameLayout {
public NoTouchFrameLayout(Context context) {
super(context);
}
public NoTouchFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NoTouchFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
}

View File

@@ -0,0 +1,59 @@
package com.mogo.och.sweeper.common.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import com.mogo.eagle.core.data.enums.DataSourceType
import com.mogo.eagle.core.function.api.hmi.view.IViewControlListener
import com.mogo.eagle.core.function.api.hmi.view.IViewControlListener.Companion.LimitingVelocityView_TAG
import com.mogo.eagle.core.function.api.datacenter.union.ILimitingVelocityListener
import com.mogo.eagle.core.function.call.hmi.CallerHmiViewControlListenerManager
import com.mogo.eagle.core.function.call.v2x.CallerLimitingVelocityListenerManager
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.sweeper_limiting_speed.view.*
class SweeperLimitingVelocityView(
context: Context,
attrs: AttributeSet? = null,
) : LinearLayout(context, attrs), ILimitingVelocityListener, IViewControlListener {
companion object {
private const val TAG = "SweeperLimitingVelocityView"
}
init {
LayoutInflater.from(context).inflate(R.layout.sweeper_limiting_speed, this, true)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerLimitingVelocityListenerManager.addListener(TAG, this)
CallerHmiViewControlListenerManager.addListener(LimitingVelocityView_TAG, this)
}
override fun onLimitingVelocityChange(limitingVelocity: Int, sourceType: DataSourceType) {
ThreadUtils.runOnUiThread {
if (limitingVelocity > 0) {
this.visibility = View.VISIBLE
tvLimitingVelocity.text = "$limitingVelocity"
} else {
this.visibility = View.GONE
}
}
}
override fun visible(v: Int) {
super.visible(v)
this.visibility = v
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerLimitingVelocityListenerManager.removeListener(TAG)
CallerHmiViewControlListenerManager.removeListener(LimitingVelocityView_TAG)
}
}

View File

@@ -0,0 +1,90 @@
package com.mogo.och.sweeper.common.view
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import chassis.Chassis
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.sweeper_turn_signal.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class TurnSignalView : LinearLayout {
constructor(context: Context?) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
private var init: Boolean = false
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
}
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes) {
}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_turn_signal, this)
init = true
}
/**
* 转向灯动画
*/
fun setTurnLight(directionLight: Chassis.LightSwitch?) {
GlobalScope.launch(Dispatchers.Main) {
if (!init) {
return@launch
}
//根据左右进行显示和隐藏,实际要判断每个来的时间和频度
when (directionLight) {
Chassis.LightSwitch.LIGHT_LEFT -> { //左转向
leftSelectImage.visibility = View.VISIBLE
rightSelectImage.visibility = View.GONE
rightSelectImage.clearAnimation()
setAnimation(leftSelectImage)
}
Chassis.LightSwitch.LIGHT_RIGHT -> { //右转向
leftSelectImage.visibility = View.GONE
rightSelectImage.visibility = View.VISIBLE
leftSelectImage.clearAnimation()
setAnimation(rightSelectImage)
}
else -> { //消失
leftSelectImage.clearAnimation()
rightSelectImage.clearAnimation()
leftSelectImage.visibility = View.GONE
rightSelectImage.visibility = View.GONE
}
}
}
}
//实现图片闪烁效果
private fun setAnimation(imageView: ImageView) {
val animationSet = AnimatorSet()
val valueAnimator = ObjectAnimator.ofFloat(imageView, "alpha", 0f, 1.0f)
val valueAnimatorDisappare = ObjectAnimator.ofFloat(imageView, "alpha", 1.0f, 0f)
valueAnimator.duration = 1000
valueAnimatorDisappare.duration = 800
valueAnimator.repeatCount = -1
valueAnimatorDisappare.repeatCount = -1
animationSet.playTogether(valueAnimatorDisappare, valueAnimator)
animationSet.start()
}
}

View File

@@ -0,0 +1,64 @@
package com.mogo.och.sweeper.common.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
/**
* 垂直虚线
*
* @author tongchenfei
*/
public class VerticalDashLineView extends View {
public VerticalDashLineView(Context context) {
this(context,null);
}
public VerticalDashLineView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public VerticalDashLineView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private final Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path dashPath = new Path();
private void init(){
linePaint.setColor(Color.GREEN);
linePaint.setStyle(Paint.Style.STROKE);
linePaint.setStrokeWidth(2);
linePaint.setPathEffect(new DashPathEffect(new float[]{5, 5}, 0));
}
public void setGradient(int startColor, int endColor) {
LinearGradient linearGradient = new LinearGradient(0, 0, getWidth(), getHeight(), startColor, endColor, Shader.TileMode.CLAMP);
linePaint.setShader(linearGradient);
invalidate();
}
public void setColor(int color) {
linePaint.setShader(null);
linePaint.setColor(color);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
dashPath.reset();
dashPath.moveTo((float) getWidth()/2, 0);
dashPath.lineTo((float) getWidth()/2,getHeight());
canvas.drawPath(dashPath,linePaint);
}
}

View File

@@ -0,0 +1,342 @@
package com.mogo.och.sweeper.common.view
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.amap.api.maps.AMap
import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.model.*
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
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.eagle.core.utilcode.util.ThreadUtils
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean
import com.mogo.och.sweeper.common.constant.SweeperConst
import com.mogo.och.sweeper.common.database.bean.WeltDataBean
import com.mogo.och.sweeper.common.util.SweeperMapAssetStyleUtil
import kotlinx.android.synthetic.main.sweeper_welt_map_overview.view.*
/**
* 作业任务全览图
*/
class WeltMapOverView : ConstraintLayout, IMoGoChassisLocationGCJ02Listener {
private var mTaskCoordinatesLatLng: MutableList<LatLng> = mutableListOf() //当前大任务的所有起终点集合
private var mCarMarker: Marker? = null
private var mAMap: AMap? = null
private var mWeltPolylines: Polyline? = null
private var mRoutePolylines: Polyline?=null
// private val mLineMarkers: MutableList<Marker?> = mutableListOf()
private var mEndStationMarker: Marker? = null
private var mFirst: Boolean = false
//清扫车任务地图
private val TAG = "WeltMapOverView"
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_welt_map_overview, this)
initAMapView()
// 注册定位监听
CallerChassisLocationGCJ02ListenerManager.addListener(TAG, this)
CallerChassisLocationGCJ02ListenerManager.setListenerHz(TAG,5)
}
private fun initAMapView() {
mAMap = sweeperTextureMapView.map
// 地图文字标注
mAMap?.showMapText(true)
//显示3D建筑物
mAMap?.showBuildings(true)
// 设置导航地图模式aMap是地图控制器对象。
mAMap?.mapType = AMap.MAP_TYPE_NIGHT
// 关闭显示实时路况图层aMap是地图控制器对象。
mAMap?.isTrafficEnabled = false
// 设置 锚点 图标
mCarMarker = mAMap?.addMarker(
MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.drawable.sweeper_car_big))
.anchor(0.5f, 0.5f)
)
// 设置地图的样式
val uiSettings = mAMap?.uiSettings
uiSettings?.isZoomControlsEnabled = false // 地图缩放级别的交换按钮
uiSettings?.setAllGesturesEnabled(false) // 所有手势
uiSettings?.isMyLocationButtonEnabled = false // 显示默认的定位按钮
uiSettings?.setLogoBottomMargin(-150) //设置Logo下边界距离屏幕底部的边距,设置为负值即可
// 加载自定义样式
val customMapStyleOptions = CustomMapStyleOptions()
.setEnable(true)
.setStyleData(SweeperMapAssetStyleUtil.getAssetsStyle(context, "map_style.data"))
.setStyleExtraData(SweeperMapAssetStyleUtil.getAssetsExtraStyle(context, "map_style_extra.data"))
// 设置自定义样式
mAMap?.setCustomMapStyle(customMapStyleOptions)
//mAMap?.moveCamera(CameraUpdateFactory.zoomTo(15f))
mAMap?.setOnMapLoadedListener(AMap.OnMapLoadedListener {
//mAMap?.moveCamera(CameraUpdateFactory.zoomTo(15f))
CallerLogger.d(SceneConstant.M_SWEEPER + TAG, "WeltView---onMapLoaded")
// 加载自定义样式
val customMapStyleOptions = CustomMapStyleOptions()
.setEnable(true)
.setStyleData(SweeperMapAssetStyleUtil.getAssetsStyle(context, "map_style.data"))
.setStyleExtraData(SweeperMapAssetStyleUtil.getAssetsExtraStyle(context, "map_style_extra.data"))
// 设置自定义样式
mAMap?.setCustomMapStyle(customMapStyleOptions)
// 实时路况图层关闭必须添加在loaded结束之后,其他位置不生效
mAMap?.isTrafficEnabled = false
mAMap?.showBuildings(true)
})
}
/**
* 添加画线颜色值
*/
private fun getRouteColorList(weltData: MutableList<WeltDataBean>):MutableList<Int> {
val colorList= mutableListOf<Int>()
var nextWeltDataBean: WeltDataBean?=null
for (i in weltData.indices) {
val weltDataBean = weltData[i]
if(i+1<weltData.size){
nextWeltDataBean = weltData[i+1]
val calculateLineDistance = CoordinateUtils.calculateLineDistance(
nextWeltDataBean.locLon, nextWeltDataBean.locLat,
weltDataBean.locLon, weltDataBean.locLat
)
// 大于10m
if(calculateLineDistance>10){
colorList.add(Color.TRANSPARENT)
continue
}
}
if (weltDataBean.weltDistance == SweeperConst.NONWELT) {//非贴边
colorList.add(ContextCompat.getColor(context,R.color.sweeper_236299))
} else if (weltDataBean.weltDistance < 0) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_c22101))
} else if (weltDataBean.weltDistance >= 0 && weltDataBean.weltDistance < 0.1) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_4dffa4))
} else if (weltDataBean.weltDistance >= 0.1 && weltDataBean.weltDistance < 0.2) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_ffdd4d))
} else if (weltDataBean.weltDistance >= 0.2) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_ff912b))
}
}
return colorList
}
override fun onChassisLocationGCJ02(mogoLocation: MogoLocation?) {
ThreadUtils.getSinglePool().run {
mogoLocation?.let { gnssInfo ->
val currentLatLng = LatLng(gnssInfo.latitude, gnssInfo.longitude)
//更新车辆位置
mCarMarker?.rotateAngle = (360 - gnssInfo.heading).toFloat()
mCarMarker?.position = currentLatLng
mCarMarker?.setToTop()
//圈定地图显示范围
val boundsBuilder = LatLngBounds.Builder()
if (mTaskCoordinatesLatLng.size > 0) {
//存放经纬度
for (i in mTaskCoordinatesLatLng.indices) {
val latLng = mTaskCoordinatesLatLng[i]
boundsBuilder.include(latLng)
}
}
mRoutePolylines?.points?.forEach {latLng->
boundsBuilder.include(latLng)
}
boundsBuilder.include(currentLatLng)
mAMap?.moveCamera(CameraUpdateFactory.newLatLngBoundsRect(boundsBuilder.build(), 100, 100, 100, 100))
}
}
}
/**
* 根据贴边数据绘制任务路线
*/
fun drawablePolyline(weltDatas: MutableList<WeltDataBean>?) {
weltDatas?.let {
val colorList=getRouteColorList(it)
val coordinatesLatLngs= mutableListOf<LatLng>()
for (i in it.indices) {
coordinatesLatLngs.add(LatLng(it[i].locLat,it[i].locLon))
}
if (coordinatesLatLngs.size > 2) {
//设置线段纹理
mWeltPolylines?.remove()
val polylineOptions = PolylineOptions()
polylineOptions.addAll(coordinatesLatLngs)
polylineOptions.width(14f) //线段宽度
polylineOptions.isUseTexture = false
polylineOptions.lineCapType(PolylineOptions.LineCapType.LineCapSquare)
polylineOptions.colorValues(colorList)
polylineOptions.visible(true)
// 绘制线
mWeltPolylines = mAMap?.addPolyline(polylineOptions)
}
}
}
/**
* 绘制起点和终点的marker
*/
private fun drawStartAndEndMarker(startPoint: LatLng, endPoint: LatLng) {
// val startMarker = mAMap?.addMarker(MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.sweeper_big_start_maker_icon)))
// startMarker?.position = startPoint
// mLineMarkers.add(startMarker)
// val endMarker = mAMap?.addMarker(MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.sweeper_big_end_maker_icon)))
// endMarker?.position = endPoint
// mLineMarkers.add(endMarker)
}
private fun drawEndMarker(endPoint: LatLng){
mEndStationMarker?.remove()
mEndStationMarker = mAMap?.addMarker(MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.sweeper_big_end_maker_icon)))
mEndStationMarker?.position = endPoint
}
/**
* 清除所有标记和路线
*/
fun clearAllMarkerAndPolyline() {
// for (i in mLineMarkers.indices) {
// mLineMarkers[i]?.isVisible = false
// mLineMarkers[i]?.remove()
// }
mEndStationMarker?.remove()
mWeltPolylines?.remove()
mRoutePolylines?.remove()
// mLineMarkers.clear()
//mFirst = false
//showOrHiddenLegendData(false)
}
/**
* 设置贴边数据
*/
fun setWeltData(weltDatas: MutableList<WeltDataBean>?, isWeltData: Boolean, distance: String?) {
drawablePolyline(weltDatas)
ThreadUtils.runOnUiThread {
distance?.let {
setDistance(it)
}
}
//if (!mFirst && isWeltData) {
// mFirst = true
// showOrHiddenLegendData(true)
//}
}
/**
* 任务轨迹数据
*/
fun setRouteList(routeList: ArrayList<SweeperRoutePlanningUpdateReqBean.Result>) {
val routeCoordinatesLatLngs= mutableListOf<LatLng>()
for (i in routeList.indices) {
routeCoordinatesLatLngs.add(LatLng(routeList[i].latitude,routeList[i].longitude))
}
ThreadUtils.runOnUiThread {
drawRouteListPolyline(routeCoordinatesLatLngs)
}
}
private fun drawRouteListPolyline(routeList: MutableList<LatLng>) {
mRoutePolylines?.remove()
val polylineOptions = PolylineOptions()
polylineOptions.width(14f) //线段宽度
polylineOptions.isUseTexture = false
polylineOptions.lineCapType(PolylineOptions.LineCapType.LineCapSquare)
polylineOptions.color(ContextCompat.getColor(context,R.color.sweeper_3ba1cc))
polylineOptions.addAll(routeList)
polylineOptions.visible(true)
mRoutePolylines=mAMap?.addPolyline(polylineOptions)
}
/**
* 设置当前大任务的所有子任务起终点集合
*/
fun setTaskListCoordinatesLatLng(coordinatesLatLng: MutableList<LatLng>) {
ThreadUtils.runOnUiThread {
this.mTaskCoordinatesLatLng = coordinatesLatLng
if (mTaskCoordinatesLatLng.size > 0) {
drawStartAndEndMarker(mTaskCoordinatesLatLng[0], mTaskCoordinatesLatLng[mTaskCoordinatesLatLng.size - 1])
}
}
}
/**
* 设置当前大任务的所有子任务起终点集合
*/
fun setCurrentTaskCoordinatesLatLng(coordinatesLatLng: LatLng) {
ThreadUtils.runOnUiThread {
drawEndMarker(coordinatesLatLng)
}
}
/**
* 设置图例数据
*/
private fun showOrHiddenLegendData(isShow: Boolean) {
groupLegend.visibility = if (isShow) View.VISIBLE else View.GONE
sweeperLegend1.setData(R.drawable.sweeper_legend1, "a<0")
sweeperLegend2.setData(R.drawable.sweeper_legend2, "0≤a<10")
sweeperLegend3.setData(R.drawable.sweeper_legend3, "10≤a<20")
sweeperLegend4.setData(R.drawable.sweeper_legend4, "a≥20")
sweeperLegend5.setData(R.drawable.sweeper_legend5, "非贴边")
sweeperLegend6.setData(R.drawable.sweeper_legend6, "未经过")
}
private fun setDistance(distance: String) {
taskWeltDistanceTv.text = "贴边:${distance}"
}
fun setProgress(progress: String?) {
progress?.let {
if ("0" == progress) {
taskProgressTv.visibility = View.GONE
taskWeltDistanceTv.visibility = View.GONE
showOrHiddenLegendData(false)
} else {
taskProgressTv.visibility = View.VISIBLE
taskWeltDistanceTv.visibility = View.VISIBLE
taskProgressTv.text = it
showOrHiddenLegendData(true)
}
}
}
fun onCreateView(savedInstanceState: Bundle?) {
sweeperTextureMapView.onCreate(savedInstanceState)
}
fun onResume() {
sweeperTextureMapView.onResume()
}
fun onPause() {
sweeperTextureMapView.onPause()
}
fun onDestroy() {
sweeperTextureMapView.onDestroy()
}
fun getSweeperSwitchToSmall(): ImageView {
return sweeperSwitchToSmall
}
}

View File

@@ -0,0 +1,301 @@
package com.mogo.och.sweeper.common.view
import android.content.Context
import android.graphics.Color
import android.os.Bundle
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.amap.api.maps.AMap
import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.model.*
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener
import com.mogo.eagle.core.function.call.autopilot.CallerChassisLocationGCJ02ListenerManager
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.CoordinateUtils
import com.mogo.eagle.core.utilcode.util.ThreadUtils
import com.mogo.och.sweeper.common.database.bean.WeltDataBean
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.sweeper_welt_small_map_view.view.*
/**
* 作业任务小地图
*/
class WeltSmallMapView : ConstraintLayout, IMoGoChassisLocationGCJ02Listener {
private var mTaskCoordinatesLatLng: MutableList<LatLng> = mutableListOf() //当前大任务的所有起终点集合
private var mCarMarker: Marker? = null
private var mAMap: AMap? = null
private var mWeltPolylines: Polyline? = null
private var mRoutePolylines: Polyline?=null
// private val mLineMarkers: MutableList<Marker?> = mutableListOf()
private var endStationMarker:Marker? = null
//清扫车任务地图
private val TAG = "WeltMapView"
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_welt_small_map_view, this)
initAMapView()
// 注册定位监听
CallerChassisLocationGCJ02ListenerManager.addListener(TAG, this)
CallerChassisLocationGCJ02ListenerManager.setListenerHz(TAG,5)
}
override fun onChassisLocationGCJ02(mogoLocation: MogoLocation?) {
d(SceneConstant.M_SWEEPER + TAG, "mogoLocation$mogoLocation")
ThreadUtils.getSinglePool().run {
mogoLocation?.let {
val currentLatLng = LatLng(it.latitude, it.longitude)
drawCarMarker(mogoLocation)
//圈定地图显示范围
val boundsBuilder = LatLngBounds.Builder()
if (mTaskCoordinatesLatLng.size > 0) {
//存放经纬度
for (i in mTaskCoordinatesLatLng.indices) {
boundsBuilder.include(mTaskCoordinatesLatLng[i])
}
}
mRoutePolylines?.points?.forEach {latLng->
boundsBuilder.include(latLng)
}
boundsBuilder.include(currentLatLng)
mAMap!!.moveCamera(CameraUpdateFactory.newLatLngBoundsRect(boundsBuilder.build(), 100, 100, 100, 100))
}
}
}
/**
* 绘制自车
*
* @param location
*/
private fun drawCarMarker(location: MogoLocation?) {
if (location == null) return
val currentLatLng = LatLng(location.latitude, location.longitude)
//更新车辆位置
if (mCarMarker != null) {
mCarMarker!!.rotateAngle = (360 - location.heading).toFloat()
mCarMarker!!.position = currentLatLng
mCarMarker!!.setToTop()
}
}
private fun initAMapView() {
mAMap = sweeperSmallTextureMapView?.map
// 地图文字标注
mAMap?.showMapText(true)
// 设置导航地图模式aMap是地图控制器对象。
mAMap?.mapType = AMap.MAP_TYPE_NIGHT
// 关闭显示实时路况图层aMap是地图控制器对象。
mAMap?.isTrafficEnabled = false
// 设置 锚点 图标
mCarMarker = mAMap?.addMarker(
MarkerOptions()
.icon(BitmapDescriptorFactory.fromResource(R.drawable.sweeper_car_small))
.anchor(0.5f, 0.5f)
)
// 设置地图的样式
val uiSettings = mAMap?.uiSettings
uiSettings?.isZoomControlsEnabled = false // 地图缩放级别的交换按钮
uiSettings?.setAllGesturesEnabled(false) // 所有手势
uiSettings?.isMyLocationButtonEnabled = false // 显示默认的定位按钮
uiSettings?.setLogoBottomMargin(-150) //设置Logo下边界距离屏幕底部的边距,设置为负值即可
// 加载自定义样式
val customMapStyleOptions = CustomMapStyleOptions()
.setEnable(true)
.setStyleData(com.mogo.och.sweeper.common.util.SweeperMapAssetStyleUtil.getAssetsStyle(context, "map_style.data"))
.setStyleExtraData(com.mogo.och.sweeper.common.util.SweeperMapAssetStyleUtil.getAssetsExtraStyle(context, "map_style_extra.data"))
// 设置自定义样式
mAMap?.setCustomMapStyle(customMapStyleOptions)
mAMap?.setOnMapLoadedListener(AMap.OnMapLoadedListener {
d(SceneConstant.M_SWEEPER + TAG, "WeltView---onMapLoaded")
// 加载自定义样式
val customMapStyleOptions = CustomMapStyleOptions()
.setEnable(true)
.setStyleData(com.mogo.och.sweeper.common.util.SweeperMapAssetStyleUtil.getAssetsStyle(context, "map_style.data"))
.setStyleExtraData(com.mogo.och.sweeper.common.util.SweeperMapAssetStyleUtil.getAssetsExtraStyle(context, "map_style_extra.data"))
// 设置自定义样式
mAMap?.setCustomMapStyle(customMapStyleOptions)
mAMap?.setPointToCenter(sweeperSmallTextureMapView.width / 2, sweeperSmallTextureMapView.height / 2)
})
}
/**
* 根据贴边数据绘制任务路线
*/
private fun drawablePolyline(weltDatas: MutableList<WeltDataBean>?) {
weltDatas?.let {
val colorList = getRouteColorList(it)
val coordinatesLatLngs = mutableListOf<LatLng>()
for (i in it.indices) {
coordinatesLatLngs.add(LatLng(it[i].locLat,it[i].locLon))
}
if (coordinatesLatLngs.size > 2) {
//设置线段纹理
mWeltPolylines?.remove()
val polylineOptions = PolylineOptions()
polylineOptions.addAll(coordinatesLatLngs)
polylineOptions.width(14f) //线段宽度
polylineOptions.isUseTexture = false
polylineOptions.lineCapType(PolylineOptions.LineCapType.LineCapSquare)
polylineOptions.colorValues(colorList)
polylineOptions.visible(true)
// 绘制线
mWeltPolylines = mAMap?.addPolyline(polylineOptions)
}
}
}
/**
* 添加画线颜色值
*/
private fun getRouteColorList(weltData: MutableList<WeltDataBean>): MutableList<Int> {
val colorList = mutableListOf<Int>()
var nextWeltDataBean:WeltDataBean?=null
for (i in weltData.indices) {
val weltDataBean = weltData[i]
if(i+1<weltData.size){
nextWeltDataBean = weltData[i+1]
val calculateLineDistance = CoordinateUtils.calculateLineDistance(
nextWeltDataBean.locLon, nextWeltDataBean.locLat,
weltDataBean.locLon, weltDataBean.locLat
)
// 大于10m
if(calculateLineDistance>10){
colorList.add(Color.TRANSPARENT)
continue
}
}
if (weltDataBean.weltDistance == com.mogo.och.sweeper.common.constant.SweeperConst.NONWELT) {//非贴边
colorList.add(ContextCompat.getColor(context,R.color.sweeper_236299))
} else if (weltDataBean.weltDistance < 0) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_c22101))
} else if (weltDataBean.weltDistance >= 0 && weltDataBean.weltDistance < 0.1) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_4dffa4))
} else if (weltDataBean.weltDistance >= 0.1 && weltDataBean.weltDistance < 0.2) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_ffdd4d))
} else if (weltDataBean.weltDistance >= 0.2) {
colorList.add(ContextCompat.getColor(context,R.color.sweeper_ff912b))
}
}
return colorList
}
/**
* 设置当前大任务的所有子任务起终点集合
*/
fun setTaskListCoordinatesLatLng(coordinatesLatLng: MutableList<LatLng>) {
ThreadUtils.runOnUiThread {
this.mTaskCoordinatesLatLng = coordinatesLatLng
}
}
/**
* 设置当前任务的终点
*/
fun setCurrentTaskCoordinatesLatLng(coordinatesLatLng: LatLng) {
ThreadUtils.runOnUiThread {
endStationMarker?.remove()
endStationMarker = mAMap?.addMarker(MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.sweeper_small_end_marker_icon)))
endStationMarker?.position = coordinatesLatLng
}
}
/**
* 清除所有标记和路线
*/
fun clearAllMarkerAndPolyline() {
endStationMarker?.remove()
mWeltPolylines?.remove()
mRoutePolylines?.remove()
}
fun getSwitchToBig(): ImageView = sweeperSwitchToBig
/**
* 设置贴边数据
*/
fun setWeltData(weltDatas: MutableList<WeltDataBean>, isWeltData: Boolean, distance: String) {
drawablePolyline(weltDatas)
ThreadUtils.runOnUiThread {
setWeltDistance(distance)
}
}
/**
* 设置贴边距离
*/
private fun setWeltDistance(distance: String) {
taskWeltDistanceTv.text = "贴边:${distance}"
}
/**
* 设置任务轨迹数据
*/
fun setRouteList(routeList: ArrayList<com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean.Result>) {
val routeCoordinatesLatLngs = mutableListOf<LatLng>()
for (i in routeList.indices) {
routeCoordinatesLatLngs.add(LatLng(routeList[i].latitude, routeList[i].longitude))
}
ThreadUtils.runOnUiThread {
drawRouteListPolyline(routeCoordinatesLatLngs)
}
}
private fun drawRouteListPolyline(routeList: MutableList<LatLng>) {
mRoutePolylines?.remove()
val polylineOptions = PolylineOptions()
polylineOptions.addAll(routeList)
polylineOptions.width(14f) //线段宽度
polylineOptions.isUseTexture = false
polylineOptions.lineCapType(PolylineOptions.LineCapType.LineCapSquare)
polylineOptions.color(ContextCompat.getColor(context,R.color.sweeper_3ba1cc))
polylineOptions.visible(true)
mRoutePolylines=mAMap?.addPolyline(polylineOptions)
}
/**
* 设置任务进度
*/
fun setTaskProgress(progress: String?) {
if (progress == "0") {
taskWeltDistanceTv.visibility = View.GONE
taskProgressTv.visibility = View.GONE
} else {
taskWeltDistanceTv.visibility = View.VISIBLE
taskProgressTv.visibility = View.VISIBLE
taskProgressTv.text = progress
}
}
fun onCreateView(savedInstanceState: Bundle?) {
sweeperSmallTextureMapView?.onCreate(savedInstanceState)
}
fun onResume() {
sweeperSmallTextureMapView?.onResume()
}
fun onPause() {
sweeperSmallTextureMapView?.onPause()
}
fun onDestroy() {
sweeperSmallTextureMapView?.onDestroy()
}
}

View File

@@ -0,0 +1,31 @@
package com.mogo.och.sweeper.operate.bean;
import com.mogo.eagle.core.data.BaseData;
public class BaseResponse<T> extends BaseData {
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}

View File

@@ -0,0 +1,11 @@
package com.mogo.och.sweeper.operate.bean
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
data class SubStartRequest(
var carSn:String=MoGoAiCloudClientConfig.getInstance().sn,
var taskId:Int,
var startTime:Long,
var isFirst:Boolean,
var isEnd:Boolean
)

View File

@@ -0,0 +1,14 @@
package com.mogo.och.sweeper.operate.bean
/**
*
* 清扫车任务信息
*
* @author tongchenfei
*/
data class SweeperMainTaskBean(
var mainTaskId:Int = 0,
var mainTaskName: String? = null,
var subTaskTotal:Int = 0,
var mainTaskStartTime: Long = 0
)

View File

@@ -0,0 +1,28 @@
package com.mogo.och.sweeper.operate.bean
/**
* 子任务信息
*/
data class SweeperSubTaskBean(
var taskId: Int = 0,
var taskName: String?,
var taskStartTime: Long = 0,
var sort: Int = 1,
var subList: MutableList<SubInfo>?
)
data class SubInfo(
var taskId: Int = 0,
var taskName: String?,
var mileage: Int = 0,
var taskStatus: Int,// 状态1未执行 2执行中 3结束 4跳过
var taskType: Int,//1自动驾驶 2人工驾驶
var timeSpent: String,
var startSiteName: String,//起点名称
var startWgs84Lon: Double,//起点经度
var startWgs84Lat: Double,//起点纬度
var endSiteName: String,//终点名称
var endWgs84Lon: Double,//终点经度
var endWgs84Lat: Double,//终点纬度
var sort: Int = 0,
)

View File

@@ -0,0 +1,314 @@
package com.mogo.och.sweeper.operate.bean;
public class SweeperSubTaskDetailBean {
private int id; //子任务id
private int lineId; //线路id
private String lineName;//线路名称
private int sort;
private int status;
private int startSiteId; //子任务起点id
private String startSiteName; //子任务起点名称
private double startSiteLon; //子任务起点高德经度
private double startSiteLat; //子任务起点高德纬度
private double startSiteWgs64Lon;//子任务起点高精经度
private double startSiteWgs64Lat;//子任务起点高精纬度
private int endSiteId;//子任务终点id
private String endSiteName;//子任务终点名称
private double endSiteLon; //子任务终点高德经度
private double endSiteLat;//子任务终点高德纬度
private double endSiteWgs64Lon;//子任务终点高精经度
private double endSiteWgs64Lat;//子任务终点高精纬度
private String brand; //品牌
private String carModel; //车辆型号
private String csvFileUrl = ""; //轨迹文件csv文件url地址
private String csvFileMd5 = ""; //轨迹文件csv md5
private String csvFileName = "";//轨迹文件cvs名称
private String txtFileUrl = ""; //轨迹文件txt url地址
private String txtFileMd5 = ""; //轨迹文件txt md5
private String txtFileName; //轨迹文件txt名称
private long publishTime;
private String contrailFileType; //轨迹文件类型
private String publishStatus;//发布状态 1发布 2未发布 3 发布中
private String csvFileUrlDPQP = ""; //轨迹文件下载的cos url默认“”
private String csvFileMd5DPQP = ""; //轨迹文件md5默认“”
private String txtFileUrlDPQP = ""; //打点文件下载的cos url默认“”
private String txtFileMd5DPQP = ""; //轨迹文件md5默认“”
private long publishTimeDPQP; //上传轨迹完成时间戳ms用于MEC本地手动导入轨迹验证时不会被云端轨迹覆盖
private long taskCreateTime;
private String brandDPQP;
private String carModelDPQP;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getLineId() {
return lineId;
}
public void setLineId(int lineId) {
this.lineId = lineId;
}
public String getLineName() {
return lineName;
}
public void setLineName(String lineName) {
this.lineName = lineName;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public int getStartSiteId() {
return startSiteId;
}
public void setStartSiteId(int startSiteId) {
this.startSiteId = startSiteId;
}
public String getStartSiteName() {
return startSiteName;
}
public void setStartSiteName(String startSiteName) {
this.startSiteName = startSiteName;
}
public double getStartSiteLon() {
return startSiteLon;
}
public void setStartSiteLon(double startSiteLon) {
this.startSiteLon = startSiteLon;
}
public double getStartSiteLat() {
return startSiteLat;
}
public void setStartSiteLat(double startSiteLat) {
this.startSiteLat = startSiteLat;
}
public double getStartSiteWgs64Lon() {
return startSiteWgs64Lon;
}
public void setStartSiteWgs64Lon(double startSiteWgs64Lon) {
this.startSiteWgs64Lon = startSiteWgs64Lon;
}
public double getStartSiteWgs64Lat() {
return startSiteWgs64Lat;
}
public void setStartSiteWgs64Lat(double startSiteWgs64Lat) {
this.startSiteWgs64Lat = startSiteWgs64Lat;
}
public int getEndSiteId() {
return endSiteId;
}
public void setEndSiteId(int endSiteId) {
this.endSiteId = endSiteId;
}
public String getEndSiteName() {
return endSiteName;
}
public void setEndSiteName(String endSiteName) {
this.endSiteName = endSiteName;
}
public double getEndSiteLon() {
return endSiteLon;
}
public void setEndSiteLon(double endSiteLon) {
this.endSiteLon = endSiteLon;
}
public double getEndSiteLat() {
return endSiteLat;
}
public void setEndSiteLat(double endSiteLat) {
this.endSiteLat = endSiteLat;
}
public double getEndSiteWgs64Lon() {
return endSiteWgs64Lon;
}
public void setEndSiteWgs64Lon(double endSiteWgs64Lon) {
this.endSiteWgs64Lon = endSiteWgs64Lon;
}
public double getEndSiteWgs64Lat() {
return endSiteWgs64Lat;
}
public void setEndSiteWgs64Lat(double endSiteWgs64Lat) {
this.endSiteWgs64Lat = endSiteWgs64Lat;
}
public long getTaskCreateTime() {
return taskCreateTime;
}
public void setTaskCreateTime(long taskCreateTime) {
this.taskCreateTime = taskCreateTime;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getCarModel() {
return carModel;
}
public void setCarModel(String carModel) {
this.carModel = carModel;
}
public String getCsvFileUrl() {
return csvFileUrl;
}
public void setCsvFileUrl(String csvFileUrl) {
this.csvFileUrl = csvFileUrl;
}
public String getCsvFileMd5() {
return csvFileMd5;
}
public void setCsvFileMd5(String csvFileMd5) {
this.csvFileMd5 = csvFileMd5;
}
public String getCsvFileName() {
return csvFileName;
}
public void setCsvFileName(String csvFileName) {
this.csvFileName = csvFileName;
}
public String getTxtFileUrl() {
return txtFileUrl;
}
public void setTxtFileUrl(String txtFileUrl) {
this.txtFileUrl = txtFileUrl;
}
public String getTxtFileMd5() {
return txtFileMd5;
}
public void setTxtFileMd5(String txtFileMd5) {
this.txtFileMd5 = txtFileMd5;
}
public String getTxtFileName() {
return txtFileName;
}
public void setTxtFileName(String txtFileName) {
this.txtFileName = txtFileName;
}
public long getPublishTime() {
return publishTime;
}
public void setPublishTime(long publishTime) {
this.publishTime = publishTime;
}
public String getBrandDPQP() {
return brandDPQP;
}
public void setBrandDPQP(String brandDPQP) {
this.brandDPQP = brandDPQP;
}
public String getCarModelDPQP() {
return carModelDPQP;
}
public void setCarModelDPQP(String carModelDPQP) {
this.carModelDPQP = carModelDPQP;
}
public String getCsvFileUrlDPQP() {
return csvFileUrlDPQP;
}
public void setCsvFileUrlDPQP(String csvFileUrlDPQP) {
this.csvFileUrlDPQP = csvFileUrlDPQP;
}
public String getCsvFileMd5DPQP() {
return csvFileMd5DPQP;
}
public void setCsvFileMd5DPQP(String csvFileMd5DPQP) {
this.csvFileMd5DPQP = csvFileMd5DPQP;
}
public String getTxtFileUrlDPQP() {
return txtFileUrlDPQP;
}
public void setTxtFileUrlDPQP(String txtFileUrlDPQP) {
this.txtFileUrlDPQP = txtFileUrlDPQP;
}
public String getTxtFileMd5DPQP() {
return txtFileMd5DPQP;
}
public void setTxtFileMd5DPQP(String txtFileMd5DPQP) {
this.txtFileMd5DPQP = txtFileMd5DPQP;
}
public long getPublishTimeDPQP() {
return publishTimeDPQP;
}
public void setPublishTimeDPQP(long publishTimeDPQP) {
this.publishTimeDPQP = publishTimeDPQP;
}
}

View File

@@ -0,0 +1,17 @@
package com.mogo.och.sweeper.operate.callback;
import com.mogo.eagle.core.data.map.MogoLocation;
/**
* Created on 2021/9/10
*
* Model->Presenter回调状态控制器监听accOn、adas ui show、voice ui show、push ui show、v2x ui show等等
*/
public interface ISweeperControllerStatusCallback {
// 是否vr map模式
void onVRModeChanged(boolean isVRMode);
// 自车定位
void onCarLocationChanged(MogoLocation location);
//开始开启自动驾驶
void startOpenAutopilot();
}

View File

@@ -0,0 +1,46 @@
package com.mogo.och.sweeper.operate.callback
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskDetailBean
import com.mogo.och.sweeper.operate.constant.SubTaskTypeEnum
import com.mogo.och.sweeper.operate.constant.TaskStatusEnum
import java.util.ArrayList
/**
* @author: wangmingjun
* @date: 2021/10/22
*/
interface ISweeperTaskCallback {
/**
* 获取主任务列表
*/
fun setMainTaskList(mainTaskBeanList: MutableList<SweeperMainTaskBean>?, refresh:Boolean)
/**
* 获取子任务列表
*/
fun setSubTaskBean(subTaskBean: SweeperSubTaskBean, isWorkingSubTask:Boolean)
/**
* 更新子任务状态
*/
fun updateSubTaskStatus(typeEnum: TaskStatusEnum, isLastSubTask: Boolean)
/**
* 主任务重置
*/
fun setMainTaskReset(isSuccess: Boolean)
/**
* 获取子任务详情包括轨迹信息
*/
fun setSubTakDetail(subTaskDetailBean: SweeperSubTaskDetailBean, subTaskTypeEnum: SubTaskTypeEnum)
/**
* 设置轨迹坐标点集合
*/
fun setRouteList(routeList: ArrayList<SweeperRoutePlanningUpdateReqBean.Result>)
}

View File

@@ -0,0 +1,5 @@
package com.mogo.och.sweeper.operate.callback
interface IWeltMapSwitchToSmallCallback {
fun onWeltMapSwitchToSmall()
}

View File

@@ -0,0 +1,9 @@
package com.mogo.och.sweeper.operate.constant
/**
* 任务类型
*/
enum class SubTaskTypeEnum(val code: Int) {
AUTOPILOT_SUBTYPE(1),//自动驾驶子任务
MANUAL_DRIVING_SUBTYPE(2),//人工驾驶
}

View File

@@ -0,0 +1,12 @@
package com.mogo.och.sweeper.operate.constant
/**
* 当前任务操作菜单
*/
enum class TaskStatusEnum(val code: Int) {
JUMP_OVER_SUBTASK( 1),//跳过子任务
END_TASK( 2),//结束主任务
CANCEL_TASK( 3),//取消
START_SUBTASK(4),//开始子任务
END_SUBTASK(5),//结束子任务
}

View File

@@ -0,0 +1,58 @@
package com.mogo.och.sweeper.operate.event;
import com.amap.api.maps.model.LatLng;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import java.util.ArrayList;
public class WeltDataEvent{
private int type; //1.添加起点和终点 2.实时添加贴边数据 3.清除数据 4.任务进度 5:任务轨迹数据
private ArrayList<WeltDataBean> weltDataBeanList;
private ArrayList<LatLng> markers;
private String distance;//贴边数据
private String progress;//任务进度
public WeltDataEvent(int type) {
this.type = type;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public ArrayList<WeltDataBean> getWeltDataBeanList() {
return weltDataBeanList;
}
public void setWeltDataBeanList(ArrayList<WeltDataBean> weltDataBeanList) {
this.weltDataBeanList = weltDataBeanList;
}
public ArrayList<LatLng> getMarkers() {
return markers;
}
public void setMarkers(ArrayList<LatLng> markers) {
this.markers = markers;
}
public String getDistance() {
return distance;
}
public void setDistance(String distance) {
this.distance = distance;
}
public String getProgress() {
return progress;
}
public void setProgress(String progress) {
this.progress = progress;
}
}

View File

@@ -0,0 +1,573 @@
package com.mogo.och.sweeper.operate.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.amap.api.maps.model.LatLng;
import com.amap.api.navi.model.NaviLatLng;
import com.mogo.commons.mvp.IView;
import com.mogo.commons.mvp.MvpFragment;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.call.autopilot.CallerAutopilotRecordListenerManager;
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager;
import com.mogo.eagle.core.function.call.hmi.CallerHmiManager;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxBubbleView;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxButtonView;
import com.mogo.eagle.core.function.hmi.ui.msgbox.DriverMsgBoxListView;
import com.mogo.eagle.core.function.view.MapBizView;
import com.mogo.eagle.core.utilcode.mogo.view.OnPreventFastClickListener;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.map.listener.IMogoMapListener;
import com.mogo.map.listener.MogoMapListenerHandler;
import com.mogo.map.uicontroller.IMogoMapUIController;
import com.mogo.map.uicontroller.VisualAngleMode;
import com.mogo.och.common.module.map.AmapNaviToDestinationModel;
import com.mogo.och.sweeper.R;
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean;
import com.mogo.och.sweeper.common.callback.ISweeperTaskDataToFragmentCallback;
import com.mogo.och.sweeper.operate.callback.IWeltMapSwitchToSmallCallback;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import com.mogo.och.sweeper.operate.view.SweeperTrafficDataView;
import com.mogo.och.sweeper.common.view.WeltSmallMapView;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.fragment.app.FragmentTransaction;
/**
* 网约车基础Fragment主要负责布局通用界面处理站点面板和通话面板互斥情况
* <p>
* 部分业务放在了此处处理
*
* @author tongchenfei
*/
public abstract class BaseSweeperTabFragment<V extends IView, P extends Presenter<V>> extends MvpFragment<V, P> implements IMogoMapListener,
View.OnClickListener, IWeltMapSwitchToSmallCallback, View.OnTouchListener {
private static final String TAG = "BaseSweeperTabFragment";
//地图放大缩小
private ImageView mSwitchMapModeImage;
//设置信息面板
protected ImageView mSettingBtn;
//安全员问题上报面板
protected ImageView mCardBtn;
//道路状况上报面板
protected ImageView mAICollectBtn;
//任务列表面板
private FrameLayout flTaskListPanelContainer;
private MapBizView mapBizView;
private Group groupTestPanel;
//清扫车车辆基本信息 速度 档位 转向灯 红绿灯等
private SweeperTrafficDataView mTrafficDataView;
//人工驾驶地图View组控制
private Group mGroupNaviPanel;
//人工驾驶地图Fragment
private SweeperAmapNaviFragment mOchAmapNaviFragment;
//关闭人工驾驶小地图
private ImageView mCloseNavIcon;
//刷新人工驾驶地图按钮
private ImageView mRefreshNavi;
//作业任务贴边数据展示图
private WeltSmallMapView mMapWeltView;
//任务作业全览图
private WeltMapOverViewFragment mWeltMapOverViewFragment;
private FrameLayout mFlWeltMapOverView;
private DriverMsgBoxButtonView mViewDriverMsgBoxButton;
private DriverMsgBoxListView mViewDriverMsgBoxList;
private DriverMsgBoxBubbleView mViewDriverMsgBoxBubble;
private ArrayList<WeltDataBean> mWeltDataBeanList;//存储贴边数据
private ArrayList<LatLng> mSubTaskCoordinates;//存储当前大任务的所有子任务起点和终点
// 当前子任务的终点坐标
protected LatLng mCurrentTaskEndStation;
private ArrayList<SweeperRoutePlanningUpdateReqBean.Result> mRouteList;//存储任务的坐标轨迹
private String mProgress;
private ISweeperTaskDataToFragmentCallback mTaskDataToFragmentCallback;
@Override
protected int getLayoutId() {
return R.layout.sweeper_base_fragment;
}
private View panelView;
@Override
public boolean onTouch(View v, MotionEvent event) {
return true;
}
@Override
protected void initViews() {
mapBizView = findViewById(R.id.mapBizView);
groupTestPanel = findViewById(R.id.groupTestPanel);
mGroupNaviPanel = findViewById(R.id.group_navi_panel);
flTaskListPanelContainer = findViewById(R.id.module_mogo_och_task_list_container);
mTrafficDataView = findViewById(R.id.sweeper_arc);
panelView = LayoutInflater.from(getContext()).inflate(getTaskListPanelViewId(), flTaskListPanelContainer);
mSwitchMapModeImage = findViewById(R.id.sweeper_switch_model_icon);
mSettingBtn = findViewById(R.id.sweeper_setting_model_icon);
mCardBtn = findViewById(R.id.sweeper_card_model_icon);
mAICollectBtn = findViewById(R.id.sweeper_collect_model_icon);
mCloseNavIcon = findViewById(R.id.sweeper_close_navi_icon);
mRefreshNavi = findViewById(R.id.sweeper_refresh_navi);
mMapWeltView = findViewById(R.id.sweeper_task_welt_small_map);
mFlWeltMapOverView = findViewById(R.id.sweeper_welt_map_overview);
initListener();
//设置消息盒子
setMessageBox();
//设置左下角四个按钮监听事件
setBottomBtnListener();
// 模拟 不可自动驾驶目前场景是刚开机adas还未和工控机连接
findViewById(R.id.btnAutopilotDisable).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE)
);
// 模拟 可自动驾驶,工控机连接正常,且处于人工干预状态
findViewById(R.id.btnAutopilotEnable).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE)
);
// 模拟 自动驾驶能力,自动驾驶中,可能是停车,可能是行进,但是是机器在处理车的前进后退,不是人
findViewById(R.id.btnAutopilotRunning).setOnClickListener(view ->
debugAutoPilotStatus(IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING)
);
// 模拟 结束子任务
findViewById(R.id.btnEndTask).setOnClickListener(view ->
debugEndSubTask()
);
mTrafficDataView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
debugTestBar();
return false;
}
});
}
private void updateSwitchMapIcon() {
if(mapBizView != null){
IMogoMapUIController uiController = mapBizView.getUI();
if(uiController != null){
if (uiController.getCurrentMapVisualAngle().isLongSight()) {
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_big_selector);
} else if (uiController.getCurrentMapVisualAngle().isMediumSight()) {
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_small_selector);
} else {
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_small_selector);
}
}
}
}
/**
* 设置任务进度
*/
protected void setTaskProgress(String progress) {
ThreadUtils.runOnUiThread(() -> {
mMapWeltView.setTaskProgress(progress);
mProgress = progress;
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setProgress(progress);
}
});
}
@Override
protected void initViews(Bundle savedInstanceState) {
super.initViews(savedInstanceState);
mapBizView.onCreate(savedInstanceState);
mMapWeltView.onCreateView(savedInstanceState);
}
/**
* 消息盒子
*/
private void setMessageBox() {
mViewDriverMsgBoxButton = findViewById(R.id.viewDriverMsgBoxButton);
mViewDriverMsgBoxList = findViewById(R.id.viewDriverMsgBoxList);
mViewDriverMsgBoxBubble = findViewById(R.id.viewDriverMsgBoxBubble);
//消息盒子
mViewDriverMsgBoxButton.setClickListener(show -> {
if (show) {
mViewDriverMsgBoxList.setVisibility(View.VISIBLE);
mViewDriverMsgBoxList.notifyData();
mViewDriverMsgBoxBubble.setVisibility(View.GONE);
mViewDriverMsgBoxBubble.isShowData(false);
} else {
mViewDriverMsgBoxList.setVisibility(View.GONE);
mViewDriverMsgBoxBubble.setVisibility(View.VISIBLE);
mViewDriverMsgBoxBubble.isShowData(true);
}
});
}
@Override
public void onPause() {
super.onPause();
mMapWeltView.onPause();
}
@Override
public void onResume() {
super.onResume();
mapBizView.onResume();
mMapWeltView.onResume();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mapBizView.onSaveInstanceState(outState);
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapBizView.onLowMemory();
}
@Override
public void onDestroyView() {
mapBizView.onDestroy();
super.onDestroyView();
CallerAutopilotRecordListenerManager.INSTANCE.removeListener(TAG);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setOnTouchListener(this);
}
private void initListener() {
MogoMapListenerHandler.Companion.getMogoMapListenerHandler().registerHostMapListener(TAG, this);
mCloseNavIcon.setOnClickListener(this);
mRefreshNavi.setOnClickListener(this);
mMapWeltView.getSwitchToBig().setOnClickListener((v) -> showOrHideOverMapViewFragment(true));
}
/**
* 清扫车任务列表面板view在{@link #initViews()}时候添加到container中
*
* @return 站点面板view
*/
public abstract int getTaskListPanelViewId();
/**
* 模拟自动驾驶返回状态
*/
public abstract void debugAutoPilotStatus(int status);
/**
* 迈速表实时更新
*/
public void updateSpeedView(float newSpeed) {
int speed = (int) (Math.abs(newSpeed) * 3.6F); // 倒车时工控机反馈定位信息中speed为负值
if (mTrafficDataView != null) {
mTrafficDataView.updateSpeedWithValue(speed);
}
}
/**
* 车辆基本信息View
*/
public SweeperTrafficDataView getTrafficDataView() {
return mTrafficDataView;
}
@Override
public void onDestroy() {
super.onDestroy();
mMapWeltView.onDestroy();
MogoMapListenerHandler.Companion.getMogoMapListenerHandler().unregisterHostMapListener(TAG);
}
@Override
public void onMapVisualAngleChanged(VisualAngleMode visualAngleMode) {
if (visualAngleMode.isMediumSight()) {
mSwitchMapModeImage.setVisibility(View.VISIBLE);
} else if (visualAngleMode.isLongSight()) {
mSwitchMapModeImage.setVisibility(View.VISIBLE);
} else if (visualAngleMode.isCloseSight()) {
mSwitchMapModeImage.setVisibility(View.GONE);
}
}
/**
* sweeper调试面板打开关闭
*/
public void debugTestBar() {
if (groupTestPanel.getVisibility() == View.VISIBLE) {
groupTestPanel.setVisibility(View.GONE);
} else {
groupTestPanel.setVisibility(View.VISIBLE);
}
}
/**
* 底部四个按钮监听
* 1.地图放大缩小
* 2.设置面板
* 3.安全员问题上报面板
* 4.道路状况上报面板
*/
private void setBottomBtnListener() {
updateSwitchMapIcon();
mSwitchMapModeImage.setOnClickListener(new OnPreventFastClickListener() {
@Override
public void onClickImpl(View v) {
if(mapBizView != null){
IMogoMapUIController uiController = mapBizView.getUI();
if(uiController != null){
if (uiController.getCurrentMapVisualAngle().isLongSight()) {
uiController.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_small_selector);
} else if (uiController.getCurrentMapVisualAngle().isMediumSight()) {
uiController.changeMapVisualAngle(VisualAngleMode.MODE_LONG_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_big_selector);
} else {
uiController.changeMapVisualAngle(VisualAngleMode.MODE_MEDIUM_SIGHT, null);
mSwitchMapModeImage.setImageResource(R.drawable.sweeper_switch_map_big_selector);
}
}
}
}
});
mSettingBtn.setOnClickListener(v -> CallerHmiManager.INSTANCE.showToolsView());
if (mCardBtn != null) {
CallerDevaToolsManager.INSTANCE.initBadCase(mCardBtn);
}
if (mAICollectBtn != null) {
CallerDevaToolsManager.INSTANCE.initAiCollect(mAICollectBtn);
}
}
/**
* 展示人工驾驶地图导航
*/
protected void showAmapNaviToStationFragment(boolean isShow) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (isShow) {
mGroupNaviPanel.setVisibility(View.VISIBLE);
if (mOchAmapNaviFragment == null) {
mOchAmapNaviFragment=SweeperAmapNaviFragment.newInstance();
}
if (mOchAmapNaviFragment.isHidden()) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(mOchAmapNaviFragment).commitAllowingStateLoss();
return;
}
if (mOchAmapNaviFragment.isAdded()) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(mOchAmapNaviFragment).commitAllowingStateLoss();
return;
}
transaction
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(R.id.module_mogo_och_navi_panel_container, mOchAmapNaviFragment)
.show(mOchAmapNaviFragment).commitAllowingStateLoss();
} else {
AmapNaviToDestinationModel.getInstance(getContext()).destroyAmaNavi();
if (mOchAmapNaviFragment != null) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
.hide(mOchAmapNaviFragment).commitAllowingStateLoss();
}
mGroupNaviPanel.setVisibility(View.GONE);
}
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.sweeper_close_navi_icon) {
showAmapNaviToStationFragment(false);
} else if (id == R.id.sweeper_refresh_navi) {
refreshNavi();
}
}
public abstract void refreshNavi();
/**
* 设置作业任务全览图隐藏或者显示
*/
public void showOrHideOverMapViewFragment(boolean isShow) {
FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
if (isShow) {
mFlWeltMapOverView.setVisibility(View.VISIBLE);
if (mWeltMapOverViewFragment == null) {
mWeltMapOverViewFragment = mWeltMapOverViewFragment.newInstance(
(IWeltMapSwitchToSmallCallback) this,
mCurrentTaskEndStation,
mWeltDataBeanList,
mSubTaskCoordinates,
mRouteList,
mProgress,
(SweeperFragment) this);
}
if (mWeltMapOverViewFragment.isHidden()) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(mWeltMapOverViewFragment).commitAllowingStateLoss();
return;
}
if (mWeltMapOverViewFragment.isAdded()) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.show(mWeltMapOverViewFragment).commitAllowingStateLoss();
return;
}
transaction
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.add(R.id.sweeper_welt_map_overview, mWeltMapOverViewFragment)
.show(mWeltMapOverViewFragment).commitAllowingStateLoss();
} else {
mFlWeltMapOverView.setVisibility(View.GONE);
if (mWeltMapOverViewFragment != null) {
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
.hide(mWeltMapOverViewFragment).commitAllowingStateLoss();
}
}
}
@Override
public void onWeltMapSwitchToSmall() {
showOrHideOverMapViewFragment(false);
}
/**
* 设置贴边数据到地图
*/
public void setWeltDataToMap(ArrayList<WeltDataBean> weltDataBeans, Boolean isWeltData, String distance) {
mWeltDataBeanList = weltDataBeans;
ThreadUtils.getSinglePool().execute(() -> {
if (mMapWeltView != null) {
mMapWeltView.setWeltData(weltDataBeans, isWeltData, distance);
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setWeltData(weltDataBeans, distance);
}
});
}
public void setTaskListCoordinatesLatLng(ArrayList<LatLng> subTaskCoordinates) {
this.mSubTaskCoordinates = subTaskCoordinates;
if (mMapWeltView != null) {
mMapWeltView.setTaskListCoordinatesLatLng(subTaskCoordinates);
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setTaskListCoordinatesLatLng(subTaskCoordinates);
}
}
public void setCurrentTaskEndMarker(LatLng subTaskCoordinate) {
if (mMapWeltView != null) {
mMapWeltView.setCurrentTaskCoordinatesLatLng(subTaskCoordinate);
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setCurrentTaskCoordinatesLatLng(subTaskCoordinate);
}
}
/**
* 清除marker标记和任务路线数据
*/
public void clearAllMarkerAndPolyline() {
if (mMapWeltView != null) {
mMapWeltView.clearAllMarkerAndPolyline();
}
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.clearAllMarkerAndPolyline();
}
}
/**
* 导航去目的地
*/
public void startNaviToStation(Boolean isVoicePlay, double startLat, double startLng, double endLat, double endLng) {
AmapNaviToDestinationModel.getInstance(getContext()).destroyAmaNavi();
NaviLatLng startNaviLatLng = new NaviLatLng(startLat, startLng);
NaviLatLng endNaviLatLng = new NaviLatLng(endLat, endLng);
AmapNaviToDestinationModel.getInstance(getContext()).initAMapNavi(startNaviLatLng, endNaviLatLng);
AmapNaviToDestinationModel.getInstance(getContext()).setVoiceIsMute(isVoicePlay);
}
/**
* 设置任务轨迹点数据
*/
public void setTaskRouteList(ArrayList<SweeperRoutePlanningUpdateReqBean.Result> routeList) {
mRouteList = routeList;
ThreadUtils.getSinglePool().execute(() -> {
mMapWeltView.setRouteList(routeList);
if (mTaskDataToFragmentCallback != null) {
mTaskDataToFragmentCallback.setRouteList(routeList);
}
});
}
public static String format(double value) {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(2, RoundingMode.HALF_UP);
return bd.toString();
}
public void setTaskDataToFragmentCallback(ISweeperTaskDataToFragmentCallback mTaskDataToFragmentCallback) {
this.mTaskDataToFragmentCallback = mTaskDataToFragmentCallback;
}
/**
* 结束子任务
*/
public void debugEndSubTask() {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> resultArrayList = tempData();
setTaskRouteList(resultArrayList);
UiThreadHandler.postDelayed(() -> {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> routeList =tempData1();
ArrayList<WeltDataBean> weltDataBeans = new ArrayList<>();
ArrayList<LatLng> subTaskCoordinates=new ArrayList<>();
for (int i = 0; i < routeList.size(); i++) {
WeltDataBean weltDataBean = new WeltDataBean();
weltDataBean.setLocLon(routeList.get(i).longitude);
weltDataBean.setLocLat(routeList.get(i).latitude);
weltDataBean.setWeltDistance(Math.random() * 50);
weltDataBeans.add(weltDataBean);
subTaskCoordinates.add(new LatLng(routeList.get(i).latitude,routeList.get(i).longitude));
}
setTaskListCoordinatesLatLng(subTaskCoordinates);
setWeltDataToMap(weltDataBeans, true, format(Math.random() * 50));
}, 2000);
}
private ArrayList<SweeperRoutePlanningUpdateReqBean.Result> tempData() {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> results = new ArrayList<>();
results.add(getRoute(116.41732262522,39.974576894194));
results.add(getRoute(116.43963532201855,39.9752190287537));
results.add(getRoute(116.4440910789222,39.96469276598098));
results.add(getRoute(116.45254030604285,39.958762580403544));
results.add(getRoute(116.46168616811329,39.94668171374373));
return results;
}
private ArrayList<SweeperRoutePlanningUpdateReqBean.Result> tempData1() {
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> results = new ArrayList<>();
results.add(getRoute(116.41732262522,39.974576894194));
results.add(getRoute(116.43963532201855,39.9752190287537));
results.add(getRoute(116.4440910789222,39.96469276598098));
return results;
}
private SweeperRoutePlanningUpdateReqBean.Result getRoute(double longitude,double latitude){
SweeperRoutePlanningUpdateReqBean.Result result = new SweeperRoutePlanningUpdateReqBean.Result();
result.latitude = latitude;
result.longitude =longitude;
return result;
}
}

View File

@@ -0,0 +1,50 @@
package com.mogo.och.sweeper.operate.fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
/**
* @author: wangmingjun
* @date: 2021/9/9
*/
public abstract class BaseSweeperUIFragment extends Fragment {
private View mRootView;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (mRootView == null) {
mRootView = inflater.inflate(getLayoutId(), container, false);
} else {
ViewGroup viewGroup = (ViewGroup) mRootView.getParent();
if (viewGroup != null) {
viewGroup.removeView(mRootView);
}
}
initViews(mRootView);
initViews(savedInstanceState);
return mRootView;
}
/**
* 布局资源
*
* @return
*/
protected abstract int getLayoutId();
protected abstract void initViews(View view);
protected void initViews(Bundle savedInstanceState) {
}
@Override
public void onDestroy() {
super.onDestroy();
mRootView = null;
}
}

View File

@@ -0,0 +1,230 @@
package com.mogo.och.sweeper.operate.fragment;
import android.os.Bundle;
import android.view.View;
import com.amap.api.navi.AMapNaviViewListener;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.och.common.module.map.AmapNaviToDestinationModel;
import com.mogo.och.common.module.map.CommonAmapNaviVIew;
import com.mogo.och.common.module.map.ICommonNaviChangedCallback;
import com.mogo.och.sweeper.R;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_TAXI;
/**
* @author: wangmingjun
* @date: 2021/11/30
*/
public class SweeperAmapNaviFragment extends BaseSweeperUIFragment implements AMapNaviViewListener {
private CommonAmapNaviVIew mAMapNaviView;
private ICommonNaviChangedCallback mNaviToStartInfoCallback;
public static SweeperAmapNaviFragment newInstance() {
Bundle args = new Bundle();
SweeperAmapNaviFragment fragment = new SweeperAmapNaviFragment();
fragment.setArguments(args);
return fragment;
}
@Override
protected int getLayoutId() {
return R.layout.sweeper_amap_navi_view;
}
@Override
protected void initViews(View view) {
mAMapNaviView = view.findViewById(R.id.navi_view);
}
@Override
protected void initViews(Bundle savedInstanceState) {
super.initViews(savedInstanceState);
if (mAMapNaviView != null)
mAMapNaviView.onCreate(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
if (mAMapNaviView != null)
mAMapNaviView.onResume();
}
@Override
public void onPause() {
super.onPause();
if (mAMapNaviView != null)
mAMapNaviView.onPause();
}
@Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
if (hidden) { //不在最前端界面显示
if (mAMapNaviView != null){
mAMapNaviView.onPause();
}
} else { //重新显示到最前端
if (mAMapNaviView != null){
mAMapNaviView.onResume();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
AmapNaviToDestinationModel.getInstance(getContext()).setVoiceIsMute(false);
if (mAMapNaviView != null){
mAMapNaviView.onDestroy();
}
if (mNaviToStartInfoCallback != null){
mNaviToStartInfoCallback = null;
}
}
@Override
public void onNaviSetting() {
//底部导航设置点击回调
}
@Override
public void onNaviCancel() {
}
@Override
public void onNaviMapMode(int naviMode) {
//导航态车头模式0:车头朝上状态1:正北朝上模式。
}
@Override
public void onNaviTurnClick() {
//转弯view的点击回调
}
@Override
public void onNextRoadClick() {
//下一个道路View点击回调
}
@Override
public void onScanViewButtonClick() {
//全览按钮点击回调
}
@Override
public void onLockMap(boolean isLock) {
//锁地图状态发生变化时回调
}
@Override
public void onNaviViewLoaded() {
CallerLogger.d(M_TAXI + "wlx", "导航页面加载成功");
CallerLogger.d(M_TAXI + "wlx", "请不要使用AMapNaviView.getMap().setOnMapLoadedListener();会overwrite导航SDK内部画线逻辑");
}
@Override
public void onMapTypeChanged(int i) {
}
@Override
public void onNaviViewShowMode(int i) {
}
@Override
public boolean onNaviBackClick() {
return false;
}
/**
* 车道信息说明:
* <p>
* 0xFF, 无对应车道
* 0, 直行
* 1, 左转
* 2, 直行+左转
* 3, 右转
* 4, 直行+右转
* 5, 左掉头
* 6, 左转+右转
* 7, 直行+左转+右转
* 8, 右掉头
* 9, 直行+左掉头
* 10, 直行+右掉头
* 11, 左转+左掉头
* 12, 右转+右掉头
* 13, 直行+扩展
* 14, 左转+左掉头+扩展
* 15, 保留
* 16, 直行+左转+左掉头
* 17, 右转+左掉头
* 18, 左转+右转+左掉头
* 19, 直行+右转+左掉头
* 20, 左转+右掉头
* 21, 公交车道
* 22, 空车道
* 23 可变车道
*/
String[] array = {
"直行车道"
, "左转车道"
, "左转或直行车道"
, "右转车道"
, "右转或直行车道"
, "左掉头车道"
, "左转或者右转车道"
, " 左转或右转或直行车道"
, "右转掉头车道"
, "直行或左转掉头车道"
, "直行或右转掉头车道"
, "左转或左掉头车道"
, "右转或右掉头车道"
, "直行并且车道扩展"
, "左转+左掉头+扩展"
, "不可以选择该车道"
, "直行+左转+左掉头车道"
, "右转+左掉头"
, "左转+右转+左掉头"
, "直行+右转+左掉头"
, "左转+右掉头"
, "公交车道"
, "空车道"
, "可变车道"
};
String[] actions = {
"直行"
, "左转"
, "左转或直行"
, "右转"
, "右转或这行"
, "左掉头"
, "左转或者右转"
, " 左转或右转或直行"
, "右转掉头"
, "直行或左转掉头"
, "直行或右转掉头"
, "左转或左掉头"
, "右转或右掉头"
, "直行并且车道扩展"
, "左转+左掉头+扩展"
, "不可以选择"
, "直行+左转+左掉头"
, "右转+左掉头"
, "左转+右转+左掉头"
, "直行+右转+左掉头"
, "左转+右掉头"
, "公交车道"
, "空车道"
, "可变车道"
};
}

View File

@@ -0,0 +1,531 @@
package com.mogo.och.sweeper.operate.fragment
import android.graphics.Color
import android.os.Bundle
import android.view.View
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import chassis.ChassisStatesOuterClass
import com.amap.api.maps.model.LatLng
import com.mogo.commons.AbsMogoApplication
import com.mogo.eagle.core.data.map.MogoLocation
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotStatusListenerManager
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.ClickUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.operate.bean.*
import com.mogo.och.sweeper.operate.constant.SubTaskTypeEnum
import com.mogo.och.sweeper.operate.constant.TaskStatusEnum
import com.mogo.och.sweeper.common.database.MyDataBase
import com.mogo.och.sweeper.operate.bean.SubInfo
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean
import com.mogo.och.sweeper.operate.presenter.SweeperPresenter
import com.mogo.och.sweeper.operate.ui.adapter.TaskListAdapter
import com.mogo.och.sweeper.operate.ui.dialog.SweeperManualDrivingDialog
import com.mogo.och.sweeper.operate.ui.dialog.SweeperNoTitleCommonDialog
import com.mogo.och.sweeper.operate.ui.popwindow.MenuPopWindow
import kotlinx.android.synthetic.main.fragment_och_sweeper.*
import kotlinx.android.synthetic.main.sweeper_current_task_info.view.*
import kotlinx.android.synthetic.main.sweeper_no_data_common_view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
/**
* 清扫车主界面
*/
class SweeperFragment : BaseSweeperTabFragment<SweeperFragment?, SweeperPresenter?>(), MenuPopWindow.OnMenuItemOnClickListener {
companion object {
const val TAG = "SweeperFragment"
}
private var mAdapter: TaskListAdapter? = null
private var mCurrentSubPosition: Int = 0 //子任务下标
private var mSubMutableList: MutableList<SubInfo>? = null
private var mSubTaskBean: SweeperSubTaskBean? = null
private var mSubTaskType: SubTaskTypeEnum = SubTaskTypeEnum.AUTOPILOT_SUBTYPE //1:自动驾驶子任务 2人工驾驶子任务
private var mCleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates? = null
private var mMainTask: SweeperMainTaskBean? = null
private var mLocation: MogoLocation? = null
private var mSubInfo: SubInfo? = null
override fun getTagName(): String {
return "SweepersFragment"
}
override fun initViews() {
super.initViews()
mAdapter = context?.let { TaskListAdapter() }
val linearLayoutManager = LinearLayoutManager(context)
linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
rvTaskList?.layoutManager = linearLayoutManager
rvTaskList?.adapter = mAdapter
mAdapter?.setOnTaskItemClickListener(onTaskItemClickListener)
initListener()
sweeper_cl_work_mode.setTrafficDataView(trafficDataView)
sweeper_current_task_view.readyTaskBtn.setOnClickListener {
if (ClickUtils.isFastClick()) {//防止重复点击
if (mSubTaskType.code == SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE.code) {
manualDriving(false)
} else {
//开始执行任务
mSubInfo?.apply {
presenter?.startTask(isFirstSubTask(), isLastSubTask(), taskId, mSubTaskType, taskStatus)
}
}
}
}
}
override fun createPresenter(): SweeperPresenter {
return SweeperPresenter(this)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mPresenter?.getCurrentTask()
}
override fun getTaskListPanelViewId(): Int {
return R.layout.fragment_och_sweeper
}
/**
* 任务列表面板隐藏或显示
*
* @return
*/
private fun setShowTaskListPanelView(isShow: Boolean) {
tvTaskConfirm.setTextColor(Color.parseColor("#66FFFFFF"))
tvTaskConfirm.isSelected = false
if (isShow) {
sweeper_current_task_list_view.visibility = View.VISIBLE
} else {
sweeper_current_task_list_view.visibility = View.GONE
}
}
/**
* 当前任务面板隐藏或显示
*
* @return
*/
private fun setShowCurrentTaskPanelView(isShow: Boolean) {
if (isShow) {
sweeper_current_task_view.visibility = View.VISIBLE
} else {
sweeper_current_task_view.visibility = View.GONE
}
}
/**
* VR模式切换
*
* @param isVRMode
*/
fun onVRModeChanged(isVRMode: Boolean) {
if (mRootView != null) {
mRootView.visibility = if (isVRMode) View.VISIBLE else View.GONE
}
}
fun hideOchSweeper() {}
override fun debugAutoPilotStatus(status: Int) {
mPresenter?.debugAutoPilotStatus(status)
}
/**
* 根据自动驾驶状态更新按钮
*/
fun startAutoBtn(autopilotState: Int) {
sweeper_current_task_view.setStartAutoBtn(autopilotState)
}
/**
* 任务按钮状态
*/
fun setTaskBtn(isWorking: Boolean) {
sweeper_current_task_view.setEnableClickBtn(isWorking)
}
/**
* 设置各种监听事件
*/
private fun initListener() {
tvTaskConfirm.setOnClickListener {
mMainTask?.let { task ->
mCurrentSubPosition = 0
mSubTaskBean = SweeperSubTaskBean(task.mainTaskId, task.mainTaskName, task.mainTaskStartTime, 1, mSubMutableList)
//获取当前子任务列表
presenter?.getSubTaskList(task.mainTaskId, mSubTaskBean)
mAdapter?.selectPosition(-1)
}
}
//列表任务刷新
ivTaskListRefresh.setOnClickListener {
mPresenter?.getMainTaskList(true)
}
//无任务时刷新
ivNoTaskRefresh.setOnClickListener {
mPresenter?.getMainTaskList(true)
}
}
private val onTaskItemClickListener = object : TaskListAdapter.TaskItemClickListener {
override fun onItemClick(position: Int, mainTask: SweeperMainTaskBean) {
mAdapter?.selectPosition(position)
tvTaskConfirm.isSelected = true
tvTaskConfirm.setTextColor(Color.parseColor("#FFFFFFFF"))
this@SweeperFragment.mMainTask = mainTask
}
}
override fun onMenuItemClick(itemType: TaskStatusEnum) {
when (itemType.code) {
TaskStatusEnum.JUMP_OVER_SUBTASK.code -> {//跳过任务
mSubInfo?.let {
//如果当前只有一个子任务则isFirst设置为false
presenter?.subTaskSkip(if (mSubMutableList?.size == 1) false else isFirstSubTask(), isLastSubTask(), it.taskId)
}
}
TaskStatusEnum.END_TASK.code -> {//结束主任务
endMainTask()
}
}
}
fun onSweeperFutianCleanSystemState(cleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates) {
lifecycleScope.launch(Dispatchers.Main) {
mCleanSystemState = cleanSystemState
sweeper_cl_work_mode.setSweeperFutianCleanSystemState(mSubTaskType, cleanSystemState)
}
}
/**
* 设置当前主任务列表数据
*/
fun setMainTaskList(mainTaskBeanList: MutableList<SweeperMainTaskBean>?, refresh: Boolean) {
setShowCurrentTaskPanelView(false)
setShowTaskListPanelView(true)
sweeper_cl_work_mode.visibility = View.GONE
if (mainTaskBeanList != null && mainTaskBeanList.isNotEmpty()) {
noTaskDataView.visibility = View.GONE
sweeperListCl.visibility = View.VISIBLE
mAdapter?.setTaskListData(mainTaskBeanList)
} else {
noTaskDataView.visibility = View.VISIBLE
sweeperListCl.visibility = View.GONE
}
if (refresh) {
ToastUtils.showLong("已刷新")
}
}
/**
* 设置当前子任务列表数据
*/
fun setSubTaskBean(subTaskBean: SweeperSubTaskBean, isWorkingSubTask: Boolean) {
this.mSubTaskBean = subTaskBean
this.mSubTaskBean?.let {
setShowCurrentTaskPanelView(true)
setShowTaskListPanelView(false)
sweeper_cl_work_mode.visibility = View.VISIBLE
mCurrentSubPosition = if (isWorkingSubTask) {//如果是正在执行的子任务
it.sort - 1
} else {
0
}
d(SceneConstant.M_SWEEPER + TAG, "setSubTaskBean mCurrentSubPosition$mCurrentSubPosition")
sweeper_current_task_view.setData(sweeper_cl_work_mode, it, mCurrentSubPosition, this, presenter)
mSubMutableList = it.subList
setSubTaskData()
if (mSubTaskType.code == SubTaskTypeEnum.AUTOPILOT_SUBTYPE.code) {//自动驾驶子任务需要调用详情
mSubInfo?.let { subInfo ->
presenter?.getSubTaskDetail(isFirstSubTask(), isLastSubTask(), subInfo.taskId, mSubTaskType, false)//加载子任务详情包括轨迹信息
}
}
mMainTask = SweeperMainTaskBean(it.taskId, it.taskName, 0, it.taskStartTime)
clearAllMarkerAndPolyline()
addWeltData()
}
}
/**
* 更新子任务状态
*/
fun updateSubTaskStatus(typeEnum: TaskStatusEnum, isLastSubTask: Boolean) {
if (isLastSubTask) {
showAmapNaviToStationFragment(false)
clearAllMarkerAndPolyline()
//整个大任务结束,则删除它相关的贴边数据缓存
lifecycleScope.launch(Dispatchers.IO) {
MyDataBase.getInstance().weltDataDao.deleteAllWeltData()
mPresenter?.clearRouteList()
}
setTaskProgress("0")
val builder = SweeperManualDrivingDialog.Builder()
val endConfirmDialog = builder
.tips("任务已完成")
.confirmStr("确认")
.build(requireContext())
endConfirmDialog?.setClickListener(object: SweeperManualDrivingDialog.ClickListener{
override fun confirm() {
mPresenter?.getMainTaskList(false)
}
})
endConfirmDialog?.show()
return
}
when (typeEnum.code) {
TaskStatusEnum.END_SUBTASK.code -> {//子任务结束
mCurrentSubPosition++
setSubTaskData()
d(SceneConstant.M_SWEEPER + TAG, "END_SUBTASK mCurrentSubPosition$mCurrentSubPosition")
if (mSubTaskType.code == SubTaskTypeEnum.AUTOPILOT_SUBTYPE.code) {//自动驾驶子任务
//关闭人工驾驶导航
showAmapNaviToStationFragment(false)
mSubInfo?.apply {
presenter?.getSubTaskDetail(isFirstSubTask(), isLastSubTask(), taskId, mSubTaskType, true)
}
sweeper_current_task_view.setCurrentData(mCurrentSubPosition)
} else {
manualDriving(true)
}
}
TaskStatusEnum.JUMP_OVER_SUBTASK.code -> { //子任务跳过
//是否是最后一个子任务结束
mCurrentSubPosition++
setSubTaskData()
d(SceneConstant.M_SWEEPER + TAG, "JUMP_OVER_SUBTASK mCurrentSubPosition$mCurrentSubPosition")
showAmapNaviToStationFragment(false)
if (mSubTaskType.code == SubTaskTypeEnum.AUTOPILOT_SUBTYPE.code) {//自动驾驶子任务
//关闭人工驾驶导航
showAmapNaviToStationFragment(false)
mSubInfo?.apply {
presenter?.getSubTaskDetail(isFirstSubTask(), isLastSubTask(), taskId, mSubTaskType, true)
}
sweeper_current_task_view.setCurrentData(mCurrentSubPosition)
} else {
manualDriving(true)
}
}
TaskStatusEnum.START_SUBTASK.code -> { //子任务开始
setTaskBtn(true)
sweeper_current_task_view.setTaskStatus(2)
if (mSubTaskType == SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE) {
sweeper_current_task_view.setCurrentData(mCurrentSubPosition)
}
}
}
setEndStationMarker()
}
/**
* 主任务重置
*/
fun setMainTaskReset(isSuccess: Boolean) {
if (isSuccess) {
//任务重置后,重新拉取任务列表数据
mPresenter?.getMainTaskList(false)
//清除任务全览图的marker和路线
clearAllMarkerAndPolyline()
showAmapNaviToStationFragment(false)
setTaskProgress("0")
//整个大任务结束,则删除它相关的贴边数据缓存
lifecycleScope.launch(Dispatchers.IO) {
MyDataBase.getInstance().weltDataDao.deleteAllWeltData()
mPresenter?.clearRouteList()
}
}
}
/**
* 设置当前选中的子任务信息
*/
private fun setSubTaskData() {
mSubMutableList?.let {
mSubInfo = it[mCurrentSubPosition]
mSubTaskType = if (it[mCurrentSubPosition].taskType == SubTaskTypeEnum.AUTOPILOT_SUBTYPE.code)
SubTaskTypeEnum.AUTOPILOT_SUBTYPE else SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE
//设置当前子任务信息
presenter?.setSubtask(isFirstSubTask(), isLastSubTask(), it[mCurrentSubPosition].taskId, it[mCurrentSubPosition].taskType)
}
/**
* 计算任务的进度
*/
lifecycleScope.launch(Dispatchers.IO) {
mSubMutableList?.let {
var sum: Double = 0.0
for (index in it.indices) {
sum += it[index].mileage
}
var completed: Double = 0.0
for (index in it.indices) {
if (index < mCurrentSubPosition) {
// 已完成的子任务记入完成度,进行中的不计入
completed += it[index].mileage
}
}
val progress = "${((completed / sum) * 100.0).roundToInt()}%"
setTaskProgress(progress)
}
}
}
/**
* 结束主任务
*/
private fun endMainTask() {
//结束任务
val builder = SweeperNoTitleCommonDialog.Builder()
val endConfirmDialog = builder
.tips("是否结束任务?")
.confirmStr("确认")
.cancelStr("取消")
.build(requireContext())
endConfirmDialog?.setClickListener(object : SweeperNoTitleCommonDialog.ClickListener {
override fun confirm() {
mMainTask?.let {
presenter?.mainTaskReset(it.mainTaskId)
}
}
override fun cancel() {
endConfirmDialog?.dismiss()
}
})
endConfirmDialog?.show()
}
/**
* 人工驾驶子任务
*/
private fun manualDriving(isCancelAutoPoilot: Boolean) {
if (isCancelAutoPoilot) { // 人工子任务 先取消自动驾驶
CallerAutoPilotStatusListenerManager.updateAutopilotControlParameters(null)
CallerAutoPilotControlManager.cancelAutoPilot()
}
mSubInfo?.apply {
val builder = SweeperManualDrivingDialog.Builder()
val endConfirmDialog = builder
.tips("前方子任务${taskName}请人工驾驶至(${endSiteName})")
.confirmStr("确认")
.build(requireContext())
endConfirmDialog?.setClickListener(object : SweeperManualDrivingDialog.ClickListener {
override fun confirm() {
presenter?.startTask(isFirstSubTask(), isLastSubTask(), taskId, mSubTaskType, taskStatus)
val startPoint =
CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(AbsMogoApplication.getApp(), startWgs84Lon, startWgs84Lat)
val endPoint = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(AbsMogoApplication.getApp(), endWgs84Lon, endWgs84Lat)
startNaviToStation(
true,
startPoint.latitude,
startPoint.longitude,
endPoint.latitude,
endPoint.longitude
)
showAmapNaviToStationFragment(true)
}
})
endConfirmDialog?.show()
}
}
/**
* 是否第一个子任务
*/
fun isFirstSubTask(): Boolean {
return mCurrentSubPosition == 0
}
/**
* 是否最后一个子任务
*/
fun isLastSubTask(): Boolean {
return mCurrentSubPosition == (mSubMutableList?.size?.minus(1))
}
/**
* 当前位置经纬度
*/
fun setCurrentLocation(location: MogoLocation) {
this.mLocation = location
}
override fun refreshNavi() {
mSubInfo?.apply {
mLocation?.let {
val endPoint = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(AbsMogoApplication.getApp(), endWgs84Lon, endWgs84Lat)
startNaviToStation(true, it.latitude, it.longitude, endPoint.latitude, endPoint.longitude)
}
}
}
private fun addWeltData() {
lifecycleScope.launch(Dispatchers.IO) {
mSubMutableList?.let { subList ->
if (subList.size <= 0) {
return@launch
}
val dataList = ArrayList<LatLng>()
for (index in subList.indices) {
val startPoint = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(
AbsMogoApplication.getApp(),
subList[index].startWgs84Lon,
subList[index].startWgs84Lat
)
val startLatLng = LatLng(startPoint.latitude, startPoint.longitude)
dataList.add(index,startLatLng)
val endPoint = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(
AbsMogoApplication.getApp(),
subList[index].endWgs84Lon,
subList[index].endWgs84Lat
)
val endLatLng = LatLng(endPoint.latitude, endPoint.longitude)
dataList.add(index+1,endLatLng)
}
setTaskListCoordinatesLatLng(dataList)
setEndStationMarker()
}
}
}
private fun setEndStationMarker(){
lifecycleScope.launch(Dispatchers.IO) {
mSubInfo?.let {
val endPoint = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(
AbsMogoApplication.getApp(),
it.endWgs84Lon,
it.endWgs84Lat
)
super.mCurrentTaskEndStation = endPoint
setCurrentTaskEndMarker(endPoint)
}
}
}
//模拟结束子任务
override fun debugEndSubTask() {
mPresenter?.onArriveTaskEnd(null)
//super.debugEndSubTask()
}
/**
* 设置路线轨迹
*/
override fun setTaskRouteList(routeList: ArrayList<SweeperRoutePlanningUpdateReqBean.Result>) {
super.setTaskRouteList(routeList)
}
}

View File

@@ -0,0 +1,129 @@
package com.mogo.och.sweeper.operate.fragment
import android.os.Bundle
import com.amap.api.maps.model.LatLng
import com.mogo.commons.mvp.BaseFragment
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean
import com.mogo.och.sweeper.common.callback.ISweeperTaskDataToFragmentCallback
import com.mogo.och.sweeper.operate.callback.IWeltMapSwitchToSmallCallback
import com.mogo.och.sweeper.common.database.bean.WeltDataBean
import kotlinx.android.synthetic.main.fragment_welt_map_overview.*
import kotlinx.android.synthetic.main.sweeper_welt_map_overview.*
/**
* 作业任务全览图
*/
class WeltMapOverViewFragment() : BaseFragment(), ISweeperTaskDataToFragmentCallback {
private var mIWeltMapSwitchToSmallCallBack: IWeltMapSwitchToSmallCallback? = null
private var mFragment: SweeperFragment? = null
override fun getLayoutId(): Int = R.layout.fragment_welt_map_overview
override fun getTagName(): String {
return "WeltMapOverViewFragment"
}
override fun initViews() {
}
fun setWeltMapSwitchToSmallCallBack(iWeltMapSwitchToSmallCallback: IWeltMapSwitchToSmallCallback){
this.mIWeltMapSwitchToSmallCallBack=iWeltMapSwitchToSmallCallback
}
fun setSweeperFragment(fragment: SweeperFragment){
this.mFragment=fragment
mFragment?.setTaskDataToFragmentCallback(this)
}
override fun initViews(savedInstanceState: Bundle?) {
super.initViews(savedInstanceState)
weltMapOverView?.onCreateView(savedInstanceState)
sweeperSwitchToSmall.setOnClickListener {
mIWeltMapSwitchToSmallCallBack?.onWeltMapSwitchToSmall()
}
val bundle = arguments
if (bundle != null) {
val latLngs = bundle.getSerializable("subTaskCoordinates") as? ArrayList<LatLng>
val latLng = bundle.getParcelable<LatLng>("subTaskEndCoordinates")
val weltDataList = bundle.getSerializable("weltDataList") as? ArrayList<WeltDataBean>
val routeList = bundle.getSerializable("routeList") as? ArrayList<SweeperRoutePlanningUpdateReqBean.Result>
val progress = bundle.getString("progress")
routeList?.let {
weltMapOverView?.setRouteList(it)
}
latLng?.let {
setCurrentTaskCoordinatesLatLng(it)
}
latLngs?.let {
setTaskListCoordinatesLatLng(it)
}
weltDataList?.let {
setWeltData(it,"0.0cm")
}
progress?.let {
weltMapOverView?.setProgress(progress)
}
}
}
companion object {
@JvmStatic
fun newInstance(
mIWeltMapSwitchToSmallCallBack: IWeltMapSwitchToSmallCallback,
mCurrentTaskEndStation:LatLng?,
weltDataList: ArrayList<WeltDataBean>?,
latLngs: ArrayList<LatLng>?,
routeList: ArrayList<SweeperRoutePlanningUpdateReqBean.Result>?,
progress:String?,
sweeperFragment: SweeperFragment
): WeltMapOverViewFragment {
val args = Bundle()
args.putSerializable("weltDataList", weltDataList)
args.putSerializable("subTaskCoordinates", latLngs)
args.putParcelable("subTaskEndCoordinates", mCurrentTaskEndStation)
args.putSerializable("routeList", routeList)
args.putString("progress", progress)
val fragment = WeltMapOverViewFragment()
fragment.setWeltMapSwitchToSmallCallBack(mIWeltMapSwitchToSmallCallBack)
fragment.setSweeperFragment(sweeperFragment)
fragment.arguments = args
return fragment
}
}
override fun onPause() {
super.onPause()
weltMapOverView?.onPause()
}
override fun onResume() {
super.onResume()
weltMapOverView?.onResume()
}
override fun onDestroy() {
super.onDestroy()
weltMapOverView?.onDestroy()
}
override fun setTaskListCoordinatesLatLng(coordinatesLatLng: java.util.ArrayList<LatLng>) {
weltMapOverView?.setTaskListCoordinatesLatLng(coordinatesLatLng)
}
override fun setCurrentTaskCoordinatesLatLng(coordinatesLatLng: LatLng) {
weltMapOverView?.setCurrentTaskCoordinatesLatLng(coordinatesLatLng)
}
override fun clearAllMarkerAndPolyline() {
weltMapOverView?.clearAllMarkerAndPolyline()
}
override fun setProgress(progress: String) {
weltMapOverView?.setProgress(progress)
}
override fun setRouteList(routeList: java.util.ArrayList<SweeperRoutePlanningUpdateReqBean.Result>) {
weltMapOverView?.setRouteList(routeList)
}
override fun setWeltData(weltDatas: java.util.ArrayList<WeltDataBean>?, distance: String) {
weltMapOverView?.setWeltData(weltDatas,true,distance)
}
}

View File

@@ -0,0 +1,897 @@
package com.mogo.och.sweeper.operate.model;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_SWEEPER;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.amap.api.maps.model.LatLng;
import com.elegant.network.utils.GsonUtil;
import com.mogo.aicloud.services.socket.IMogoOnMessageListener;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.module.status.IMogoStatusChangedListener;
import com.mogo.commons.module.status.MogoStatusManager;
import com.mogo.commons.module.status.StatusDescriptor;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLocationGCJ02Listener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoPlanningRottingListener;
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.CallerChassisLocationGCJ02ListenerManager;
import com.mogo.eagle.core.function.call.autopilot.CallerPlanningRottingListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.CoordinateUtils;
import com.mogo.eagle.core.utilcode.util.NetworkUtils;
import com.mogo.eagle.core.utilcode.util.ToastUtils;
import com.mogo.och.common.module.biz.common.socketmessage.OCHSocketMessageManager;
import com.mogo.och.common.module.biz.common.socketmessage.data.OCHOperationalMessage;
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback;
import com.mogo.och.common.module.callback.OchAdasStartFailureCallback;
import com.mogo.och.common.module.manager.autopilotmanager.OCHAdasAbilityManager;
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil;
import com.mogo.och.common.module.utils.NumberFormatUtil;
import com.mogo.och.common.module.utils.PinYinUtil;
import com.mogo.och.common.module.utils.ToastUtilsOch;
import com.mogo.och.common.module.voice.VoiceNotice;
import com.mogo.och.sweeper.R;
import com.mogo.och.sweeper.operate.bean.BaseResponse;
import com.mogo.och.sweeper.operate.bean.SubInfo;
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean;
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskDetailBean;
import com.mogo.och.sweeper.common.callback.ISweeperADASStatusCallback;
import com.mogo.och.sweeper.operate.callback.ISweeperControllerStatusCallback;
import com.mogo.och.sweeper.operate.callback.ISweeperTaskCallback;
import com.mogo.och.sweeper.operate.constant.SubTaskTypeEnum;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import com.mogo.och.sweeper.operate.constant.TaskStatusEnum;
import com.mogo.och.sweeper.operate.net.SweeperServiceManager;
import com.mogo.och.sweeper.operate.util.SweeperAnalyticsManager;
import com.mogo.och.sweeper.operate.util.SweeperTrajectoryManager;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import io.reactivex.exceptions.UndeliverableException;
import io.reactivex.functions.Consumer;
import io.reactivex.plugins.RxJavaPlugins;
import mogo.telematics.pad.MessagePad;
/**
* @author congtaowang
* @since 2021/3/23
* <p>
* 清扫车任务管理
*/
public class SweeperTaskModel {
private final String TAG = SweeperTaskModel.class.getSimpleName();
private static volatile SweeperTaskModel sInstance;
public double mLongitude = 0;
public double mLatitude = 0;
private Context mContext;
// 运营类型
private static final int VEHICLE_TYPE = 10;
private ISweeperControllerStatusCallback mControllerStatusCallback; //Model->PresenterVR mode等
private ISweeperADASStatusCallback mADASStatusCallback;
private ISweeperTaskCallback mSweeperTaskCallback;
ArrayList<SweeperRoutePlanningUpdateReqBean.Result> points = new ArrayList<>();//全路径信息
//当前子任务详情信息包括轨迹文件信息
private SweeperSubTaskDetailBean mCurrentSubTaskDetail;
//子任务是否正在进行中
private boolean mIsSubTaskWorking = false;
//自动驾驶是否到达子任务终点
private boolean isAutopilotSubTaskArriveEndSite = false;
//当前子任务id
private int mSubTaskId = 0;
//当前是否最后一个子任务
private boolean mIsLastSubtask = false;
//当前任务是否是第一个
private boolean mIsFirstSubtask = false;
//当前任务是否是第一个
private int mSubTaskType = 0; //1自动驾驶 2.人工驾驶
//0: 代表没有启动过 1代表是启动第一次当>=1 代表是重试 每次子任务结束/主任务结束清空置为0
private volatile int firstStartAutopilot = 0;
private int mLineId;//路线id
//自动驾驶状态
private int mAutopilotState = IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE;
public static SweeperTaskModel getInstance() {
if (sInstance == null) {
synchronized (SweeperTaskModel.class) {
if (sInstance == null) {
sInstance = new SweeperTaskModel();
}
}
}
return sInstance;
}
private SweeperTaskModel() {
}
public void init() {
mContext = AbsMogoApplication.getApp();
// 定位监听
CallerChassisLocationGCJ02ListenerManager.INSTANCE.addListener(TAG, mMapLocationListener);
MogoStatusManager.getInstance().registerStatusChangedListener(TAG, StatusDescriptor.VR_MODE, mMogoStatusChangedListener);
//自动驾驶路线规划接口
CallerPlanningRottingListenerManager.INSTANCE.addListener(TAG, moGoAutopilotPlanningListener);
//开启自驾后 异常信息返回
OCHAdasAbilityManager.getInstance().setAdasStartFailureCallback(mAdasStartFailureListener);
OCHSocketMessageManager.INSTANCE.registerSocketMessageListener(
OCHSocketMessageManager.msgMonitorType,
mMogoOnMessageListener);
//2022.1.28
// 调用Disposable.dispose() 时候会出现InterruptedException 导致出现崩溃
// The exception could not be delivered to the consumer because it has already canceled/disposed
// the flow or the excTeption has nowhere to go to begin with
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
@Override
public void accept(Throwable e) {
if (e instanceof UndeliverableException) {
e = e.getCause();
CallerLogger.d(M_SWEEPER + TAG, "UndeliverableException");
}
if ((e instanceof IOException)) {//
// fine, irrelevant network problem or API that throws on cancellation
CallerLogger.d(M_SWEEPER + TAG, "IOException");
return;
}
if (e instanceof InterruptedException) {
// fine, some blocking code was interrupted by a dispose call
CallerLogger.d(M_SWEEPER + TAG, "InterruptedException");
return;
}
if ((e instanceof NullPointerException) || (e instanceof IllegalArgumentException)) {
// that's likely a bug in the application
CallerLogger.d(M_SWEEPER + TAG, "NullPointerException or IllegalArgumentException");
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
return;
}
if (e instanceof IllegalStateException) {
// that's a bug in RxJava or in a custom operator
CallerLogger.d(M_SWEEPER + TAG, "IllegalStateException");
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
return;
}
CallerLogger.d(M_SWEEPER + TAG, "Undeliverable exception");
}
});
}
public void setAdasStatusCallback(ISweeperADASStatusCallback callback) {
this.mADASStatusCallback = callback;
}
public void setControllerStatusCallback(ISweeperControllerStatusCallback callback) {
this.mControllerStatusCallback = callback;
}
public void setSweeperTaskCallback(ISweeperTaskCallback callback) {
this.mSweeperTaskCallback = callback;
}
private final IMoGoPlanningRottingListener moGoAutopilotPlanningListener = new IMoGoPlanningRottingListener() {
@Override
public void onAutopilotRotting(MessagePad.GlobalPathResp routeList) {
if (null != routeList && routeList.getWayPointsList()!=null&&routeList.getWayPointsList().size() > 0) {
if (mCurrentSubTaskDetail.getLineId() != mLineId) {//判断是否同一条路线
mLineId = mCurrentSubTaskDetail.getLineId();
points.addAll(coordinateConverterWgsToGcjList(mContext, routeList.getWayPointsList()));
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.setRouteList(points);
}
}
}
}
};
public void setSubtask(boolean isFirstSubtask, boolean isLastSubtask, int subTaskId, int subTaskType) {
this.mIsLastSubtask = isLastSubtask;
this.mIsFirstSubtask = isFirstSubtask;
this.mSubTaskId = subTaskId;
this.mSubTaskType = subTaskType;
}
/**
* 清除路线缓存数据
*/
public void clearRouteList() {
points.clear();
mSubTaskType=0;
}
/**
* 保存自动驾驶状态
*
* @param autopilotState
*/
public void setAutopilotState(int autopilotState) {
this.mAutopilotState = autopilotState;
}
public static List<SweeperRoutePlanningUpdateReqBean.Result> coordinateConverterWgsToGcjList(Context mContext,
List<MessagePad.Location> mogoLatLngList) {
List<SweeperRoutePlanningUpdateReqBean.Result> points = new ArrayList<>();
for (MessagePad.Location m : mogoLatLngList) {
LatLng mogoLatLng = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(mContext, m);
SweeperRoutePlanningUpdateReqBean.Result result = new SweeperRoutePlanningUpdateReqBean.Result();
result.latitude = mogoLatLng.latitude;
result.longitude = mogoLatLng.longitude;
points.add(result);
}
return points;
}
public void release() {
MogoStatusManager.getInstance().unregisterStatusChangedListener(TAG, StatusDescriptor.VR_MODE, mMogoStatusChangedListener);
// 注销定位监听
CallerChassisLocationGCJ02ListenerManager.INSTANCE.removeListener(TAG);
//自动驾驶路线规划接口
CallerPlanningRottingListenerManager.INSTANCE.removeListener(moGoAutopilotPlanningListener);
OCHAdasAbilityManager.getInstance().setAdasStartFailureCallback(null);
}
private Object readResolve() {
// 阻止反序列化,必须实现 Serializable 接口
return sInstance;
}
private final IMogoOnMessageListener<OCHOperationalMessage> mMogoOnMessageListener =
new IMogoOnMessageListener<OCHOperationalMessage>() {
@Override
public Class<OCHOperationalMessage> target() {
return OCHOperationalMessage.class;
}
@Override
public void onMsgReceived(OCHOperationalMessage obj) {
if (obj == null) {
CallerLogger.d(M_SWEEPER + TAG, "onMsgReceived = null");
return;
}
CallerLogger.d(M_SWEEPER + TAG, "onMsgReceived = " + obj.getMessage());
OCHSocketMessageManager.INSTANCE.pushAppOperationalMsgBox(obj.getPushTimeStamp(),
obj.getMessage(),OCHSocketMessageManager.OPERATION_SYSTEM);
}
};
private final IMogoStatusChangedListener mMogoStatusChangedListener = new IMogoStatusChangedListener() {
// VR mode变更回调
@Override
public void onStatusChanged(StatusDescriptor descriptor, boolean isTrue) {
if (StatusDescriptor.VR_MODE == descriptor) {
if (mControllerStatusCallback != null) {
mControllerStatusCallback.onVRModeChanged(isTrue);
}
}
}
};
private final OchAdasStartFailureCallback mAdasStartFailureListener = new OchAdasStartFailureCallback() {
@Override
public void brakeStatusChanged(boolean isBrakeAvailable) {
}
@Override
public void onStartAutopilotFailure(@NotNull String startFailedCode, @NonNull String startFailedMessage) {
SweeperAnalyticsManager.getInstance().triggerStartAutopilotFailureEventByAdas(startFailedCode, startFailedMessage);
if (mADASStatusCallback != null && !FunctionBuildConfig.isDemoMode) {
CallerLogger.e(M_SWEEPER + TAG, "mAdasStartFailureListener = " + startFailedMessage);
mADASStatusCallback.onStartAdasFailure();
}
}
};
// 自车定位
private final IMoGoChassisLocationGCJ02Listener mMapLocationListener = new IMoGoChassisLocationGCJ02Listener() {
@Override
public void onChassisLocationGCJ02(@Nullable MogoLocation gnssInfo) {
if (null == gnssInfo) return;
CallerLogger.e(M_SWEEPER + TAG, "高德定位到站mLongitude = " + gnssInfo.getLongitude()
+ ", mLatitude=" + gnssInfo.getLatitude() + ", mSubTaskType=" + mSubTaskType +", mIsSubTaskWorking=" + mIsSubTaskWorking
+ ", isAutopilotSubTaskArriveEndSite=" + isAutopilotSubTaskArriveEndSite
);
if (mControllerStatusCallback != null) {
mControllerStatusCallback.onCarLocationChanged(gnssInfo);
}
if (SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE.getCode() == mSubTaskType) {//人工驾驶子任务需要手动跳过,不能自动结束
addCoordinates(gnssInfo);
return;
}
//子任务完成的围栏判断 子任务正在执行中,还未到达子任务终点
//20230504 为了避免自车定位提前结束任务导致的溜车问题,这里删除自车自动触发到站
// if (mIsSubTaskWorking && !isAutopilotSubTaskArriveEndSite) {
// judgeArrivedStation(gnssInfo);
// }
}
};
/**
* 根据当前定位收集人工驾驶子任务轨迹点
*
* @param mogoLatLng
*/
private void addCoordinates(MogoLocation mogoLatLng) {
String tempLongitude = NumberFormatUtil.cutOutNumber(mogoLatLng.getLongitude(), 5);
String tempLatitude = NumberFormatUtil.cutOutNumber(mogoLatLng.getLatitude(), 5);
//用于过滤车是否停在原地,经纬度相同的情况
if (!tempLongitude.equals(mLongitude) && !tempLatitude.equals(mLatitude)) {
mLongitude = mogoLatLng.getLongitude();
mLatitude = mogoLatLng.getLatitude();
SweeperRoutePlanningUpdateReqBean.Result result = new SweeperRoutePlanningUpdateReqBean.Result();
result.latitude = mogoLatLng.getLatitude();
result.longitude = mogoLatLng.getLongitude();
points.add(result);
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.setRouteList(points);
}
}
}
//根据围栏判断,是否到达子任务终点
private void judgeArrivedStation(MogoLocation location) {
if (mCurrentSubTaskDetail == null) {
CallerLogger.d(TAG, "行程日志-judgeArrivedStation() mCurrentSubTaskDetail is null");
return;
}
double endSiteLon = mCurrentSubTaskDetail.getEndSiteLon();
double endSiteLat = mCurrentSubTaskDetail.getEndSiteLat();
double distance = CoordinateUtils.calculateLineDistance(
endSiteLon, endSiteLat,
location.getLongitude(), location.getLatitude());
if (distance <= SweeperConst.ARRIVE_AT_END_STATION_DISTANCE) {
CallerLogger.d(M_SWEEPER + TAG, "行程日志-judgeArrivedStation() distance = " + distance
+ " to " + mCurrentSubTaskDetail.getEndSiteName());
isAutopilotSubTaskArriveEndSite = true;
//到达子任务终点 结束子任务
subTaskEnd(mIsFirstSubtask, mIsLastSubtask, mSubTaskId); //无自动驾驶到终点信息传null
return;
}
}
/**
* 在踩刹车、控制方向盘等操作后,会停止自动驾驶,重启自动驾驶的话相当于重新设置自动驾驶目的地
*/
public void startAutopilot() {
CallerLogger.d(M_SWEEPER + TAG, "行程日志-重启自动驾驶===" + mIsSubTaskWorking);
//只去启动自动驾驶
startAutopilot(firstStartAutopilot >= 1);
}
/**
* 开启自动驾驶
*
* @param isRestart
*/
private void startAutopilot(boolean isRestart) {
if (!FunctionBuildConfig.isDemoMode && !OCHAdasAbilityManager.getInstance().getAutopilotAbilityStatus()) {
ToastUtils.showLong(OCHAdasAbilityManager.getInstance().getAutopilotUnAbilityReason() +
", 请稍候重试");
triggerUnableStartAPReasonEvent();
return;
}
firstStartAutopilot++;
triggerStartServiceEvent(isRestart, false);
AutopilotControlParameters parameters = initAutopilotControlParameters();
if (null == parameters) {
CallerLogger.e(M_SWEEPER + TAG, "行程日志-AutopilotControlParameters is empty.");
return;
}
CallerAutoPilotControlManager.INSTANCE.startAutoPilot(parameters);
CallerLogger.d(M_SWEEPER + TAG, "行程日志-开启自动驾驶====" + GsonUtil.jsonFromObject(parameters)
+ " startLatLon=" + parameters.startName + "endLatLon=" + parameters.endName +
"isRestart = " + isRestart);
if (mControllerStatusCallback != null) {
mControllerStatusCallback.startOpenAutopilot();
}
}
/**
* 获取任务子任务执行状态
*/
public boolean getSubWorking() {
return mIsSubTaskWorking;
}
public void setWorking(Boolean isWorking) {
this.mIsSubTaskWorking = isWorking;
}
/**
* 关闭美化模式
*/
public void closeBeautificationMode() {
if (FunctionBuildConfig.isDemoMode) {
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = false; //是否强制绘制引导线
CallerAutoPilotControlManager.INSTANCE.setIPCDemoMode(false);//是否自动启动自驾
CallerLogger.d(M_SWEEPER + TAG, "美化模式-ignore置为false");
}
}
/**
* 开启美化模式
*/
public void startBeautificationMode() {
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = true;//是否强制绘制引导线
CallerAutoPilotControlManager.INSTANCE.setIPCDemoMode(true);//是否自动启动自驾
CallerLogger.d(M_SWEEPER + TAG, "美化模式-ignore置为true");
}
/**
* @param data
*/
public void onArriveTaskEnd(MessagePad.ArrivalNotification data) {
//MAP 280 每隔100ms左右返回一次到站 导致在到达中间站后再次滑动出发后会有时间差,收到一次到站,出现问题
//此处比对 自驾告诉的子任务终点坐标和本地应到子任务终点坐标, 一致时才表示子任务完成
if (data != null && data.getEndLocation() != null && mCurrentSubTaskDetail != null) {
CallerLogger.d(M_SWEEPER + TAG, "MAP到站通知" + mCurrentSubTaskDetail.getEndSiteName() + " 经度:"
+ data.getEndLocation().getLongitude() + " 纬度:" + data.getEndLocation().getLatitude());
String latitude = NumberFormatUtil.cutOutNumber(data.getEndLocation().getLatitude(), 5); //wgs
String longitude = NumberFormatUtil.cutOutNumber(data.getEndLocation().getLongitude(), 5);
String arriveLat = NumberFormatUtil.cutOutNumber(mCurrentSubTaskDetail.getEndSiteWgs64Lat(), 5);
String arriveLon = NumberFormatUtil.cutOutNumber(mCurrentSubTaskDetail.getEndSiteWgs64Lon(), 5);
if (!latitude.equals(arriveLat) || !longitude.equals(arriveLon)) {
CallerLogger.e(M_SWEEPER + TAG, "行程日志-子任务终点拦截,自动子任务终点与本地子任务终点不一致");
return;
}
}
CallerLogger.d(M_SWEEPER + TAG, "MAP到站通知mSubTaskType=" + mSubTaskType + " isAutopilotSubTaskArriveEndSite="
+ isAutopilotSubTaskArriveEndSite + " mSubTaskId=" + mSubTaskId);
if (SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE.getCode() == mSubTaskType) {//人工驾驶子任务需要手动跳过,不能自动结束
return;
}
if (isAutopilotSubTaskArriveEndSite) {
return;
}
isAutopilotSubTaskArriveEndSite = true;
//到达子任务终点 结束子任务
subTaskEnd(mIsFirstSubtask, mIsLastSubtask, mSubTaskId);
}
public void triggerStartServiceEvent(boolean isRestart, boolean send) {
if (mCurrentSubTaskDetail == null) {
return;
}
SweeperAnalyticsManager.getInstance().triggerStartAutopilotEvent(isRestart, send,
mCurrentSubTaskDetail.getStartSiteName(), mCurrentSubTaskDetail.getEndSiteName(), mCurrentSubTaskDetail.getLineId());
}
public boolean isRestartAutopilot() {
return firstStartAutopilot > 1;
}
public void triggerUnableStartAPReasonEvent() {
if (mCurrentSubTaskDetail == null) {
return;
}
SweeperAnalyticsManager.getInstance().triggerUnableStartAPReasonEvent(
mCurrentSubTaskDetail.getStartSiteName(), mCurrentSubTaskDetail.getEndSiteName(), mCurrentSubTaskDetail.getLineId(),
OCHAdasAbilityManager.getInstance().getAutopilotUnAbilityReason());
}
/**
* 将轨迹文件信息同步到鹰眼
*/
private void updateAutopilotControlParameters() {
AutopilotControlParameters parameters = initAutopilotControlParameters();
if (null == parameters) {
CallerLogger.e(M_SWEEPER + TAG, "AutopilotControlParameters is empty.");
return;
}
CallerLogger.d(M_SWEEPER + TAG, "AutopilotControlParameters" + GsonUtil.jsonFromObject(parameters));
CallerLogger.d(M_SWEEPER + TAG, "AutopilotControlParameters is update.");
CallerAutoPilotStatusListenerManager.INSTANCE.updateAutopilotControlParameters(parameters);
}
private void clearAutopilotControlParameters() {
CallerLogger.d(M_SWEEPER + TAG, "AutopilotControlParameters is clear.");
CallerAutoPilotStatusListenerManager.INSTANCE.updateAutopilotControlParameters(null);
}
private AutopilotControlParameters initAutopilotControlParameters() {
AutopilotControlParameters parameters = new AutopilotControlParameters();
if (mCurrentSubTaskDetail != null) {
parameters.routeID = mCurrentSubTaskDetail.getLineId();
parameters.routeName = mCurrentSubTaskDetail.getLineName();
parameters.startName = PinYinUtil.getPinYinHeadChar(mCurrentSubTaskDetail.getStartSiteName());
parameters.endName = PinYinUtil.getPinYinHeadChar(mCurrentSubTaskDetail.getEndSiteName());
parameters.startLatLon = new AutopilotControlParameters
.AutoPilotLonLat(mCurrentSubTaskDetail.getStartSiteWgs64Lat(), mCurrentSubTaskDetail.getStartSiteWgs64Lon());
parameters.endLatLon = new AutopilotControlParameters
.AutoPilotLonLat(mCurrentSubTaskDetail.getEndSiteWgs64Lat(), mCurrentSubTaskDetail.getEndSiteWgs64Lon());
parameters.vehicleType = VEHICLE_TYPE;
parameters.autoPilotLine = new AutopilotControlParameters.AutoPilotLine(
mCurrentSubTaskDetail.getLineId(), mCurrentSubTaskDetail.getLineName(),
mCurrentSubTaskDetail.getCsvFileUrl(), mCurrentSubTaskDetail.getCsvFileMd5(),
mCurrentSubTaskDetail.getTxtFileUrl(), mCurrentSubTaskDetail.getTxtFileMd5(),
mCurrentSubTaskDetail.getPublishTime(), mCurrentSubTaskDetail.getCarModel(),
mCurrentSubTaskDetail.getCsvFileUrlDPQP(), mCurrentSubTaskDetail.getCsvFileMd5DPQP(),
mCurrentSubTaskDetail.getTxtFileUrlDPQP(), mCurrentSubTaskDetail.getTxtFileMd5DPQP(),
mCurrentSubTaskDetail.getPublishTimeDPQP());
}
return parameters;
}
/**
* 获取当前正在执行的任务
*/
public void getCurrentTask() {
SweeperServiceManager.getCurrentTask(mContext, new OchCommonServiceCallback<BaseResponse<SweeperSubTaskBean>>() {
@Override
public void onSuccess(BaseResponse<SweeperSubTaskBean> data) {
CallerLogger.d(M_SWEEPER + TAG, "getCurrentTask" + GsonUtil.jsonFromObject(data));
if (mSweeperTaskCallback != null && data != null) {
SweeperSubTaskBean sweeperSubTaskBean = data.getData();
if (sweeperSubTaskBean != null && sweeperSubTaskBean.getSubList() != null && sweeperSubTaskBean.getSubList().size() > 0) {
mSweeperTaskCallback.setSubTaskBean(sweeperSubTaskBean, true);
} else {
getMainTaskList(false);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
});
}
/**
* 获取主任务列表
*/
public void getMainTaskList(boolean refresh) {
SweeperServiceManager.getMainTaskList(mContext, new OchCommonServiceCallback<BaseResponse<List<SweeperMainTaskBean>>>() {
@Override
public void onSuccess(BaseResponse<List<SweeperMainTaskBean>> data) {
CallerLogger.d(M_SWEEPER + TAG, "getMainTaskList" + GsonUtil.jsonFromObject(data));
if (mSweeperTaskCallback != null && data != null && data.getCode() == 0) {
List<SweeperMainTaskBean> list = data.getData();
mSweeperTaskCallback.setMainTaskList(list, refresh);
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
/**
* 获取子任务列表
*/
public void getSubTaskList(int taskId, SweeperSubTaskBean sweeperSubTaskBean) {
SweeperServiceManager.getSubTaskList(taskId, mContext, new OchCommonServiceCallback<BaseResponse<List<SubInfo>>>() {
@Override
public void onSuccess(BaseResponse<List<SubInfo>> data) {
CallerLogger.d(M_SWEEPER + TAG, "getSubTaskList" + GsonUtil.jsonFromObject(data));
if (mSweeperTaskCallback != null && data != null && data.getCode() == 0) {
List<SubInfo> list = data.getData();
if (list != null && list.size() > 0) {
sweeperSubTaskBean.setSubList(list);
mSweeperTaskCallback.setSubTaskBean(sweeperSubTaskBean, false);
} else {
getMainTaskList(false);
}
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
/**
* 获取子任务详情包括轨迹文件信息
*/
public void getSubTaskDetail(boolean isFirst, boolean isEnd, int subTaskId, SubTaskTypeEnum subTaskType, boolean isStartSubtask) {
SweeperServiceManager.getSubTaskDetail(subTaskId, mContext, new OchCommonServiceCallback<BaseResponse<SweeperSubTaskDetailBean>>() {
@Override
public void onSuccess(BaseResponse<SweeperSubTaskDetailBean> data) {
CallerLogger.d(M_SWEEPER + TAG, "getSubTaskDetail" + GsonUtil.jsonFromObject(data));
if (data != null) {
mCurrentSubTaskDetail = data.getData();
if (mCurrentSubTaskDetail != null) {
mSweeperTaskCallback.setSubTakDetail(mCurrentSubTaskDetail, subTaskType);
CallerLogger.d(M_SWEEPER + TAG, "getSubTaskDetailsubTaskType=" + subTaskType
+ ", isStartSubtask=" + isStartSubtask + ", mAutopilotState=" + mAutopilotState);
Log.d(M_SWEEPER + TAG, "getSubTaskDetailsubTaskType=" + subTaskType
+ ", isStartSubtask=" + isStartSubtask + ", mAutopilotState=" + mAutopilotState);
if (subTaskType == SubTaskTypeEnum.AUTOPILOT_SUBTYPE) {//自动驾驶子任务
//向自动驾驶发送要下载文件轨迹路径
SweeperTrajectoryManager.getInstance().syncTrajectoryInfo(mCurrentSubTaskDetail);
updateAutopilotControlParameters();
// 不在启动驾驶状态也需要先开启子任务
if (isStartSubtask) {//是否需要自动开始执行任务
subTaskStart(isFirst, isEnd, subTaskId, subTaskType);
if (mAutopilotState == IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING) {
cancelAutoPilot();
startAutopilot();
//提示必须是N档且拉起手刹 才能进入自动驾驶
VoiceNotice.showNotice(mContext.getString(R.string.sweeper_arrive_auto_start_next_subtask_tips));
ToastUtils.showLong(mContext.getString(R.string.sweeper_arrive_auto_start_next_subtask_tips));
}
}
}
}
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
/**
* 子任务开始上报
*/
public void subTaskStart(boolean isFirst, boolean isEnd, int subTaskId, SubTaskTypeEnum subTaskType) {
SweeperServiceManager.subTaskStart(isFirst, isEnd, subTaskId, mContext, new OchCommonServiceCallback<BaseResponse<Boolean>>() {
@Override
public void onSuccess(BaseResponse<Boolean> data) {
CallerLogger.d(M_SWEEPER + TAG, "subTaskStart" + GsonUtil.jsonFromObject(data));
if (mSweeperTaskCallback != null && data != null) {
if (data.getData()) {
mIsSubTaskWorking = true;
isAutopilotSubTaskArriveEndSite = false;
mSweeperTaskCallback.updateSubTaskStatus(TaskStatusEnum.START_SUBTASK, false);
if (subTaskType == SubTaskTypeEnum.AUTOPILOT_SUBTYPE) {//如果是自动驾驶子任务子开启自驾
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
SweeperTaskModel.getInstance().startBeautificationMode();
}
//ÏstartAutopilot(false);
} else {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = true;//是否强制绘制引导线
CallerAutoPilotControlManager.INSTANCE.setIPCDemoMode(false);//是否自动启动自驾
}
}
}
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
/**
* 子任务结束上报
*/
public void subTaskEnd(boolean isFirst, boolean isEnd, int subTaskId) {
SweeperServiceManager.subTaskEnd(isFirst, isEnd, subTaskId, mContext, new OchCommonServiceCallback<BaseResponse<Boolean>>() {
@Override
public void onSuccess(BaseResponse<Boolean> data) {
CallerLogger.d(M_SWEEPER + TAG, "subTaskEnd" +
String.format("isFirst=%b; isEnd=%b; ", isFirst, isEnd) + GsonUtil.jsonFromObject(data));
if (data != null) {
if (data.getData()) {
mIsSubTaskWorking = false;
if (mSweeperTaskCallback != null) {
mSweeperTaskCallback.updateSubTaskStatus(TaskStatusEnum.END_SUBTASK, isEnd);
}
if (isEnd) {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
SweeperTaskModel.getInstance().closeBeautificationMode();
}
clearAutopilotControlParameters();
//结束自动驾驶
cancelAutoPilot();
}
}
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
if (isAutopilotSubTaskArriveEndSite) return;
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
/**
* 子任务跳过上报
*/
public void subTaskSkip(boolean isFirst, boolean isEnd, int subTaskId) {
SweeperServiceManager.subTaskSkip(isFirst, isEnd, subTaskId, mContext, new OchCommonServiceCallback<BaseResponse<Boolean>>() {
@Override
public void onSuccess(BaseResponse<Boolean> data) {
CallerLogger.d(M_SWEEPER + TAG, "subTaskSkip" +
String.format("isFirst=%b; isEnd=%b; ", isFirst, isEnd)+ GsonUtil.jsonFromObject(data));
if (mSweeperTaskCallback != null && data != null) {
if (data.getData()) {
mIsSubTaskWorking = false;
// 跳过子任务也相当于开始并结束一个子任务,相关变量状态需要重置
isAutopilotSubTaskArriveEndSite = false;
mSweeperTaskCallback.updateSubTaskStatus(TaskStatusEnum.JUMP_OVER_SUBTASK, isEnd);
}
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
/**
* 主任务重置
*/
public void mainTaskReset(int taskId) {
SweeperServiceManager.mainTaskReset(taskId, mContext, new OchCommonServiceCallback<BaseResponse<Boolean>>() {
@Override
public void onSuccess(BaseResponse<Boolean> data) {
CallerLogger.d(M_SWEEPER + TAG, "mainTaskReset" + GsonUtil.jsonFromObject(data));
// 主任务重置 当前子任务也需重置
mIsSubTaskWorking = false;
isAutopilotSubTaskArriveEndSite = false;
if (mSweeperTaskCallback != null && data != null) {
mSweeperTaskCallback.setMainTaskReset(data.getData());
}
}
@Override
public void onFail(int code, String msg) {
if (ToastUtilsOch.isCustomFastClick(5000)) {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort("网络异常,请稍后重试");
} else {
ToastUtils.showShort(msg);
}
}
}
@Override
public void onError() {
if (!NetworkUtils.isConnected(mContext)) {
ToastUtils.showShort(mContext.getString(R.string.network_error_tip));
} else {
ToastUtils.showShort(mContext.getString(R.string.request_error_tip));
}
}
});
}
//结束自动驾驶
public void cancelAutoPilot() {
CallerAutoPilotControlManager.INSTANCE.cancelAutoPilot();
}
}

View File

@@ -0,0 +1,103 @@
package com.mogo.och.sweeper.operate.net;
import com.mogo.och.sweeper.operate.bean.BaseResponse;
import com.mogo.och.sweeper.operate.bean.SubInfo;
import com.mogo.och.sweeper.operate.bean.SubStartRequest;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean;
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskDetailBean;
import java.util.List;
import java.util.Map;
import io.reactivex.Observable;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.Query;
import retrofit2.http.QueryMap;
/**
* 清扫车相关接口
*/
public interface ISweeperApiService {
/**
* 获取当前正在执行的任务
* @param appId
* @param ticket
* @param carSn
* @return
*/
@GET("/och-sweep/api/task/v1/currentTask")
Observable<BaseResponse<SweeperSubTaskBean>> getCurrentTask(@Header ("appId") String appId, @Header("ticket") String ticket, @Query("carSn") String carSn);
/**
* 清扫车任务列表
* @param appId
* @param ticket
* @param carSn
* @return
*/
@GET("/och-sweep/api/task/v1/mainTaskList")
Observable<BaseResponse<List<SweeperMainTaskBean>>> getMainTaskList(@Header ("appId") String appId, @Header("ticket") String ticket, @Query("carSn") String carSn);
/**
* 清扫车子任务列表
* @param appId
* @param ticket
* @param map
* @return
*/
@GET("/och-sweep/api/task/v1/subTaskList")
Observable<BaseResponse<List<SubInfo>>> getSubTaskList(@Header ("appId") String appId, @Header("ticket") String ticket, @QueryMap Map<String, String> map);
/**
* 子任务详情 包括子任务轨迹信息
* @param appId
* @param ticket
* @param map
* @return
*/
@GET("/och-sweep/api/task/v1/subTaskInfo")
Observable<BaseResponse<SweeperSubTaskDetailBean>> getSubTaskDetail(@Header ("appId") String appId, @Header("ticket") String ticket, @QueryMap Map<String, String> map);
/**
* 子任务开始
* @param appId
* @param ticket
* @param subStartRequest
* @return
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@POST("/och-sweep/api/task/v1/subTaskStart")
Observable<BaseResponse<Boolean>> subTaskStart(@Header ("appId") String appId, @Header("ticket") String ticket, @Body SubStartRequest subStartRequest);
/**
* 子任务结束
* @param appId
* @param ticket
* @param subStartRequest
* @return
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@POST("/och-sweep/api/task/v1/subTaskEnd")
Observable<BaseResponse<Boolean>> subTaskEnd(@Header ("appId") String appId, @Header("ticket") String ticket, @Body SubStartRequest subStartRequest);
/**
* 子任务跳过
* @param appId
* @param ticket
* @param subStartRequest
* @return
*/
@Headers({"Content-type:application/json;charset=UTF-8"})
@POST("/och-sweep/api/task/v1/subTaskSkip")
Observable<BaseResponse<Boolean>> subTaskSkip(@Header ("appId") String appId, @Header("ticket") String ticket, @Body SubStartRequest subStartRequest);
/**
* 主任务重置
* @param appId
* @param ticket
* @return
*/
@GET("/och-sweep/api/task/v1/mainTaskReset")
Observable<BaseResponse<Boolean>> mainTaskReset(@Header ("appId") String appId, @Header("ticket") String ticket,@QueryMap Map<String, String> map);
}

View File

@@ -0,0 +1,178 @@
package com.mogo.och.sweeper.operate.net
import android.content.Context
import com.elegant.network.utils.GsonUtil
import com.mogo.cloud.passport.MoGoAiCloudClientConfig
import com.mogo.eagle.core.network.MoGoRetrofitFactory
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.och.common.module.biz.constant.OchCommonConst
import com.mogo.och.common.module.biz.network.OchCommonServiceCallback
import com.mogo.och.common.module.biz.network.OchCommonSubscribeImpl
import com.mogo.och.common.module.biz.network.interceptor.transformTry
import com.mogo.och.sweeper.operate.model.SweeperTaskModel
import com.mogo.och.sweeper.operate.bean.BaseResponse
import com.mogo.och.sweeper.operate.bean.SubInfo
import com.mogo.och.sweeper.operate.bean.SubStartRequest
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskDetailBean
object SweeperServiceManager {
private val TAG = SweeperTaskModel::class.java.simpleName
private val mService: ISweeperApiService = MoGoRetrofitFactory.getInstance(OchCommonConst.getSweeperUrl()).create(
ISweeperApiService::class.java
)
/**
* 获取当前正在执行的任务
*/
@JvmStatic
fun getCurrentTask(context: Context, callback: OchCommonServiceCallback<BaseResponse<SweeperSubTaskBean>>?) {
val map = hashMapOf<String, String>()
map["serviceAppId"] = getServiceAppId()
map["token"] = getToken()
map["sn"] = getCarSN()
d(SceneConstant.M_SWEEPER + TAG, "getCurrentTask" + GsonUtil.jsonFromObject(map))
mService.getCurrentTask(
getServiceAppId(),
getToken(),
getCarSN()
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "getCurrentTask"))
}
/**
* 获取主任务列表
*/
@JvmStatic
fun getMainTaskList(context: Context, callback: OchCommonServiceCallback<BaseResponse<MutableList<SweeperMainTaskBean>>>?) {
val map = hashMapOf<String, String>()
map["serviceAppId"] = getServiceAppId()
map["token"] = getToken()
map["sn"] = getCarSN()
d(SceneConstant.M_SWEEPER + TAG, "getMainTaskList" + GsonUtil.jsonFromObject(map))
mService.getMainTaskList(
getServiceAppId(),
getToken(),
getCarSN()
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "getMainTaskList"))
}
/**
* 获取子任务列表
*/
@JvmStatic
fun getSubTaskList(taskId: Int, context: Context, callback: OchCommonServiceCallback<BaseResponse<MutableList<SubInfo>>>?) {
val map = hashMapOf<String, String>()
map["carSn"] = getCarSN()
map["taskId"] = taskId.toString()
d(SceneConstant.M_SWEEPER + TAG, "getSubTaskList" + GsonUtil.jsonFromObject(map))
mService.getSubTaskList(
getServiceAppId(),
getToken(),
map
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "getSubTaskList"))
}
/**
* 获取子任务详情包括轨迹文件信息
*/
@JvmStatic
fun getSubTaskDetail(subTaskId: Int, context: Context, callback: OchCommonServiceCallback<BaseResponse<SweeperSubTaskDetailBean>>?) {
val map = hashMapOf<String, String>()
map["carSn"] = getCarSN()
map["taskId"] = subTaskId.toString()
d(SceneConstant.M_SWEEPER + TAG, "getSubTaskDetail" + GsonUtil.jsonFromObject(map))
mService.getSubTaskDetail(
getServiceAppId(),
getToken(),
map
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "getSubTaskList"))
}
/**
* 子任务开始上报
*/
@JvmStatic
fun subTaskStart(isFirst: Boolean, isEnd: Boolean, subTaskId: Int, context: Context, callback: OchCommonServiceCallback<BaseResponse<Boolean>>?) {
val subStartRequest = SubStartRequest(getCarSN(), subTaskId, System.currentTimeMillis(), isFirst, isEnd)
d(SceneConstant.M_SWEEPER + TAG, "subTaskStart" + GsonUtil.jsonFromObject(subStartRequest))
mService.subTaskStart(
getServiceAppId(),
getToken(),
subStartRequest
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "subTaskStart"))
}
/**
* 子任务结束上报
*/
@JvmStatic
fun subTaskEnd(isFirst: Boolean, isEnd: Boolean, subTaskId: Int, context: Context, callback: OchCommonServiceCallback<BaseResponse<Boolean>>?) {
val subStartRequest = SubStartRequest(getCarSN(), subTaskId, System.currentTimeMillis(), isFirst, isEnd)
d(SceneConstant.M_SWEEPER + TAG, "subTaskEnd" + GsonUtil.jsonFromObject(subStartRequest))
mService.subTaskEnd(
getServiceAppId(),
getToken(),
subStartRequest
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "subTaskEnd"))
}
/**
* 子任务跳过上报
*/
@JvmStatic
fun subTaskSkip(isFirst: Boolean, isEnd: Boolean, subTaskId: Int, context: Context, callback: OchCommonServiceCallback<BaseResponse<Boolean>>?) {
val subStartRequest = SubStartRequest(getCarSN(), subTaskId, System.currentTimeMillis(), isFirst, isEnd)
d(SceneConstant.M_SWEEPER + TAG, "subTaskSkip" + GsonUtil.jsonFromObject(subStartRequest))
mService.subTaskSkip(
getServiceAppId(),
getToken(),
subStartRequest
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "subTaskSkip"))
}
/**
* 主任务重置
*/
@JvmStatic
fun mainTaskReset(taskId: Int, context: Context, callback: OchCommonServiceCallback<BaseResponse<Boolean>>?) {
val map = hashMapOf<String, String>()
map["carSn"] = getCarSN()
map["taskId"] = taskId.toString()
d(SceneConstant.M_SWEEPER + TAG, "mainTaskReset" + GsonUtil.jsonFromObject(map))
mService.mainTaskReset(
getServiceAppId(),
getToken(),
map
)
.transformTry()
.subscribe(OchCommonSubscribeImpl(context, callback, "mainTaskReset"))
}
private fun getCarSN(): String {
return MoGoAiCloudClientConfig.getInstance().sn
}
private fun getServiceAppId(): String {
return MoGoAiCloudClientConfig.getInstance().serviceAppId
}
private fun getToken(): String {
return MoGoAiCloudClientConfig.getInstance().token
}
}

View File

@@ -0,0 +1,460 @@
package com.mogo.och.sweeper.operate.presenter;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_SWEEPER;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import com.amap.api.maps.model.LatLng;
import com.mogo.commons.AbsMogoApplication;
import com.mogo.commons.mvp.Presenter;
import com.mogo.eagle.core.data.autopilot.AutopilotStatusInfo;
import com.mogo.eagle.core.data.config.FunctionBuildConfig;
import com.mogo.eagle.core.data.map.MogoLocation;
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener;
import com.mogo.eagle.core.function.api.autopilot.IMoGoSweeperFutianCleanSystemListener;
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.CallerSweeperFutianCleanSystemListenerManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.ThreadUtils;
import com.mogo.och.common.module.manager.autopilotmanager.OCHAdasAbilityManager;
import com.mogo.och.common.module.utils.CoordinateCalculateRouteUtil;
import com.mogo.och.common.module.utils.NumberFormatUtil;
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean;
import com.mogo.och.sweeper.common.bean.SweeperRoutePlanningUpdateReqBean;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskDetailBean;
import com.mogo.och.sweeper.common.callback.ISweeperADASStatusCallback;
import com.mogo.och.sweeper.operate.callback.ISweeperControllerStatusCallback;
import com.mogo.och.sweeper.operate.callback.ISweeperTaskCallback;
import com.mogo.och.sweeper.common.callback.ISweeperTaskRouteCallback;
import com.mogo.och.sweeper.operate.constant.SubTaskTypeEnum;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import com.mogo.och.sweeper.operate.constant.TaskStatusEnum;
import com.mogo.och.sweeper.common.database.MyDataBase;
import com.mogo.och.sweeper.common.database.bean.WeltDataBean;
import com.mogo.och.sweeper.operate.fragment.SweeperFragment;
import com.mogo.och.sweeper.operate.model.SweeperTaskModel;
import com.mogo.och.sweeper.common.util.SweeperFutianCmdUtil;
import com.mogo.och.sweeper.operate.util.SweeperTrajectoryManager;
import org.jetbrains.annotations.NotNull;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import chassis.ChassisStatesOuterClass;
import mogo.telematics.pad.MessagePad;
import mogo_msg.MogoReportMsg;
import planning.RoboSweeperTaskIndexOuterClass;
import system_master.SsmInfo;
import system_master.SystemStatusInfo;
/**
* 网约车小巴
*
* @author tongchenfei
*/
public class SweeperPresenter extends Presenter<SweeperFragment>
implements IMoGoAutopilotStatusListener, ISweeperControllerStatusCallback, ISweeperADASStatusCallback,
IMoGoSweeperFutianCleanSystemListener, ISweeperTaskCallback, ISweeperTaskRouteCallback {
private static final String TAG = "SweeperPresenter";
//当前子任务id
private int mSubTaskId = 0;
// 清扫模式回调时间间隔
private static final long VEHICLE_STATE_INTERVAL_MILLIS = 500L;
// 清扫模式当前时间戳
private long mVehicleStateCurrentTimeMillis;
// 贴边数据回调时间间隔
private static final long WELT_DATA_INTERVAL_MILLIS = 1000L;
// 贴边数据当前时间戳
private long mWeltDataCurrentTimeMillis;
private String longitude;//经度
private String latitude;//纬度
public SweeperPresenter(SweeperFragment view) {
super(view);
//2021.11.1 鹰眼架构整合由IMoGoAutopilotStatusListener逐步替代IMogoAdasOCHCallback接口
CallerAutoPilotStatusListenerManager.INSTANCE.addListener(TAG, this);
//清扫车模式和贴边数据回调监听
CallerSweeperFutianCleanSystemListenerManager.INSTANCE.addListener(TAG, this);
SweeperTaskModel.getInstance().init();
OCHAdasAbilityManager.getInstance().init(AbsMogoApplication.getApp());
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
super.onCreate(owner);
initModelListener();
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
super.onDestroy(owner);
SweeperTaskModel.getInstance().release();
releaseListener();
}
public void initModelListener() {
SweeperTaskModel.getInstance().setControllerStatusCallback(this);
SweeperTaskModel.getInstance().setAdasStatusCallback(this);
SweeperTaskModel.getInstance().setSweeperTaskCallback(this);
}
public void releaseListener() {
SweeperTaskModel.getInstance().setControllerStatusCallback(null);
SweeperTaskModel.getInstance().setAdasStatusCallback(null);
SweeperTaskModel.getInstance().setSweeperTaskCallback(null);
CallerSweeperFutianCleanSystemListenerManager.INSTANCE.removeListener(TAG);
}
@Override
public void onAutopilotRouteLineId(long lineId) {
}
@Override
public void onAutopilotStatusResponse(@NotNull AutopilotStatusInfo autopilotStatusInfo) {
}
@Override
public void onAutopilotStatusResponse(int state) {
SweeperTaskModel.getInstance().setAutopilotState(state);
ThreadUtils.runOnUiThread(() -> mView.startAutoBtn(state));
switch (state) {
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE://不可自动驾驶
break;
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE://人工驾驶
if (SweeperTaskModel.getInstance().getSubWorking()) {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
SweeperTaskModel.getInstance().startBeautificationMode();
}
} else {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
//关闭美化模式
SweeperTaskModel.getInstance().closeBeautificationMode();
}
}
break;
case IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING://自动驾驶中
SweeperTaskModel.getInstance().triggerStartServiceEvent(
SweeperTaskModel.getInstance().isRestartAutopilot(), true);
if (SweeperTaskModel.getInstance().getSubWorking()) {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
SweeperTaskModel.getInstance().startBeautificationMode();
}
}
break;
case IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING://平行驾驶
if (SweeperTaskModel.getInstance().getSubWorking()) {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
FunctionBuildConfig.isIgnoreConditionsDrawAutopilotTrajectoryData = true;//是否强制绘制引导线
CallerAutoPilotControlManager.INSTANCE.setIPCDemoMode(false);//是否自动启动自驾
}
} else {
if (FunctionBuildConfig.isDemoMode) {//美化模式开启时
//关闭美化模式
SweeperTaskModel.getInstance().closeBeautificationMode();
}
}
break;
default:
break;
}
}
@Override
public void onVRModeChanged(boolean isVRMode) {
ThreadUtils.runOnUiThread(() -> mView.onVRModeChanged(isVRMode));
}
@Override
public void onCarLocationChanged(MogoLocation location) {
if (null != location) {
ThreadUtils.runOnUiThread(() -> mView.updateSpeedView((float) location.getGnssSpeed()));
ThreadUtils.runOnUiThread(() -> mView.setCurrentLocation(location));
}
}
@Override
public void startOpenAutopilot() {
}
/**
* 清除路线缓存数据
*/
public void clearRouteList(){
SweeperTaskModel.getInstance().clearRouteList();
}
public void startAutopilot() {
SweeperTaskModel.getInstance().startAutopilot();
}
/**
* 设置当前子任务信息
*/
public void setSubtask(boolean isFirstSubtask, boolean isLastSubtask, int subTaskId,int subTaskType) {
this.mSubTaskId = subTaskId;
SweeperTaskModel.getInstance().setSubtask(isFirstSubtask, isLastSubtask, subTaskId,subTaskType);
}
public void setWorking(boolean isWorking){
SweeperTaskModel.getInstance().setWorking(isWorking);
}
/**
* 测试使用
*/
public void debugAutoPilotStatus(int status) {
AutopilotStatusInfo info = new AutopilotStatusInfo();
info.setState(status);
onAutopilotStatusResponse(info);
}
@Override
public void onAutopilotGuardian(MogoReportMsg.MogoReportMessage guardianInfo) {
SweeperTrajectoryManager.getInstance().onAutopilotGuardian(guardianInfo);
}
@Override
public void onAutopilotIpcConnectStatusChanged(int status, @Nullable String reason) {
}
@Override
public void onAutopilotStatusRespByQuery(@NonNull SystemStatusInfo.StatusInfo status) {
}
@Override
public void onSystemStatus(@NonNull SsmInfo.SsmStatusInf statusInf) {
}
@Override
public void onAutopilotArriveAtStation(@Nullable MessagePad.ArrivalNotification arrivalNotification) {
SweeperTaskModel.getInstance().onArriveTaskEnd(arrivalNotification);
}
@Override
public void onAutopilotSNRequest() {
}
@Override
public void onStartAdasFailure() {
}
public void onArriveTaskEnd(@Nullable MessagePad.ArrivalNotification arrivalNotification) {
SweeperTaskModel.getInstance().onArriveTaskEnd(arrivalNotification);
}
@Override
public void onSweeperFutianCleanSystemState(@NonNull ChassisStatesOuterClass.SweeperFuTianTaskSystemStates cleanSystemState) {
long current = System.currentTimeMillis();
if (current - mVehicleStateCurrentTimeMillis <= VEHICLE_STATE_INTERVAL_MILLIS) {
return;
}
mVehicleStateCurrentTimeMillis = current;
boolean clean_open_requirement = cleanSystemState.getSecuMotWorkSts();
// 洗扫
boolean clean_mode_wash_sweep = cleanSystemState.getSecuModWashSweepSts();
// 纯洗
boolean clean_mode_pure_wash = cleanSystemState.getSecuModWashSts();
// 纯吸
boolean clean_mode_pure_draw = cleanSystemState.getSecuWorkTonSts();
// 左侧
boolean clean_direction_left_side = cleanSystemState.getSecuWorkLeftSts();
// 右侧
boolean clean_direction_right_side = cleanSystemState.getSecuWorkRightSts();
// 两侧
boolean clean_direction_both_side = cleanSystemState.getSecuWorkOnBothsidesSts();
// 作业强度状态
boolean clean_intensity_standard = cleanSystemState.getSecuWorkStandSts();
boolean clean_intensity_strong = cleanSystemState.getSecuWorkStrongSts();
// 纯扫 模式判断不是另外其他3个模式同时有清扫方向说明开启了纯扫模式
boolean clean_mode_pure_sweep = SweeperFutianCmdUtil.checkIfCleanModePureSweep(cleanSystemState);
String stringBuilder = "开关:" +
clean_open_requirement +
"\n" +
"纯扫:" +
clean_mode_wash_sweep +
"纯洗:" +
clean_mode_pure_wash +
"纯吸:" +
clean_mode_pure_draw +
"纯扫:" +
"\n" +
clean_mode_pure_sweep +
"左侧:" +
clean_direction_left_side +
"右侧:" +
clean_direction_right_side +
"两侧:" +
"\n" +
clean_direction_both_side +
"标准:" +
clean_intensity_standard +
"强力:" +
clean_intensity_strong;
CallerLogger.d(M_SWEEPER + TAG, "onSweeperFutianCleanSystemState" + stringBuilder);
mView.onSweeperFutianCleanSystemState(cleanSystemState);
}
@Override
public void onSweeperFutianTaskIndexData(@NonNull RoboSweeperTaskIndexOuterClass.RoboSweeperTaskIndex roboSweeperTaskIndex) {
long current = System.currentTimeMillis();
if (current - mWeltDataCurrentTimeMillis <= WELT_DATA_INTERVAL_MILLIS) {
return;
}
mWeltDataCurrentTimeMillis = current;
String stringBuilder = "清扫模式:" +
roboSweeperTaskIndex.getCleanMode() +
"清扫方向:" +
roboSweeperTaskIndex.getCleanDirection() +
"清扫强度:" +
roboSweeperTaskIndex.getCleanIntensity() +
"贴边距离:" +
roboSweeperTaskIndex.getDistToRefEdgePoint() +
"经度:" +
roboSweeperTaskIndex.getLocLon() +
"纬度:" +
roboSweeperTaskIndex.getLocLat();
CallerLogger.d(M_SWEEPER + TAG, "onSweeperFutianTaskIndexData" + stringBuilder);
String tempLongitude=NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLon(), 6);
String tempLatitude=NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLat(), 6);
//用于过滤车是否停在原地,经纬度相同的情况
if (!tempLongitude.equals(longitude)&&!tempLatitude.equals(latitude)){
latitude = NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLat(), 6);
longitude = NumberFormatUtil.cutOutNumber(roboSweeperTaskIndex.getLocLon(), 6);
//保存贴边数据到数据库中
WeltDataBean weltDataBean = new WeltDataBean();
//把wgs坐标系坐标转换成gcj坐标
LatLng latLng = CoordinateCalculateRouteUtil.coordinateConverterWgsToGcj(AbsMogoApplication.getApp(), roboSweeperTaskIndex.getLocLon(),
roboSweeperTaskIndex.getLocLat());
weltDataBean.setLocLon(latLng.longitude);
weltDataBean.setLocLat(latLng.latitude);
int edgeCleanState = roboSweeperTaskIndex.getEdgeCleanState();
if(edgeCleanState==0){
weltDataBean.setWeltDistance(SweeperConst.NONWELT);
}else {
weltDataBean.setWeltDistance(roboSweeperTaskIndex.getDistToRefEdgePoint());
}
weltDataBean.setCleanMode(roboSweeperTaskIndex.getCleanMode());
weltDataBean.setCleanDirection(roboSweeperTaskIndex.getCleanDirection());
weltDataBean.setCleanIntensity(roboSweeperTaskIndex.getCleanIntensity());
weltDataBean.setSubTaskId(String.valueOf(mSubTaskId));
MyDataBase.getInstance().getWeltDataDao().insert(weltDataBean);
String distance;
if (roboSweeperTaskIndex.getDistToRefEdgePoint() >= 1.0) {//大于等于1m
distance = format(roboSweeperTaskIndex.getDistToRefEdgePoint()) + "m";
} else {//小于1m
distance = Math.round(roboSweeperTaskIndex.getDistToRefEdgePoint() * 100) + "cm";//m->cm 四舍五入到整数
}
mView.setWeltDataToMap((ArrayList<WeltDataBean>) MyDataBase.getInstance().getWeltDataDao().loadAllWeltDataInfo(), true, distance);
}
}
public static String format(double value) {
BigDecimal bd = new BigDecimal(value);
bd = bd.setScale(2, RoundingMode.HALF_UP);
return bd.toString();
}
/**
* 获取当前正在执行的任务
*/
public void getCurrentTask() {
SweeperTaskModel.getInstance().getCurrentTask();
}
/**
* 获取主任务列表
*/
public void getMainTaskList(boolean refresh) {
SweeperTaskModel.getInstance().getMainTaskList(refresh);
}
/**
* 获取子任务列表
*/
public void getSubTaskList(int taskId, SweeperSubTaskBean sweeperSubTaskBean) {
SweeperTaskModel.getInstance().getSubTaskList(taskId, sweeperSubTaskBean);
}
/**
* 获取子任务详情包括轨迹文件信息
*/
public void getSubTaskDetail(boolean isFirst, boolean isEnd, int subTaskId, SubTaskTypeEnum subTaskType, boolean isStartSubtask) {
SweeperTaskModel.getInstance().getSubTaskDetail(isFirst, isEnd, subTaskId, subTaskType, isStartSubtask);
}
/**
* 开始任务
*/
public void startTask(boolean isFirst, boolean isEnd, int subTaskId, SubTaskTypeEnum subTaskType, int subTaskStatus) {
if (SubTaskTypeEnum.AUTOPILOT_SUBTYPE.getCode() == subTaskStatus) {
SweeperTaskModel.getInstance().subTaskStart(isFirst, isEnd, subTaskId, subTaskType);
}
}
/**
* 子任务结束上报
*/
public void subTaskEnd(boolean isFirst, boolean isEnd, int subTaskId) {
SweeperTaskModel.getInstance().subTaskEnd(isFirst, isEnd, subTaskId);
}
/**
* 子任务跳过上报
*/
public void subTaskSkip(boolean isFirst, boolean isEnd, int subTaskId) {
SweeperTaskModel.getInstance().subTaskSkip(isFirst, isEnd, subTaskId);
}
/**
* 主任务重置
*/
public void mainTaskReset(int taskId) {
SweeperTaskModel.getInstance().mainTaskReset(taskId);
}
@Override
public void setMainTaskList(List<SweeperMainTaskBean> mainTaskBeanList,boolean refresh) {
mView.setMainTaskList(mainTaskBeanList,refresh);
}
@Override
public void setSubTaskBean(SweeperSubTaskBean subTaskBean, boolean isWorkingSubTask) {
mView.setSubTaskBean(subTaskBean, isWorkingSubTask);
}
@Override
public void updateSubTaskStatus(TaskStatusEnum typeEnum, boolean isLastSubTask) {
mView.updateSubTaskStatus(typeEnum, isLastSubTask);
}
@Override
public void setMainTaskReset(boolean isSuccess) {
mView.setMainTaskReset(isSuccess);
}
@Override
public void setSubTakDetail(@NonNull SweeperSubTaskDetailBean subTaskDetailBean, SubTaskTypeEnum subTaskTypeEnum) {
}
@Override
public void setRouteList(@NonNull ArrayList<SweeperRoutePlanningUpdateReqBean.Result> routeList) {
mView.setTaskRouteList(routeList);
}
@Override
public void onAutopilotDockerInfo(@NonNull String dockerVersion) {
}
}

View File

@@ -0,0 +1,79 @@
package com.mogo.och.sweeper.operate.ui.adapter
import android.graphics.Color
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.operate.bean.SweeperMainTaskBean
class TaskListAdapter() : RecyclerView.Adapter<TaskListAdapter.TaskViewHolder>() {
private var mItemClickListener: TaskItemClickListener? = null
private var mSelectPosition: Int = -1
private var data:MutableList<SweeperMainTaskBean>?=null
fun setOnTaskItemClickListener(itemClickListener: TaskItemClickListener?) {
mItemClickListener = itemClickListener
}
class TaskViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val selectIv: ImageView
val taskName: TextView//任务名称
val taskDesc: TextView//任务描述
val tvTaskTime: TextView//任务时间
init {
selectIv = itemView.findViewById(R.id.ivTaskSelect)
taskName = itemView.findViewById(R.id.tvTaskName)
taskDesc = itemView.findViewById(R.id.tvTaskDesc)
tvTaskTime = itemView.findViewById(R.id.tvTaskTime)
}
}
fun selectPosition(position: Int) {
this.mSelectPosition = position
notifyDataSetChanged()
}
fun setTaskListData(data: MutableList<SweeperMainTaskBean>?){
this.data=data
notifyDataSetChanged()
}
interface TaskItemClickListener {
fun onItemClick(position: Int,mainTask: SweeperMainTaskBean)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.sweeper_item_task_info, parent, false
)
return TaskViewHolder(view)
}
override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {
data?.let { mainTask->
holder.taskName.text= mainTask[position].mainTaskName
holder.taskDesc.text= "全程包含${mainTask[position].subTaskTotal}个子任务"
val calendar = DateTimeUtil.formatLongToCalendar(mainTask[position].mainTaskStartTime)
if (DateTimeUtil.compareDateIsCurrentDay(calendar)){
holder.tvTaskTime.text="今天${DateTimeUtil.formatCalendarToString(calendar, DateTimeUtil.HH_mm)}"
}else{
holder.tvTaskTime.text=DateTimeUtil.formatCalendarToString(calendar, DateTimeUtil.MM_dd_HH_mm)
}
holder.itemView.setOnClickListener {
mItemClickListener?.onItemClick(position,mainTask[position])
}
}
if (position == mSelectPosition) {
holder.selectIv.visibility = View.VISIBLE
holder.itemView.setBackgroundColor(Color.parseColor("#FF122B4E"))
} else {
holder.selectIv.visibility = View.GONE
holder.itemView.setBackgroundColor(Color.parseColor("#00000000"))
}
}
override fun getItemCount(): Int = data?.size ?: 0
}

View File

@@ -0,0 +1,70 @@
package com.mogo.och.sweeper.operate.ui.dialog
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.och.sweeper.R
/**
* 不带带有title, tip,confirm,cancel的dialog
*/
class SweeperManualDrivingDialog: BaseFloatDialog, LifecycleObserver {
private var commonConfirm : TextView? = null
private var commonTips : TextView? = null
private var clickListener: ClickListener? = null
constructor(builder: Builder, context: Context) : super(context) {
commonTips?.text = builder.tipsStr
commonConfirm?.text = builder.confirmStr
}
init{
setContentView(R.layout.dialog_sweeper_manual_driving)
setCanceledOnTouchOutside(false)
commonConfirm = findViewById(R.id.sweeper_common_confirm)
commonTips = findViewById(R.id.sweeper_common_tips)
commonConfirm?.setOnClickListener{
clickListener?.confirm()
dismiss()
}
}
fun setClickListener(clickListener: ClickListener) {
this.clickListener = clickListener
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
interface ClickListener{
fun confirm()
}
class Builder{
var tipsStr:String = ""
var confirmStr:String = ""
fun tips(tips: String) : Builder {
this.tipsStr = tips
return this
}
fun confirmStr(commit: String) : Builder {
this.confirmStr = commit
return this
}
fun build(context: Context): SweeperManualDrivingDialog? {
return SweeperManualDrivingDialog(this,context)
}
}
}

View File

@@ -0,0 +1,85 @@
package com.mogo.och.sweeper.operate.ui.dialog
import android.content.Context
import android.widget.TextView
import androidx.lifecycle.LifecycleObserver
import com.mogo.eagle.core.function.hmi.dialog.BaseFloatDialog
import com.mogo.och.sweeper.R
/**
* 不带带有title, tip,confirm,cancel的dialog
*/
class SweeperNoTitleCommonDialog: BaseFloatDialog, LifecycleObserver {
private var commonConfirm : TextView? = null
private var commonCancel : TextView? = null
private var commonTips : TextView? = null
private var clickListener: ClickListener? = null
constructor(builder: Builder, context: Context) : super(context) {
commonTips?.text = builder.tipsStr
commonCancel?.text = builder.cancelStr
commonConfirm?.text = builder.confirmStr
}
init{
setContentView(R.layout.dialog_sweeper_no_title)
setCanceledOnTouchOutside(true)
commonConfirm = findViewById(R.id.sweeper_common_confirm)
commonCancel = findViewById(R.id.sweeper_common_cancel)
commonTips = findViewById(R.id.sweeper_common_tips)
commonConfirm?.setOnClickListener{
clickListener?.confirm()
dismiss()
}
commonCancel?.setOnClickListener {
clickListener?.cancel()
dismiss()
}
}
fun setClickListener(clickListener: ClickListener) {
this.clickListener = clickListener
}
fun showUpgradeDialog(){
if(isShowing){
return
}
show()
}
interface ClickListener{
fun confirm()
fun cancel()
}
class Builder{
var tipsStr:String = ""
var confirmStr:String = ""
var cancelStr:String = ""
fun tips(tips: String) : Builder {
this.tipsStr = tips
return this
}
fun confirmStr(commit: String) : Builder {
this.confirmStr = commit
return this
}
fun cancelStr(cancel: String) : Builder {
this.cancelStr = cancel
return this
}
fun build(context: Context): SweeperNoTitleCommonDialog? {
return SweeperNoTitleCommonDialog(this,context)
}
}
}

View File

@@ -0,0 +1,91 @@
package com.mogo.och.sweeper.operate.ui.popwindow
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams
import android.widget.PopupWindow
import android.widget.TextView
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.operate.constant.TaskStatusEnum
/**
* 当前任务操作菜单栏
*/
class MenuPopWindow : PopupWindow, View.OnClickListener{
private var mTvJumpOverTask: TextView? = null
private var mTaskJumpLineView: View? = null
private var mTvEndTask: TextView? = null
private var mTvCancelTask: TextView? = null
private var mMenuItemClickListener: OnMenuItemOnClickListener?=null
constructor(context: Context,menuItemClickListener: OnMenuItemOnClickListener) : super(context) {
init(context)
this.mMenuItemClickListener=menuItemClickListener
}
/**
* 初始化布局
*/
private fun init(context: Context) {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
var view: View = LayoutInflater.from(context).inflate(R.layout.sweeper_task_menu, null)
mTvJumpOverTask = view.findViewById(R.id.tvJumpOverTask)
mTaskJumpLineView = view.findViewById(R.id.taskJumpLineView)
mTvEndTask = view.findViewById(R.id.tvEndTask)
mTvCancelTask = view.findViewById(R.id.tvCancelTask)
mTvJumpOverTask?.setOnClickListener(this)
mTvEndTask?.setOnClickListener(this)
mTvCancelTask?.setOnClickListener(this)
width = context.resources.getDimension(R.dimen.dp_174).toInt()
height = LayoutParams.WRAP_CONTENT
contentView = view
isFocusable = true
isOutsideTouchable = true
}
/**
* 设置是否是手动任务
* 1云端自动驾驶任务
* 2云端手动驾驶任务
* 3: 运营任务
*/
fun setMenuView(type: Int) {
if (type == 1) {
mTvJumpOverTask?.visibility = View.GONE
mTaskJumpLineView?.visibility = View.GONE
} else {
mTvJumpOverTask?.visibility = View.VISIBLE
mTaskJumpLineView?.visibility = View.VISIBLE
}
}
interface OnMenuItemOnClickListener {
fun onMenuItemClick(itemType: TaskStatusEnum)
}
override fun onClick(v: View) {
when(v.id){
R.id.tvJumpOverTask->{
mMenuItemClickListener?.onMenuItemClick(TaskStatusEnum.JUMP_OVER_SUBTASK)
dismiss()
}
R.id.tvEndTask->{
mMenuItemClickListener?.onMenuItemClick(TaskStatusEnum.END_TASK)
dismiss()
}
R.id.tvCancelTask->{
mMenuItemClickListener?.onMenuItemClick(TaskStatusEnum.CANCEL_TASK)
dismiss()
}
}
}
}

View File

@@ -0,0 +1,59 @@
package com.mogo.och.sweeper.operate.ui.popwindow
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.PopupWindow
import chassis.ChassisStatesOuterClass
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.callback.ICleaningModeStateCallback
import com.mogo.och.sweeper.common.constant.OperateStateEnum
import com.mogo.och.sweeper.operate.ui.SweeperOperatePanelView
/**
* 清扫车面板浮窗
*/
class SweeperOperatePanelPopWindow : PopupWindow{
private var mOperatePanelView: SweeperOperatePanelView? = null
constructor(context: Context) : super(context) {
init(context)
}
private fun init(context: Context) {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
mOperatePanelView = LayoutInflater.from(context).inflate(R.layout.sweeper_popwindow_operate_panel, null) as SweeperOperatePanelView?
width = ViewGroup.LayoutParams.WRAP_CONTENT
height = ViewGroup.LayoutParams.WRAP_CONTENT
contentView = mOperatePanelView
}
/**
* 设置清扫模式数据
*/
fun setCleanSystemState(
cleanSystemState: ChassisStatesOuterClass.SweeperFuTianTaskSystemStates?,
cleaningModeStateCallback: ICleaningModeStateCallback
) {
mOperatePanelView?.setSweeperFutianCleanSystemState(cleanSystemState, cleaningModeStateCallback)
}
/**.
*
* 设置是否让popWindow消失
*/
fun setIsOutsideTouchable(isOutsideTouchable:Boolean){
this.isFocusable = isOutsideTouchable
this.isOutsideTouchable = isOutsideTouchable
this.isTouchable=isOutsideTouchable
}
/**
* 设置是否展示状态同步中
*/
fun showSyncing(operateState: OperateStateEnum){
mOperatePanelView?.showSyncing(operateState)
}
}

View File

@@ -0,0 +1,149 @@
package com.mogo.och.sweeper.operate.util;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_SWEEPER;
import android.os.Build;
import android.text.TextUtils;
import com.mogo.cloud.passport.MoGoAiCloudClientConfig;
import com.mogo.commons.debug.DebugConfig;
import com.mogo.commons.utils.MogoAnalyticUtils;
import com.mogo.eagle.core.data.app.AppConfigInfo;
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.util.DateTimeUtils;
import com.mogo.eagle.core.utilcode.util.UiThreadHandler;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import java.util.HashMap;
/**
* OCH sweeper埋点工具
*
* Created on 2022/3/24
*/
public class SweeperAnalyticsManager {
private static final class SingletonHolder {
private static final SweeperAnalyticsManager INSTANCE = new SweeperAnalyticsManager();
}
public static SweeperAnalyticsManager getInstance() {
return SweeperAnalyticsManager.SingletonHolder.INSTANCE;
}
private String mStartAutopilotKey;
private HashMap<String, Object> mStartAutopilotParams = new HashMap<>();
private Runnable startAutopilotRunnable = () -> {
// 15s内未开启上报失败埋点
triggerStartAutopilotFailureEvent("", "15s后app等待超时");
};
public void triggerStartAutopilotFailureEventByAdas(String failCode, String failMsg){
removeWaitingCallback();
triggerStartAutopilotFailureEvent(failCode, failMsg);
}
private void triggerStartAutopilotFailureEvent(String failCode, String failMsg){
if (mStartAutopilotParams.isEmpty()) return;
CallerLogger.e( M_SWEEPER + "triggerStartAutopilotFailureEvent", failMsg );
if (CallerAutoPilotStatusListenerManager.INSTANCE.getState() !=
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING){
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_CODE, failCode);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_MSG, failMsg);
}
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_RESULT
, CallerAutoPilotStatusListenerManager.INSTANCE.getState() ==
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING);
MogoAnalyticUtils.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
clearStartAutopilotParams();//清空参数数据,防止误传
}
private void removeWaitingCallback() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (startAutopilotRunnable != null) {
UiThreadHandler.removeCallbacks(startAutopilotRunnable);
}
}
}
public void clearStartAutopilotFailureMSG(){
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_CODE, "");
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_FAILURE_MSG, "");
}
private void clearStartAutopilotParams(){
mStartAutopilotParams.clear();
}
/**
* 触发'开启自动驾驶'埋点流程
* 开启自动驾驶15s内成功则发送成功埋点否则发送失败埋点
* @param restart false点击'滑动出发'启动)/true接管后点击'自动驾驶'按钮启动)
* @param send 是否直接发送埋点15s内开启成功则直接发送成功埋点
*/
public void triggerStartAutopilotEvent(
boolean restart, boolean send, String startName, String endName, int lineId) {
mStartAutopilotKey = restart ?
SweeperConst.EVENT_KEY_RESTART_AUTOPILOT : SweeperConst.EVENT_KEY_START_SERVICE;
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
String plateNum = AppConfigInfo.INSTANCE.getPlateNumber();
String dateTime = DateTimeUtils.getTimeText(
System.currentTimeMillis(), DateTimeUtils.yyyy_MM_dd_HH_mm_ss);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_SN, sn);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_TIME, dateTime);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_NAME, startName);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_END_NAME, endName);
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_LINE_ID, lineId);
if (send) {
if (mStartAutopilotParams.isEmpty()) return;
// 开启成功,上报埋点
clearStartAutopilotFailureMSG();
removeWaitingCallback();
mStartAutopilotParams.put(SweeperConst.EVENT_PARAM_START_RESULT, true);
MogoAnalyticUtils.INSTANCE.track(mStartAutopilotKey, mStartAutopilotParams);
clearStartAutopilotParams();//清空参数数据,防止误传
} else {
UiThreadHandler.postDelayed(startAutopilotRunnable, SweeperConst.LOOP_PERIOD_15S);
}
}
/**
* 触发"无法开启自驾已知异常"埋点
* @param startName
* @param endName
* @param lineId
*/
public void triggerUnableStartAPReasonEvent(String startName, String endName, int lineId,
String reason) {
String sn = MoGoAiCloudClientConfig.getInstance().getSn();
String plateNum = AppConfigInfo.INSTANCE.getPlateNumber();
String dateTime = DateTimeUtils.getTimeText(
System.currentTimeMillis(), DateTimeUtils.yyyy_MM_dd_HH_mm_ss);
HashMap<String, Object> params = new HashMap<>();
params.put(SweeperConst.EVENT_PARAM_SN, sn);
params.put(SweeperConst.EVENT_PARAM_PLATE_NUM, TextUtils.isEmpty(plateNum) ? "" : plateNum);
params.put(SweeperConst.EVENT_PARAM_ENV_ONLINE,
DebugConfig.getNetMode() == DebugConfig.NET_MODE_RELEASE ? true : false);
params.put(SweeperConst.EVENT_PARAM_TIME, dateTime);
params.put(SweeperConst.EVENT_PARAM_START_NAME, startName);
params.put(SweeperConst.EVENT_PARAM_END_NAME, endName);
params.put(SweeperConst.EVENT_PARAM_LINE_ID, lineId);
params.put(SweeperConst.EVENT_PARAM_UNABLE_START_REASON, reason);
MogoAnalyticUtils.INSTANCE.track(SweeperConst.EVENT_KEY_AP_UNABLE_START_REASON, params);
}
}

View File

@@ -0,0 +1,170 @@
package com.mogo.och.sweeper.operate.util;
import com.mogo.eagle.core.data.autopilot.AutopilotControlParameters;
import com.mogo.eagle.core.function.call.autopilot.CallerAutoPilotControlManager;
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger;
import com.mogo.eagle.core.utilcode.util.GsonUtils;
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskDetailBean;
import com.mogo.och.sweeper.common.constant.SweeperConst;
import java.util.concurrent.TimeUnit;
import androidx.annotation.Nullable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import mogo_msg.MogoReportMsg;
import static com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant.M_SWEEPER;
/**
* Bus轨迹管理给MEC下发用于轨迹下载的信息
* Created on 2022/6/23
*/
public class SweeperTrajectoryManager {
private static final String TAG = SweeperTrajectoryManager.class.getSimpleName();
private static final class SingletonHolder {
private static final SweeperTrajectoryManager INSTANCE = new SweeperTrajectoryManager();
}
public static SweeperTrajectoryManager getInstance() {
return SweeperTrajectoryManager.SingletonHolder.INSTANCE;
}
private AutopilotControlParameters.AutoPilotLine mAutoPilotLine = null;
private Disposable mSendReqDisposable = null;
public SweeperTrajectoryManager() {
mAutoPilotLine = new AutopilotControlParameters.AutoPilotLine(-1, "",
"", "", "", "", 0, "",
"", "", "", "", 0);
}
/**
* 同步Bus路线信息
*/
public void syncTrajectoryInfo(SweeperSubTaskDetailBean sweeperSubTaskDetailBean) {
if (sweeperSubTaskDetailBean != null) {
CallerLogger.d(M_SWEEPER + TAG, "syncTrajectoryInfo() start.");
startTrajReqLoop(sweeperSubTaskDetailBean);
} else {
// 无路线信息or当前未在始发站
CallerLogger.d(M_SWEEPER + TAG, "syncTrajectoryInfo() stop.");
stopTrajReqLoop();
}
}
/**
* 接口MEC反馈的常规信息MAP v2.5.0新增轨迹相关信息)
*
* @param guardianInfo
*/
public void onAutopilotGuardian(@Nullable MogoReportMsg.MogoReportMessage guardianInfo) {
if (guardianInfo == null || !guardianInfo.hasCode()) return;
if ("ISYS_INIT_TRAJECTORY_START".equals(guardianInfo.getCode())) {
// 1. 轨迹管理_轨迹开始下载本地已有对应轨迹也触发
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotGuardian() 轨迹开始下载");
// ToastUtils.showShort("轨迹开始下载");
stopTrajReqLoop();
} else if ("ISYS_INIT_TRAJECTORY_SUCCESS".equals(guardianInfo.getCode())) {
// 2. 轨迹管理_轨迹下载成功本地已有对应轨迹也触发
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotGuardian() 轨迹下载成功");
// ToastUtils.showShort("轨迹下载成功");
stopTrajReqLoop();
} else if ("ISYS_INIT_TRAJECTORY_FAILURE".equals(guardianInfo.getCode())) {
// 3. 轨迹管理_轨迹下载失败本地无对应轨迹
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotGuardian() " +
"轨迹下载失败,本地无对应轨迹");
// ToastUtils.showShort("轨迹下载失败,本地无对应轨迹");
} else if ("ISYS_INIT_TRAJECTORY_WARNING".equals(guardianInfo.getCode())) {
// 4. 轨迹管理_轨迹下载失败本地有对应轨迹认为成功
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotGuardian() " +
"轨迹下载失败,本地有对应轨迹,认为成功");
// ToastUtils.showShort("轨迹下载失败,本地有对应轨迹,认为成功");
} else if ("ISYS_INIT_TRAJECTORY_TIMEOUT".equals(guardianInfo.getCode())) {
// 5. 轨迹管理_轨迹下载超时
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotGuardian() 轨迹下载超时");
// ToastUtils.showShort("轨迹下载超时");
} else if ("ISSM_FUNC_AUTO_PILOT_READY".equals(guardianInfo.getCode())) {
// 收到ssm的自动驾驶变为ready再次下发轨迹下载.解决域控重启或者102域控启动太早107节点初始化未完成导致的轨迹未进行下载。
CallerLogger.d(M_SWEEPER + TAG, "onAutopilotGuardian() ssm ready再次发起下载");
// TODO: 2023/7/25
}
}
private void setupAutoPilotLine(SweeperSubTaskDetailBean subTaskDetail) {
if (subTaskDetail == null) {
CallerLogger.e(M_SWEEPER + TAG, "setupAutoPilotLine(): routesResult is null.");
return;
} else {
mAutoPilotLine = new AutopilotControlParameters.AutoPilotLine(
subTaskDetail.getLineId(), subTaskDetail.getLineName(),
subTaskDetail.getCsvFileUrl(), subTaskDetail.getCsvFileMd5(),
subTaskDetail.getTxtFileUrl(), subTaskDetail.getTxtFileMd5(),
subTaskDetail.getPublishTime(), subTaskDetail.getCarModel(),
subTaskDetail.getCsvFileUrlDPQP(), subTaskDetail.getCsvFileMd5DPQP(),
subTaskDetail.getTxtFileUrlDPQP(), subTaskDetail.getTxtFileMd5DPQP(),
subTaskDetail.getPublishTimeDPQP());
}
}
private void clearAutoPilotLine() {
if (mAutoPilotLine == null) return;
mAutoPilotLine.setLineId(-1);
mAutoPilotLine.setLineName("");
mAutoPilotLine.setTrajUrl("");
mAutoPilotLine.setTrajMd5("");
mAutoPilotLine.setStopUrl("");
mAutoPilotLine.setStopMd5("");
mAutoPilotLine.setTimestamp(0);
mAutoPilotLine.setVehicleModel("");
mAutoPilotLine.setTrajUrl_dpqp("");
mAutoPilotLine.setTrajMd5_dpqp("");
mAutoPilotLine.setStopUrl_dpqp("");
mAutoPilotLine.setStopMd5_dpqp("");
mAutoPilotLine.setTimestamp_dpqp(0);
}
private void startTrajReqLoop(SweeperSubTaskDetailBean sweeperSubTaskDetailBean) {
if (mSendReqDisposable != null && !mSendReqDisposable.isDisposed()) {
return;
}
CallerLogger.d(M_SWEEPER + TAG, "startTrajReqLoop()");
setupAutoPilotLine(sweeperSubTaskDetailBean);
mSendReqDisposable = Observable.interval(SweeperConst.LOOP_DELAY,
SweeperConst.LOOP_PERIOD_10S, TimeUnit.MILLISECONDS)
.map((aLong -> aLong + 1))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aLong -> {
if (aLong > SweeperConst.LOOP_SEND_TRAJ_TIMES) {
stopTrajReqLoop();
return;
}
CallerLogger.d(M_SWEEPER + TAG, "loop sendTrajectoryReq: " + aLong);
sendTrajectoryReq();
});
}
public void stopTrajReqLoop() {
if (mSendReqDisposable != null) {
CallerLogger.d(M_SWEEPER + TAG, "stopTrajReqLoop()");
mSendReqDisposable.dispose();
mSendReqDisposable = null;
clearAutoPilotLine();
}
}
private void sendTrajectoryReq() {
if (mAutoPilotLine == null) {
CallerLogger.e(M_SWEEPER + TAG, "sendTrajectoryReq(): mAutoPilotLine is null!!!");
return;
}
CallerAutoPilotControlManager.INSTANCE.sendTrajectoryDownloadReq(mAutoPilotLine);
CallerLogger.d(M_SWEEPER + TAG, "sendTrajectoryReq(): "
+ GsonUtils.toJson(mAutoPilotLine));
}
}

View File

@@ -0,0 +1,270 @@
package com.mogo.och.sweeper.operate.view;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import androidx.annotation.Nullable;
import com.mogo.och.sweeper.R;
/**
* 滑块滑动面板
*
* @author tongchenfei
*/
public class SlidePanelView extends View {
private static final String TAG = "SlidePanelView";
public SlidePanelView(Context context) {
this(context, null);
}
public SlidePanelView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SlidePanelView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.SlidePanelView);
textSize = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_textSize, textSize);
BLOCK_START_X = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_BLOCK_START_X, BLOCK_START_X);
BLOCK_START_Y = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_BLOCK_START_Y, BLOCK_START_Y);
NORMAL_TEXT_MARGIN_LEFT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_NORMAL_TEXT_MARGIN_LEFT, NORMAL_TEXT_MARGIN_LEFT);
NORMAL_TEXT_MARGIN_RIGHT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_NORMAL_TEXT_MARGIN_RIGHT, NORMAL_TEXT_MARGIN_RIGHT);
SHORT_TEXT_MARGIN_LEFT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_SHORT_TEXT_MARGIN_LEFT, SHORT_TEXT_MARGIN_LEFT);
SHORT_TEXT_MARGIN_RIGHT = (int) mTypedArray.getDimension(R.styleable.SlidePanelView_SHORT_TEXT_MARGIN_RIGHT, SHORT_TEXT_MARGIN_RIGHT);
init();
}
private final Paint bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint blockPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Paint textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private static int textSize = 40;
private static int BLOCK_START_X = 15;
private static int BLOCK_START_Y = 15;
private static int NORMAL_TEXT_MARGIN_LEFT = 40;
private static int NORMAL_TEXT_MARGIN_RIGHT = 60;
private static int SHORT_TEXT_MARGIN_LEFT = 60;
private static int SHORT_TEXT_MARGIN_RIGHT = 70;
private int textMarginLeft = NORMAL_TEXT_MARGIN_LEFT;
private int textMarginRight = NORMAL_TEXT_MARGIN_RIGHT;
private OnSlidePanelMoveToEndListener moveToEndListener;
private int blockWidth = 0;
private int blockOffset = 0;
private float lastX;
private boolean isToEnd = false;
private static final String STRING_SLIDE_TO_RIGHT = "向右滑动";
private RectF bgRectF;
private Bitmap bmBlock;
private final Matrix gradientMatrix = new Matrix();
private float matrixTranslate;
private final Rect textRect = new Rect();
private LinearGradient textGradient;
private ObjectAnimator matrixAnim;
private String blockText = STRING_SLIDE_TO_RIGHT;
private Paint.FontMetrics blockTextMetrics = new Paint.FontMetrics();
private static final int GRADIENT_OFFSET = 200;
public void setOnSlidePanelMoveToEndListener(OnSlidePanelMoveToEndListener moveToEndListener) {
this.moveToEndListener = moveToEndListener;
}
private void setBlockOffset(int blockOffset) {
this.blockOffset = blockOffset;
invalidate();
}
private void setMatrixTranslate(float matrixTranslate) {
this.matrixTranslate = matrixTranslate;
invalidate();
}
public void setText(String text) {
this.blockText = text;
requestLayout();
invalidate();
}
private void init() {
bgRectF = new RectF(0, 0, 0, 0);
bgPaint.setColor(Color.parseColor("#CC0F1325"));
bgPaint.setStyle(Paint.Style.FILL);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextSize(textSize);
textPaint.setTextAlign(Paint.Align.LEFT);
textGradient = new LinearGradient(-GRADIENT_OFFSET, 0, 0, 0, new int[]{0x33ffffff, 0xffffffff, 0x33ffffff}, null, Shader.TileMode.CLAMP);
textGradient.setLocalMatrix(gradientMatrix);
textPaint.setShader(textGradient);
textPaint.getFontMetrics(blockTextMetrics);
bmBlock = BitmapFactory.decodeResource(getResources(), R.drawable.sweeper_base_slide_block);
blockWidth = bmBlock.getWidth();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize;
int heightSize;
if (blockText.length() < 5) {
textMarginLeft = SHORT_TEXT_MARGIN_LEFT;
textMarginRight = SHORT_TEXT_MARGIN_RIGHT;
} else {
textMarginLeft = NORMAL_TEXT_MARGIN_LEFT;
textMarginRight = NORMAL_TEXT_MARGIN_RIGHT;
}
if (widthMode == MeasureSpec.AT_MOST) {
// 宽度根据图片大小,字符串长度,各种间隔确定
// 高度根据图片大小和上下间隔确定
textPaint.getTextBounds(blockText, 0, blockText.length(), textRect);
widthSize = BLOCK_START_X * 2 + bmBlock.getWidth() + textMarginLeft + textMarginRight + textRect.width();
heightSize = BLOCK_START_Y * 2 + bmBlock.getHeight();
widthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private float textOffset = 0;
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bgRectF != null){
bgRectF.left = 0;
bgRectF.top = 0;
bgRectF.right = w;
bgRectF.bottom = h;
}
if (matrixAnim != null) {
matrixAnim.cancel();
}
textOffset = (getHeight() - blockTextMetrics.ascent - blockTextMetrics.descent) / 2;
matrixAnim = ObjectAnimator.ofFloat(this, "matrixTranslate", 0, w + GRADIENT_OFFSET).setDuration(2000);
matrixAnim.setRepeatCount(ValueAnimator.INFINITE);
matrixAnim.start();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (matrixAnim != null) {
matrixAnim.start();
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (matrixAnim != null) {
matrixAnim.cancel();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (x > BLOCK_START_X + blockOffset && x < blockWidth + BLOCK_START_X + blockOffset && y > BLOCK_START_Y && y < getHeight() - BLOCK_START_Y) {
isToEnd = false;
lastX = x;
}
break;
case MotionEvent.ACTION_MOVE:
if (lastX != 0) {
blockOffset = (int) (x - lastX);
if (blockOffset < 0) {
blockOffset = 0;
}
if (blockOffset + BLOCK_START_X + blockWidth > getWidth()) {
// 超出右边界
blockOffset = getWidth() - BLOCK_START_X - blockWidth;
if (!isToEnd) {
isToEnd = true;
if (moveToEndListener != null) {
moveToEndListener.moveToEnd();
}
startBlockBackAnim();
}
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
// 执行滑块回归动画
if (!isToEnd) {
startBlockBackAnim();
}
break;
default:
break;
}
return true;
}
private void startBlockBackAnim() {
ObjectAnimator blockBackanimator = ObjectAnimator.ofInt(this, "blockOffset", blockOffset, 0);
blockBackanimator.setInterpolator(new DecelerateInterpolator());
blockBackanimator.setDuration(1000 * blockOffset / getWidth());
blockBackanimator.start();
lastX = 0;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 画背景
canvas.drawRoundRect(bgRectF, (float) getHeight() / 2, (float) getHeight() / 2, bgPaint);
// 画文字
gradientMatrix.setTranslate(matrixTranslate, 0);
textGradient.setLocalMatrix(gradientMatrix);
canvas.save();
canvas.drawText(blockText, blockWidth + BLOCK_START_X + textMarginLeft, textOffset, textPaint);
canvas.restore();
// 画滑块
canvas.drawBitmap(bmBlock, BLOCK_START_X + blockOffset, BLOCK_START_Y, blockPaint);
}
public interface OnSlidePanelMoveToEndListener {
/**
* 滑块滑到了末尾
*/
void moveToEnd();
}
}

View File

@@ -0,0 +1,57 @@
package com.mogo.och.sweeper.operate.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.operate.bean.SubInfo
import kotlinx.android.synthetic.main.sweeper_subtask_view.view.*
/**
* 子任务View
*/
class SubTaskView : ConstraintLayout {
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_subtask_view, this)
}
/**
* 设置子任务信息
*/
fun setData(taskInfo: SubInfo, isSelect: Boolean = false, isLastTask: Boolean = false) {
tvSubTaskName.text = taskInfo.taskName
var mileage: String = if (taskInfo.mileage < 1000) {
"${taskInfo.mileage}"
} else {
"${taskInfo.mileage / 1000F}千米"
}
val time=taskInfo.timeSpent.toLong()
var timeSpent=if(DateTimeUtil.secondsToHourStr(time)!=""){
"${DateTimeUtil.secondsToHourStr(time)}小时${DateTimeUtil.secondsToMinuteStr(time)}分钟"
}else{
"${DateTimeUtil.secondsToMinuteStr(time)}分钟"
}
tvSubTaskDesc.text = "全程约${mileage},预计耗时$timeSpent"
tvSubTaskName.setTextColor(if (isSelect) Color.parseColor("#3BD2FF") else Color.parseColor("#FFFFFF"))
if (isSelect) {
ivSubTaskSelect.visibility = View.VISIBLE
ivSubTask.visibility = View.GONE
} else {
ivSubTaskSelect.visibility = View.GONE
ivSubTask.visibility = View.VISIBLE
}
ivRightDownArrow.visibility = if (isLastTask) View.GONE else View.VISIBLE
}
}

View File

@@ -0,0 +1,233 @@
package com.mogo.och.sweeper.operate.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.mogo.eagle.core.function.api.autopilot.IMoGoAutopilotStatusListener
import com.mogo.eagle.core.utilcode.util.ClickUtils
import com.mogo.eagle.core.utilcode.util.ToastUtils
import com.mogo.och.common.module.utils.DateTimeUtil
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.operate.bean.SubInfo
import com.mogo.och.sweeper.operate.bean.SweeperSubTaskBean
import com.mogo.och.sweeper.operate.constant.SubTaskTypeEnum
import com.mogo.och.sweeper.operate.presenter.SweeperPresenter
import com.mogo.och.sweeper.operate.ui.popwindow.MenuPopWindow
import kotlinx.android.synthetic.main.sweeper_current_task_info.view.*
/**
* 清扫车当前任务信息展示
*/
class SweeperCurrentTaskInfoView : ConstraintLayout {
private val TAG: String = "SweeperCurrentTaskInfoView"
//当前任务操作菜单
private var mTaskMenuPopWindow: MenuPopWindow? = null
private var presenter: SweeperPresenter? = null
private var mCurrentPosition = 0
private var listTask: List<SubInfo>? = null
private var mSubTaskType: SubTaskTypeEnum = SubTaskTypeEnum.AUTOPILOT_SUBTYPE
private var mSubTaskStatus: Int = 1
//当前自动驾驶状态
private val mCurrentAutopilotStatus = 0
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_current_task_info, this)
setEnableClickBtn(false)
tvStartAuto.setOnClickListener {
if (ClickUtils.isFastClick()){//防止重复点击
if (mSubTaskStatus==1){
ToastUtils.showLong("任务未开始无轨迹,无法启动自驾")
return@setOnClickListener
}
if (mSubTaskType== SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE){
ToastUtils.showLong("人工子任务无轨迹,无法启动自驾")
return@setOnClickListener
}
if (mCurrentAutopilotStatus!=IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING){
presenter?.startAutopilot()
}
}
}
}
/**
* 设置当前任务数据
*/
fun setData(
workModePanelView: SweeperWorkModeView,
subTaskBean: SweeperSubTaskBean?,
currentPosition: Int,
menuItemClickListener: MenuPopWindow.OnMenuItemOnClickListener,
presenter: SweeperPresenter?
) {
this.presenter = presenter
this.mCurrentPosition = currentPosition
mTaskMenuPopWindow = MenuPopWindow(context, menuItemClickListener)
subTaskBean?.apply {
this@SweeperCurrentTaskInfoView.listTask = subList
tvTaskName.text = taskName
val calendar = DateTimeUtil.formatLongToCalendar(taskStartTime)
tvTaskTime.text = DateTimeUtil.formatCalendarToString(calendar, DateTimeUtil.HH_mm)
}
listTask?.let {
mSubTaskStatus=it[currentPosition].taskStatus
setEnableClickBtn(mSubTaskStatus==2)
}
setCurrentData(currentPosition)
//任务操作菜单打开关闭处理
ivMore.setOnClickListener {
if (mTaskMenuPopWindow?.isShowing != true) {
mTaskMenuPopWindow?.showAsDropDown(
workModePanelView,
resources.getDimension(R.dimen.dp_580).toInt(),
resources.getDimension(R.dimen.dp_36).toInt()
)
} else {
mTaskMenuPopWindow?.dismiss()
}
}
}
/**
* 设置准备就绪按钮时都可以点击
*/
fun setEnableClickBtn(isWorking: Boolean) {
readyTaskBtn.isClickable=!isWorking
presenter?.setWorking(isWorking)
if (isWorking) {
tvTaskState.text = "正在作业"
readyTaskBtn.text="任务中"
readyTaskBtn.setTextColor(Color.parseColor("#FFFFFFFF"))
tvTaskState.setBackgroundResource(R.drawable.bg_shape_task_state_working)
readyTaskBtn.setBackgroundResource(R.drawable.sweeper_task_working)
} else {
tvTaskState.text = "暂未准备"
readyTaskBtn.text="开始任务"
readyTaskBtn.setTextColor(Color.parseColor("#66FFFFFF"))
tvTaskState.setBackgroundResource(R.drawable.bg_shape_task_state_not_ready)
readyTaskBtn.setBackgroundResource(R.drawable.sweeper_task_not_working)
}
}
/**
* 设置准备就绪按钮时都可以点击
*/
fun setStartAutoBtn(autopilotState: Int) {
when (autopilotState) {
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_DISABLE -> {
tvStartAuto.text="自动驾驶"
tvStartAuto.setTextColor(Color.parseColor("#66FFFFFF"))
tvStartAuto.setBackgroundResource(R.drawable.sweeper_start_auto_not_running)
}
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_ENABLE -> {
tvStartAuto.text="自动驾驶"
tvStartAuto.setTextColor(Color.parseColor("#66FFFFFF"))
tvStartAuto.setBackgroundResource(R.drawable.sweeper_start_auto_not_running)
}
IMoGoAutopilotStatusListener.STATUS_AUTOPILOT_RUNNING -> {
tvStartAuto.text="自动驾驶"
tvStartAuto.setTextColor(Color.parseColor("#FFFFFFFF"))
tvStartAuto.setBackgroundResource(R.drawable.sweeper_start_auto_running)
}
IMoGoAutopilotStatusListener.STATUS_PARALLEL_DRIVING -> {
tvStartAuto.text="平行驾驶"
tvStartAuto.setTextColor(Color.parseColor("#FFFFFFFF"))
tvStartAuto.setBackgroundResource(R.drawable.sweeper_start_auto_running)
}
}
}
/**
* 开始子任务
*/
private fun startSubTask() {
listTask?.let {
presenter?.startTask(
isFirstSubTask(),
isLastSubTask(),
it[mCurrentPosition].taskId,
if (it[mCurrentPosition].taskType == SubTaskTypeEnum.AUTOPILOT_SUBTYPE.code) SubTaskTypeEnum.AUTOPILOT_SUBTYPE else SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE,
mSubTaskStatus
)
}
}
/**
* 是否第一个子任务
*/
private fun isFirstSubTask(): Boolean {
return mCurrentPosition == 0
}
/**
* 是否第一个子任务
*/
private fun isLastSubTask(): Boolean {
return mCurrentPosition == (listTask?.size?.minus(1))
}
/**
* 设置任务状态
*/
fun setTaskStatus(status: Int) {
this.mSubTaskStatus = status
}
/**
* 填充数据
*/
fun setCurrentData(mCurrentPosition: Int) {
listTask?.let {
mSubTaskType =
if (it[mCurrentPosition].taskType == SubTaskTypeEnum.AUTOPILOT_SUBTYPE.code) SubTaskTypeEnum.AUTOPILOT_SUBTYPE else SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE
if (it.size == 1) {
preSubTask.setData(it[mCurrentPosition], isSelect = true, isLastTask = true)
currentSubTask.visibility = View.INVISIBLE
lastSubTask.visibility = View.INVISIBLE
} else if (it.size == 2) {
if (mCurrentPosition == 0) {
preSubTask.setData(it[mCurrentPosition], isSelect = true)
currentSubTask.setData(it[1], isLastTask = true)
} else {
preSubTask.setData(it[mCurrentPosition - 1])
currentSubTask.setData(it[mCurrentPosition], isSelect = true, isLastTask = true)
}
preSubTask.visibility=View.VISIBLE
currentSubTask.visibility=View.VISIBLE
lastSubTask.visibility=View.GONE
} else {
preSubTask.visibility=View.VISIBLE
currentSubTask.visibility=View.VISIBLE
lastSubTask.visibility=View.VISIBLE
//当前正在执行的任务是第一个子任务
if (mCurrentPosition == 0) {
preSubTask.setData(it[mCurrentPosition], isSelect = true)
currentSubTask.setData(it[1])
lastSubTask.setData(it[2], isLastTask = true)
//当前正在执行的任务是最后一个子任务
} else if (mCurrentPosition == it.size - 1) {
preSubTask.setData(it[mCurrentPosition - 2])
currentSubTask.setData(it[mCurrentPosition - 1])
lastSubTask.setData(it[mCurrentPosition], isSelect = true, isLastTask = true)
} else {
preSubTask.setData(it[mCurrentPosition - 1])
currentSubTask.setData(it[mCurrentPosition], isSelect = true)
lastSubTask.setData(it[mCurrentPosition + 1], isLastTask = true)
}
}
}
}
}

View File

@@ -0,0 +1,170 @@
package com.mogo.och.sweeper.operate.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import chassis.Chassis.GearPosition
import chassis.Chassis.LightSwitch
import chassis.ChassisStatesOuterClass.BMSSystemStates
import chassis.ChassisStatesOuterClass.SweeperFuTianTaskSystemStates
import com.elegant.utils.UiThreadHandler
import com.mogo.eagle.core.function.api.autopilot.IMoGoBatteryManagementSystemListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisGearStateListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoChassisLamplightListener
import com.mogo.eagle.core.function.api.autopilot.IMoGoSweeperFutianCleanSystemListener
import com.mogo.eagle.core.function.call.autopilot.*
import com.mogo.eagle.core.function.hmi.ui.widget.TapPositionView
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.ThreadUtils
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.view.TurnSignalView
import planning.RoboSweeperTaskIndexOuterClass
import kotlin.math.roundToInt
/**
* 车辆基本信息:方向盘下方的档位 转向灯 限速 速度 电量 水量
*/
class SweeperTrafficDataView : ConstraintLayout,
IMoGoBatteryManagementSystemListener,
IMoGoChassisLamplightListener,
IMoGoChassisGearStateListener,
IMoGoSweeperFutianCleanSystemListener
{
private var tapPositionView //方向盘下方的档位
: TapPositionView? = null
private var speedImage //速度图标
: ImageView? = null
private var speedTextView //速度值
: TextView? = null
private var sweeperTurnSignal //转向灯
: TurnSignalView? = null
private var tvBattery //电量百分比展示
: TextView? = null
private var ivBgWaterWarning //水位预警背景图
: ImageView? = null
private var ivWater //水位图标
: ImageView? = null
private val TAG = "SweeperTrafficDataView"
// 底盘数据回调时间间隔
private val VEHICLE_STATE_INTERVAL_MILLIS = 500L
// 当前时间戳
private var mCurrentTimeWaterMillis: Long = 0
private var mCurrentTimeBatteryMillis: Long = 0
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_traffic_data, this)
tapPositionView = findViewById(R.id.sweeperTrafficPosition)
speedImage = findViewById(R.id.sweeperSpeedImage)
speedTextView = findViewById(R.id.sweeperSpeedText)
sweeperTurnSignal = findViewById(R.id.sweeperTurnSignal)
tvBattery = findViewById(R.id.tvBattery)
ivBgWaterWarning = findViewById(R.id.sweeperIvBgWaterWarning)
ivWater = findViewById(R.id.sweeperIvWater)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
//电量
CallerBatteryManagementSystemListenerManager.addListener(TAG, this)
//转向灯
CallerChassisLamplightListenerManager.addListener(TAG, this)
//档位
CallerChassisGearStateListenerManager.addListener(TAG, this)
//清扫车相关数据接口
CallerSweeperFutianCleanSystemListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerBatteryManagementSystemListenerManager.removeListener(TAG )
CallerChassisLamplightListenerManager.removeListener(TAG)
CallerChassisGearStateListenerManager.removeListener(TAG)
CallerSweeperFutianCleanSystemListenerManager.removeListener(TAG)
}
override fun onSweeperFutianTaskIndexData(roboSweeperTaskIndex: RoboSweeperTaskIndexOuterClass.RoboSweeperTaskIndex) {}
override fun onSweeperFutianCleanSystemState(cleanSystemState: SweeperFuTianTaskSystemStates) {
val current = System.currentTimeMillis()
if (current - mCurrentTimeWaterMillis <=VEHICLE_STATE_INTERVAL_MILLIS) {
return
}
mCurrentTimeWaterMillis = current
d(SceneConstant.M_SWEEPER + TAG, "水位:${cleanSystemState.secuCleanWaterTankLow}")
UiThreadHandler.post {
if (cleanSystemState.secuCleanWaterTankLow) { //清水箱水位低不能清洗作业报警信号
ivBgWaterWarning?.visibility = VISIBLE
ivWater?.isSelected = true
} else {
ivBgWaterWarning?.visibility = GONE
ivWater?.isSelected = false
}
}
}
@SuppressLint("SetTextI18n")
override fun onBatteryManagementSystemStates(states: BMSSystemStates) {
val current = System.currentTimeMillis()
if (current - mCurrentTimeBatteryMillis <=VEHICLE_STATE_INTERVAL_MILLIS) {
return
}
mCurrentTimeBatteryMillis = current
d(SceneConstant.M_SWEEPER + TAG, "电量:${states.bmsSoc}")
UiThreadHandler.post{
tvBattery?.text = "${states.bmsSoc.roundToInt()}%"
}
}
/**
* 车辆转向灯
* @param directionLight
*/
override fun onAutopilotLightSwitchData(lightSwitch: LightSwitch?) {
//转向灯状态 0是正常 1是左转 2是右转
if (lightSwitch != null) {
sweeperTurnSignal?.setTurnLight(lightSwitch)
}
}
/**
* 刹车灯
* @param brakeLight
*/
override fun onAutopilotBrakeLightData(brakeLight: Boolean) {
d(TAG, "刹车灯:$brakeLight")
}
/**
* 方向盘下方的档位
* @param gear
*/
override fun onAutopilotGearData(gear: GearPosition) {
d(TAG, "司机屏档位$gear")
ThreadUtils.runOnUiThread {
tapPositionView?.updateWithGear(gear)
}
}
/**
* 速度设置
*/
fun updateSpeedWithValue(newSpeed: Int) {
speedTextView?.text = newSpeed.toString()
speedImage?.setBackgroundResource(if (newSpeed > 60) R.drawable.sweeper_traffic_data_speed_warning else R.drawable.sweeper_bg_traffic_data_speed)
}
}

View File

@@ -0,0 +1,144 @@
package com.mogo.och.sweeper.operate.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
import com.mogo.eagle.core.data.enums.DataSourceType
import com.mogo.eagle.core.data.enums.TrafficLightEnum
import com.mogo.eagle.core.function.api.datacenter.union.IMoGoTrafficLightListener
import com.mogo.eagle.core.function.call.v2x.CallerTrafficLightListenerManager
import com.mogo.eagle.core.utilcode.util.UiThreadHandler
import com.mogo.och.sweeper.R
import kotlinx.android.synthetic.main.sweeper_traffic_light_view.view.*
/**
* 清扫车红绿灯view-
*
* Created on 2022/3/29
*/
class SweeperTrafficLightView @JvmOverloads constructor(
context: Context?,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr), IMoGoTrafficLightListener {
companion object {
private const val TAG = "SweeperTrafficLightView"
}
private var mCurrentLightId = TrafficLightEnum.BLACK
init {
init(context)
}
private fun init(context: Context?) {
LayoutInflater.from(context).inflate(R.layout.sweeper_traffic_light_view, this, true)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
CallerTrafficLightListenerManager.addListener(TAG, this)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
CallerTrafficLightListenerManager.removeListener(TAG)
}
/**
* 展示红绿灯预警
*
* @param checkLightId 0-都是默认1-红2-黄3-绿
* @param lightSource 1:云端下发2:自车感知
*/
override fun showTrafficLight(checkLightId: TrafficLightEnum, lightSource: DataSourceType) {
mCurrentLightId = checkLightId
updateTrafficLightIcon(checkLightId)
}
/**
* 关闭红绿灯预警展示,并重制灯态
*/
override fun disableTrafficLight() {
UiThreadHandler.post {
mCurrentLightId = TrafficLightEnum.BLACK
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_gay_nor)
sweeper_traffic_light_time_tv.text = ""
this@SweeperTrafficLightView.visibility = VISIBLE
}
}
/**
* @param redNum 红灯倒计时
* @param yellowNum 黄灯倒计时
* @param greenNum 绿灯倒计时
*/
override fun changeCountdownTrafficLightNum(redNum: Int, yellowNum: Int, greenNum: Int) {
when (mCurrentLightId) {
TrafficLightEnum.RED -> changeCountdownRed(redNum)
TrafficLightEnum.YELLOW -> changeCountdownYellow(yellowNum)
TrafficLightEnum.GREEN -> changeCountdownGreen(greenNum)
else -> UiThreadHandler.post { sweeper_traffic_light_time_tv.text = "" }
}
}
override fun changeCountdownRed(redNum: Int) {
UiThreadHandler.post {
if (redNum > 0) {
sweeper_traffic_light_time_tv.text = redNum.toString()
} else {
sweeper_traffic_light_time_tv.text = ""
}
}
}
override fun changeCountdownGreen(greenNum: Int) {
UiThreadHandler.post {
if (greenNum > 0) {
sweeper_traffic_light_time_tv.text = greenNum.toString()
} else {
sweeper_traffic_light_time_tv.text = ""
}
}
}
override fun changeCountdownYellow(yellowNum: Int) {
UiThreadHandler.post {
if (yellowNum > 0) {
sweeper_traffic_light_time_tv.text = yellowNum.toString()
} else {
sweeper_traffic_light_time_tv.text = ""
}
}
}
/**
* 更新红绿灯icon
*
* @param lightId 0-都是默认1-红2-黄3-绿
*/
private fun updateTrafficLightIcon(lightId: TrafficLightEnum) {
UiThreadHandler.post {
when (lightId) {
TrafficLightEnum.RED -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_red_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
TrafficLightEnum.YELLOW -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_lightyellow_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
TrafficLightEnum.GREEN -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_green_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
else -> {
sweeper_traffic_light_iv.setImageResource(R.drawable.sweeper_light_gay_nor)
this@SweeperTrafficLightView.visibility = VISIBLE
}
}
}
}
}

View File

@@ -0,0 +1,182 @@
package com.mogo.och.sweeper.operate.view
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import chassis.ChassisStatesOuterClass.SweeperFuTianTaskSystemStates
import com.elegant.utils.UiThreadHandler
import com.mogo.eagle.core.utilcode.mogo.logger.CallerLogger.d
import com.mogo.eagle.core.utilcode.mogo.logger.scene.SceneConstant
import com.mogo.och.sweeper.R
import com.mogo.och.sweeper.common.callback.ICleaningModeStateCallback
import com.mogo.och.sweeper.common.constant.OperateStateEnum
import com.mogo.och.sweeper.operate.constant.SubTaskTypeEnum
import com.mogo.och.sweeper.operate.ui.popwindow.SweeperOperatePanelPopWindow
import com.mogo.och.sweeper.common.util.SweeperFutianCmdUtil
import com.mogo.och.sweeper.operate.view.SweeperTrafficDataView
import kotlinx.android.synthetic.main.sweeper_work_mode.view.*
/**
* 清扫车模式信息展示
*/
class SweeperWorkModeView : ConstraintLayout,
ICleaningModeStateCallback {
private var isSelectPureSweepMode: Boolean = false
private val TAG = "SweeperWorkModeView"
//清扫模式选择面板
private var mOperatePanelPopWindow: SweeperOperatePanelPopWindow? = null
private var operateState: OperateStateEnum = OperateStateEnum.SYNCING_STATUS
constructor(context: Context) : super(context) {}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
initView(context)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
private fun initView(context: Context) {
LayoutInflater.from(context).inflate(R.layout.sweeper_work_mode, this)
mOperatePanelPopWindow = SweeperOperatePanelPopWindow(context)
setShowOrHideCleanSystemState(OperateStateEnum.SYNCING_STATUS,null)
}
/**
* 设置view
*/
fun setTrafficDataView(trafficDataView: SweeperTrafficDataView){
//清扫模式选择面板打开关闭处理
ivOpenOperatePanel.setOnClickListener {
if (mOperatePanelPopWindow?.isShowing != true) {
mOperatePanelPopWindow?.showAsDropDown(
trafficDataView,
resources.getDimension(R.dimen.dp_600).toInt(),
0
)
UiThreadHandler.postDelayed({
d(SceneConstant.M_SWEEPER + TAG, "operateState"+operateState.code)
mOperatePanelPopWindow?.showSyncing(operateState)
},300)
} else {
mOperatePanelPopWindow?.dismiss()
}
}
}
/**
* 设置清扫模式面板
*/
fun setSweeperFutianCleanSystemState(taskType: SubTaskTypeEnum, cleanSystemState: SweeperFuTianTaskSystemStates?) {
// TODO:传递清扫车底盘数据给上装面板
//mOperatePanelPopWindow?.setCleanSystemState(cleanSystemState, this@SweeperWorkModeView)
if (this.operateState.code== OperateStateEnum.STARTING_STATUS.code){
return
}
//如果状态是同步中,在底盘首次回调时把状态强制修改成成功
if (this.operateState.code== OperateStateEnum.SYNCING_STATUS.code){
this.operateState= OperateStateEnum.SUCCESS_STATUS
d(SceneConstant.M_SWEEPER + TAG, "SystemState operateState"+operateState.code)
}
//清扫车暂未选择清扫模式或者任务类型是人工驾驶子任务,则暂无清扫模式
if (SweeperFutianCmdUtil.checkIfCleanMode(cleanSystemState) || taskType.code == SubTaskTypeEnum.MANUAL_DRIVING_SUBTYPE.code) {
setShowOrHideCleanSystemState(OperateStateEnum.NO_STATUS, cleanSystemState)
} else {
setShowOrHideCleanSystemState(OperateStateEnum.SUCCESS_STATUS, cleanSystemState)
}
}
/**
* 设置清扫模式状态显示和隐藏
*/
private fun setShowOrHideCleanSystemState(operateState: OperateStateEnum, cleanSystemState: SweeperFuTianTaskSystemStates?) {
when (operateState.code) {
OperateStateEnum.SYNCING_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "状态同步中,请稍后"
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
OperateStateEnum.FAIL_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "上装启动失败"
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
OperateStateEnum.STARTING_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "上装启动中..."
mOperatePanelPopWindow?.setIsOutsideTouchable(false)
}
OperateStateEnum.NO_STATUS.code -> {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "暂无"
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
else -> {
groupWorkModelPanel?.visibility = View.VISIBLE
tvNoDataDesc?.visibility = View.GONE
setCleanModeData(cleanSystemState)
mOperatePanelPopWindow?.setIsOutsideTouchable(true)
}
}
}
override fun cleaningModeState(operateState: OperateStateEnum, cleanSystemState: SweeperFuTianTaskSystemStates?, isSelectPureSweepMode: Boolean) {
this.isSelectPureSweepMode = isSelectPureSweepMode
this.operateState=operateState
setShowOrHideCleanSystemState(operateState, cleanSystemState)
}
/**
* 设置清扫模式数据
*/
private fun setCleanModeData(cleanSystemState: SweeperFuTianTaskSystemStates?) {
if (isSelectPureSweepMode) {
tvCleaningMode?.text = "纯扫模式"
groupWorkModelPanel?.visibility = View.VISIBLE
tvNoDataDesc?.visibility = View.GONE
} else {
if (SweeperFutianCmdUtil.checkIfCleanMode(cleanSystemState)) {
groupWorkModelPanel?.visibility = View.GONE
tvNoDataDesc?.visibility = View.VISIBLE
tvNoDataDesc?.text = "暂无"
return
} else {
groupWorkModelPanel?.visibility = View.VISIBLE
tvNoDataDesc?.visibility = View.GONE
// 作业模式
if (cleanSystemState?.secuModWashSweepSts == true) tvCleaningMode?.text = "洗扫模式"
if (cleanSystemState?.secuModWashSts == true) tvCleaningMode?.text = "纯洗模式"
if (cleanSystemState?.secuWorkTonSts == true) tvCleaningMode?.text = "纯吸模式"
if (SweeperFutianCmdUtil.checkIfCleanModePureSweep(cleanSystemState)) tvCleaningMode?.text = "纯扫模式"
}
}
//清扫方向
if (SweeperFutianCmdUtil.checkIfCleanDirection(cleanSystemState)) {
tvCleaningDirection.visibility = View.GONE
} else {
tvCleaningDirection.visibility = View.VISIBLE
// 左侧
if (cleanSystemState?.secuWorkLeftSts == true) tvCleaningDirection?.text = "左侧"
// 右侧
if (cleanSystemState?.secuWorkRightSts == true) tvCleaningDirection?.text = "右侧"
// 两侧
if (cleanSystemState?.secuWorkOnBothsidesSts == true) tvCleaningDirection?.text = "两侧"
}
if (SweeperFutianCmdUtil.checkIfCleanIntensity(cleanSystemState)) {
tvCleaningIntensity.visibility = View.GONE
} else {
tvCleaningIntensity.visibility = View.VISIBLE
// 作业强度
if (cleanSystemState?.secuWorkStandSts == true) tvCleaningIntensity?.text = "标准"
if (cleanSystemState?.secuWorkStrongSts == true) tvCleaningIntensity?.text = "强力"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Some files were not shown because too many files have changed in this diff Show More