降落修改
This commit is contained in:
parent
66db69f894
commit
143ef4ffd5
|
|
@ -171,6 +171,11 @@ open class MainActivity : BaseActivity() {
|
|||
fun updateVtxHeartbeat() {
|
||||
lastVtxFrameTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setStartArucoType(type: Int) {
|
||||
instance?.startArucoType = type
|
||||
}
|
||||
}
|
||||
|
||||
protected var primaryFpvWidget: FPVWidget? = null
|
||||
|
|
@ -236,12 +241,13 @@ open class MainActivity : BaseActivity() {
|
|||
private var cameraManager: ICameraStreamManager =
|
||||
MediaDataCenter.getInstance().cameraStreamManager
|
||||
|
||||
private var startArucoType = 0 //1执行机库二维码识别 2执行备降点二维码识别
|
||||
private var startArucoType = 0 //1执行机库二维码识别 2执行备降点二维码识别 3扫到任何码触发刹停
|
||||
private var dictionary: Dictionary? = null
|
||||
override fun useEventBus(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
|
|
@ -500,13 +506,13 @@ open class MainActivity : BaseActivity() {
|
|||
secondaryFPVWidget?.post {
|
||||
secondaryFPVWidget?.performClick()
|
||||
}
|
||||
LogUtil.log(TAG, "智能刷新:有云台,模拟点击 FPV Widget")
|
||||
//LogUtil.log(TAG, "智能刷新:有云台,模拟点击 FPV Widget")
|
||||
} else {
|
||||
// 没有云台:直接刷新当前流
|
||||
primaryFpvWidget?.post {
|
||||
refreshVideoStream()
|
||||
}
|
||||
LogUtil.log(TAG, "智能刷新:无云台,直接刷新主视频流")
|
||||
//LogUtil.log(TAG, "智能刷新:无云台,直接刷新主视频流")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -793,8 +799,9 @@ open class MainActivity : BaseActivity() {
|
|||
enableStream()
|
||||
initFpvStream()
|
||||
startVtxHeartbeat()
|
||||
SpeakerManager.getInstance().initMegaphoneInfo()
|
||||
SpeakerManager.getInstance().addMegaphoneInfoListener()
|
||||
GimbalManager.getInstance().setmode()
|
||||
PayloadWidgetManager.getInstance().initPayloadInfo()
|
||||
|
||||
LogUtil.log(TAG, "自定义推流方式:" + PreferenceUtils.getInstance().customStreamType)
|
||||
|
||||
|
|
@ -834,9 +841,10 @@ open class MainActivity : BaseActivity() {
|
|||
enableStream()
|
||||
initMixedStream()
|
||||
startVtxHeartbeat()
|
||||
SpeakerManager.getInstance().initMegaphoneInfo()
|
||||
SpeakerManager.getInstance().addMegaphoneInfoListener()
|
||||
GeoidManager.getInstance().init(this)
|
||||
GimbalManager.getInstance().setmode()
|
||||
PayloadWidgetManager.getInstance().initPayloadInfo()
|
||||
|
||||
LogUtil.log(TAG, "自定义推流方式:" + PreferenceUtils.getInstance().customStreamType)
|
||||
|
||||
|
|
@ -876,9 +884,11 @@ open class MainActivity : BaseActivity() {
|
|||
enableStream()
|
||||
initCameraStream()
|
||||
startVtxHeartbeat()
|
||||
SpeakerManager.getInstance().initMegaphoneInfo()
|
||||
|
||||
SpeakerManager.getInstance().addMegaphoneInfoListener()
|
||||
GeoidManager.getInstance().init(this)
|
||||
GimbalManager.getInstance().setmode()
|
||||
PayloadWidgetManager.getInstance().initPayloadInfo()
|
||||
|
||||
LogUtil.log(TAG, "自定义推流方式:" + PreferenceUtils.getInstance().customStreamType)
|
||||
|
||||
|
|
@ -905,6 +915,8 @@ open class MainActivity : BaseActivity() {
|
|||
//val mixedLanding = MixedVisionLanding.getInstance()
|
||||
|
||||
// 为 PORT_1(云台相机)添加帧监听器
|
||||
|
||||
|
||||
cameraManager.addFrameListener(
|
||||
ComponentIndexType.PORT_1,
|
||||
ICameraStreamManager.FrameFormat.YUV420_888
|
||||
|
|
@ -933,6 +945,13 @@ open class MainActivity : BaseActivity() {
|
|||
frameData,
|
||||
dictionary
|
||||
)
|
||||
} else if (startArucoType == 3) {
|
||||
Aprongim.getInstance()?.detectForceTriggerTags(
|
||||
height,
|
||||
width,
|
||||
frameData,
|
||||
dictionary
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
Synchronizedstatus.setIsruningframe(false)
|
||||
|
|
@ -972,6 +991,13 @@ open class MainActivity : BaseActivity() {
|
|||
frameData,
|
||||
dictionary
|
||||
)
|
||||
} else if (startArucoType == 3) {
|
||||
Aprondown.getInstance()?.detectForceTriggerTags(
|
||||
height,
|
||||
width,
|
||||
frameData,
|
||||
dictionary
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
Synchronizedstatus.setIsruningframe(false)
|
||||
|
|
@ -1013,6 +1039,13 @@ open class MainActivity : BaseActivity() {
|
|||
frameData,
|
||||
dictionary
|
||||
)
|
||||
} else if (startArucoType == 3) {
|
||||
ApronArucoDetect.getInstance()?.detectForceTriggerTags(
|
||||
height,
|
||||
width,
|
||||
frameData,
|
||||
dictionary
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
Synchronizedstatus.setIsruningframe(false)
|
||||
|
|
@ -1023,6 +1056,9 @@ open class MainActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@SuppressLint("SuspiciousIndentation")
|
||||
private fun initCameraStream() {
|
||||
cameraManager.addFrameListener(
|
||||
|
|
@ -1053,6 +1089,13 @@ open class MainActivity : BaseActivity() {
|
|||
frameData,
|
||||
dictionary
|
||||
)
|
||||
} else if (startArucoType == 3) {
|
||||
ApronArucoDetectPort.getInstance()?.detectForceTriggerTags(
|
||||
height,
|
||||
width,
|
||||
frameData,
|
||||
dictionary
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
Synchronizedstatus.setIsruningframe(false)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import com.google.gson.Gson;
|
|||
import org.eclipse.paho.android.service.MqttAndroidClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -201,6 +202,37 @@ public abstract class BaseManager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送飞往备降点event事件
|
||||
*/
|
||||
public void sendFlyToAlternateLandPointEvent() {
|
||||
try {
|
||||
if (MqttManager.getInstance().mqttAndroidClient.isConnected()) {
|
||||
MessageEvent messageEvent = new MessageEvent();
|
||||
messageEvent.setBid(UUID.randomUUID().toString());
|
||||
messageEvent.setTid(UUID.randomUUID().toString());
|
||||
messageEvent.setTimestamp(System.currentTimeMillis());
|
||||
messageEvent.setMethod(Constant.FLY_TO_ALTERNATE_LAND_POINT);
|
||||
MessageEvent.Data data = new MessageEvent.Data();
|
||||
if (TextUtils.isEmpty(PreferenceUtils.getInstance().getFlightId())) {
|
||||
data.setFlight_id("null");
|
||||
} else {
|
||||
data.setFlight_id(PreferenceUtils.getInstance().getFlightId());
|
||||
}
|
||||
messageEvent.setData(data);
|
||||
MqttMessage mqttMessage = new MqttMessage(new Gson().toJson(messageEvent).getBytes("UTF-8"));
|
||||
mqttMessage.setQos(0);
|
||||
MqttManager.getInstance().mqttAndroidClient.publish(AMSConfig.UP_UAV_EVENT, mqttMessage);
|
||||
LogUtil.log(TAG, "发送fly_to_alternate_land_point event成功:" + new Gson().toJson(messageEvent));
|
||||
} else {
|
||||
LogUtil.log(TAG, "发送fly_to_alternate_land_point event失败:mqtt 未连接");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
LogUtil.log(TAG, "发送fly_to_alternate_land_point event异常:" + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送媒体文件上传事件
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.aros.apron.callback;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.aros.apron.constant.AMSConfig;
|
||||
import com.aros.apron.tools.LogUtil;
|
||||
|
|
@ -17,6 +18,11 @@ public class MqttActionCallBack implements IMqttActionListener {
|
|||
private final String TAG = "MqttActionCallBack";
|
||||
private MqttAndroidClient mqttAndroidClient;
|
||||
private MqttConnectOptions options;
|
||||
private int reconnectAttempt = 0;
|
||||
private static final int INITIAL_RETRY_INTERVAL = 3000; // 初始重试间隔 3 秒
|
||||
private static final int MAX_RETRY_INTERVAL = 30000; // 最大重试间隔 30 秒
|
||||
private static final Handler retryHandler = new Handler(Looper.getMainLooper());
|
||||
private Runnable retryRunnable;
|
||||
|
||||
public MqttActionCallBack(MqttAndroidClient mqttAndroidClient, MqttConnectOptions options) {
|
||||
this.mqttAndroidClient = mqttAndroidClient;
|
||||
|
|
@ -26,10 +32,14 @@ public class MqttActionCallBack implements IMqttActionListener {
|
|||
@Override
|
||||
public void onSuccess(IMqttToken asyncActionToken) {
|
||||
LogUtil.log(TAG, "MQtt连接成功:-------");
|
||||
// 异步显示Toast,避免Binder IPC阻塞当前主线程消息导致ANR
|
||||
new Handler().post(() -> ToastUtil.showToast("MQtt连接成功"));
|
||||
reconnectAttempt = 0;
|
||||
if (retryRunnable != null) {
|
||||
retryHandler.removeCallbacks(retryRunnable);
|
||||
retryRunnable = null;
|
||||
}
|
||||
new Handler(Looper.getMainLooper()).post(() -> ToastUtil.showToast("MQtt连接成功"));
|
||||
try {
|
||||
mqttAndroidClient.subscribe(AMSConfig.DOWN_UAV_SERVICES, 1);//订阅主题:注册
|
||||
mqttAndroidClient.subscribe(AMSConfig.DOWN_UAV_SERVICES, 1);
|
||||
} catch (MqttException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
@ -39,19 +49,32 @@ public class MqttActionCallBack implements IMqttActionListener {
|
|||
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
|
||||
LogUtil.log(TAG, "MQtt连接失败:" + exception.toString());
|
||||
try {
|
||||
if (!mqttAndroidClient.isConnected()){
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
if (!mqttAndroidClient.isConnected()) {
|
||||
reconnectAttempt++;
|
||||
|
||||
// 指数退避:3s → 6s → 12s → 24s → 30s(封顶)
|
||||
long retryDelay = Math.min(INITIAL_RETRY_INTERVAL * (1L << Math.min(reconnectAttempt, 4)), MAX_RETRY_INTERVAL);
|
||||
|
||||
if (reconnectAttempt % 10 == 0 || reconnectAttempt <= 3) {
|
||||
LogUtil.log(TAG, "第 " + reconnectAttempt + " 次重连,间隔 " + retryDelay + "ms");
|
||||
}
|
||||
|
||||
if (retryRunnable != null) {
|
||||
retryHandler.removeCallbacks(retryRunnable);
|
||||
}
|
||||
|
||||
final long finalDelay = retryDelay;
|
||||
retryRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
mqttAndroidClient.connect(options, null, MqttActionCallBack.this); // 再次尝试连接
|
||||
mqttAndroidClient.connect(options, null, MqttActionCallBack.this);
|
||||
} catch (MqttException e) {
|
||||
LogUtil.log(TAG,"mqtt重连异常:"+e.toString());
|
||||
e.printStackTrace();
|
||||
LogUtil.log(TAG, "mqtt重连异常:" + e.toString());
|
||||
}
|
||||
|
||||
}
|
||||
},1500);
|
||||
};
|
||||
retryHandler.postDelayed(retryRunnable, finalDelay);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
|||
|
|
@ -208,8 +208,10 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.FLIGHTTASK_RECOVERY:
|
||||
LogUtil.log(TAG, "收到:航线继续" + jsonString);
|
||||
|
||||
clearVirtualStickHeartbeat();
|
||||
MissionV3Manager.getInstance().resumeMission(message);
|
||||
|
||||
break;
|
||||
case Constant.RETURN_HOME:
|
||||
LogUtil.log(TAG, "收到:返航" + jsonString);
|
||||
|
|
@ -428,12 +430,16 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.IR_METERING_MODE_SET:
|
||||
LogUtil.log(TAG, "收到:负载控制—红外测温模式设置" + jsonString);
|
||||
CameraManager.getInstance().setThermalTemperatureMeasureMode(message);
|
||||
break;
|
||||
case Constant.IR_METERING_POINT_SET:
|
||||
LogUtil.log(TAG, "收到:负载控制—红外测温点设置" + jsonString);
|
||||
|
||||
CameraManager.getInstance().setThermalSpotMetersurePoint(message);
|
||||
break;
|
||||
case Constant.IR_METERING_AREA_SET:
|
||||
LogUtil.log(TAG, "收到:负载控制—红外测温区域设置" + jsonString);
|
||||
CameraManager.getInstance().setThermalRegionMetersureArea(message);
|
||||
break;
|
||||
case Constant.POI_MODE_ENTER:
|
||||
LogUtil.log(TAG, "收到:飞行控制—进入 POI 环绕模式" + jsonString);
|
||||
|
|
@ -456,8 +462,12 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.SPEAKER_AUDIO_PLAY_START:
|
||||
LogUtil.log(TAG, "收到:喊话器-开始播放音频" + jsonString);
|
||||
Movement.getInstance().setMd5(message.getData().getFile().getMd5());
|
||||
Movement.getInstance().setPlay_file_md5(message.getData().getFile().getName());
|
||||
Movement.getInstance().setStep_key("download");
|
||||
|
||||
SpeakerProgressReporter.getInstance().startAudioReport(2);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
//SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerAudioPlayStart(message);
|
||||
|
|
@ -470,8 +480,12 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.SPEAKER_TTS_PLAY_START:
|
||||
LogUtil.log(TAG, "收到:喊话器-开始播放TTS文本" + jsonString);
|
||||
Movement.getInstance().setMd5(message.getData().getTts().getMd5());
|
||||
Movement.getInstance().setPlay_file_md5(message.getData().getTts().getMd5());
|
||||
Movement.getInstance().setPlay_file_name(message.getData().getTts().getText());
|
||||
|
||||
SpeakerProgressReporter.getInstance().startTTSReport(2);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
//SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerTTSPlayStart(message, 0);
|
||||
|
|
@ -484,7 +498,7 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.SPEAKER_REPLAY:
|
||||
LogUtil.log(TAG, "收到:喊话器-重新播放" + jsonString);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
// SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerReply(message);
|
||||
|
|
@ -497,7 +511,7 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.SPEAKER_PLAY_STOP:
|
||||
LogUtil.log(TAG, "收到:喊话器-停止播放" + jsonString);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
// SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerStop(message);
|
||||
|
|
@ -511,7 +525,7 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.SPEAKER_PLAY_MODE_SET:
|
||||
LogUtil.log(TAG, "收到:喊话器-设置播放模式" + jsonString);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
// SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerPlayModeSet(message);
|
||||
|
|
@ -524,7 +538,7 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.SPEAKER_PLAY_VOLUME_SET:
|
||||
LogUtil.log(TAG, "收到:喊话器-设置音量" + jsonString);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
//SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerPlayVolumeSet(message);
|
||||
|
|
@ -537,7 +551,7 @@ public class MqttCallBack extends BaseManager implements MqttCallbackExtended {
|
|||
break;
|
||||
case Constant.DRC_SPEAKER_TTS_SET:
|
||||
LogUtil.log(TAG, "收到:喊话器-TTS喊话设置" + jsonString);
|
||||
SpeakerManager.getInstance().setindex();
|
||||
//SpeakerManager.getInstance().setindex();
|
||||
synchronized (lock) {
|
||||
if (!Synchronizedstatus.isSpeakrunning()) {
|
||||
SpeakerManager.getInstance().speakerTTSPlayStart(message, 1);
|
||||
|
|
|
|||
|
|
@ -322,6 +322,11 @@ public class Constant {
|
|||
*/
|
||||
public static final String DRC_SPEAKER_TTS_SET="drc_speaker_tts_set";
|
||||
|
||||
/**
|
||||
* 飞向备降点
|
||||
*/
|
||||
public static final String FLY_TO_ALTERNATE_LAND_POINT="fly_to_alternate_land_point";
|
||||
|
||||
/**
|
||||
* 推流切换fpv
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.aros.apron.entity;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.util.List;
|
||||
|
||||
public class MessageDown {
|
||||
|
|
@ -111,6 +112,8 @@ public class MessageDown {
|
|||
|
||||
private float width;
|
||||
|
||||
|
||||
|
||||
private int psdk_index;
|
||||
private int play_mode;
|
||||
private int play_volume;
|
||||
|
|
@ -132,6 +135,10 @@ public class MessageDown {
|
|||
// saved: 是否保存 0-否 1-是 (bool)
|
||||
private boolean saved;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public int getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
|
@ -628,6 +635,33 @@ public class MessageDown {
|
|||
public static class File {
|
||||
private String fingerprint;
|
||||
private String url;
|
||||
private String md5;
|
||||
private String name;
|
||||
private String format;
|
||||
|
||||
public String getMd5() {
|
||||
return md5;
|
||||
}
|
||||
|
||||
public void setMd5(String md5) {
|
||||
this.md5 = md5;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String getFingerprint() {
|
||||
return fingerprint;
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ package com.aros.apron.entity;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public class Movement {
|
||||
|
||||
|
||||
|
||||
private static class MovementHolder {
|
||||
private static final Movement INSTANCE = new Movement();
|
||||
}
|
||||
|
|
@ -57,7 +55,7 @@ public class Movement {
|
|||
private int landingPower;//降落电量所需百分比
|
||||
private int lowBatteryRTHState;//智能低电量返航状态 0未触发智能低电量返航 1触发智能低电量返航,飞行器正在倒计时 2执行智能低电量返航 3智能低电量返航被取消
|
||||
private String missionName;//当前正在执行的航线名
|
||||
private int currentWaypointIndex=0;//当前航点下标
|
||||
private int currentWaypointIndex = 0;//当前航点下标
|
||||
|
||||
private boolean planeWing;//飞机是否在飞
|
||||
private boolean isMotorsOn;//电机是否起转
|
||||
|
|
@ -111,12 +109,12 @@ public class Movement {
|
|||
private boolean resumeMissionStatus;//前端判断是否显示/隐藏继续航线
|
||||
private boolean isVirtualStickQuitMission;//用户手动后退出航线
|
||||
|
||||
private boolean virtualcontrollget=false;//是否已经获得过控制权
|
||||
private boolean virtualcontrollget = false;//是否已经获得过控制权
|
||||
|
||||
private int GPSSatelliteCount; //卫星个数
|
||||
private boolean Alternate=false;
|
||||
private boolean Alternate = false;
|
||||
|
||||
private boolean camera3stick=false;//3号相机模式释放控制权
|
||||
private boolean camera3stick = false;//3号相机模式释放控制权
|
||||
|
||||
|
||||
//适配上云格式参数,拿到后再进行组装
|
||||
|
|
@ -197,12 +195,12 @@ public class Movement {
|
|||
private int wind_direction;
|
||||
private int wind_speed;
|
||||
|
||||
private int camera_mode=0;
|
||||
private int camera_mode = 0;
|
||||
private int ir_metering_mode;
|
||||
private int temperature;
|
||||
private double x;
|
||||
private double y;
|
||||
private int ir_zoom_factor=2;
|
||||
private int ir_zoom_factor = 2;
|
||||
private double bottom;
|
||||
private double left;
|
||||
private double right;
|
||||
|
|
@ -212,7 +210,7 @@ public class Movement {
|
|||
private int record_time;
|
||||
private int recording_state;
|
||||
private int remain_photo_num;
|
||||
private int remain_record_duration=3000;
|
||||
private int remain_record_duration = 3000;
|
||||
private boolean screen_split_enable;
|
||||
private int wide_exposure_mode;
|
||||
private int wide_exposure_value;
|
||||
|
|
@ -222,7 +220,7 @@ public class Movement {
|
|||
private int zoom_calibrate_nearest_focus_value;
|
||||
private int zoom_exposure_mode;
|
||||
private int zoom_exposure_value;
|
||||
private double zoom_factor=2;
|
||||
private double zoom_factor = 2;
|
||||
private int zoom_focus_mode;
|
||||
private int zoom_focus_state;
|
||||
private int zoom_focus_value;
|
||||
|
|
@ -278,9 +276,9 @@ public class Movement {
|
|||
private String gnd_quality_4g;
|
||||
private String sdr_freq_band;
|
||||
private String freq_band_4g;
|
||||
private boolean istakeoffex=false;
|
||||
private boolean istakeoffex = false;
|
||||
|
||||
private boolean waylineinterpter=true;
|
||||
private boolean waylineinterpter = true;
|
||||
|
||||
|
||||
public boolean isWaylineinterpter() {
|
||||
|
|
@ -318,12 +316,12 @@ public class Movement {
|
|||
}
|
||||
|
||||
//航线上报状态
|
||||
private int result=0;
|
||||
private int result = 0;
|
||||
|
||||
|
||||
// ========== 一键起飞相关字段 ==========
|
||||
private boolean takeofftopointmission;
|
||||
private volatile String takeoff_status="task_ready"; // task_ready/wayline_progress/wayline_ok/wayline_failed/wayline_cancel/task_finish
|
||||
private volatile String takeoff_status = "task_ready"; // task_ready/wayline_progress/wayline_ok/wayline_failed/wayline_cancel/task_finish
|
||||
private int takeoff_result; // 0成功,非0错误
|
||||
private float takeoff_remaining_distance; // 剩余距离(米)
|
||||
private float takeoff_remaining_time; // 剩余时间(秒)
|
||||
|
|
@ -334,7 +332,7 @@ public class Movement {
|
|||
private double takeofftargetlongitude;
|
||||
private double takeofftargetheight;
|
||||
|
||||
private int mission_type;
|
||||
private int mission_type;
|
||||
|
||||
public int getMission_type() {
|
||||
return mission_type;
|
||||
|
|
@ -355,6 +353,163 @@ public class Movement {
|
|||
private String step_key;
|
||||
private String md5;
|
||||
|
||||
//==================================psdk上报
|
||||
|
||||
private String psdk_index;
|
||||
private String psdk_lib_version;
|
||||
private String psdk_name="Speaker";
|
||||
private String psdk_sn="8V2CP1X00A1ZPA";
|
||||
private String psdk_type="5";
|
||||
private String psdk_version="01.00.01.11";
|
||||
private String play_file_md5;
|
||||
private String play_file_name;
|
||||
private String play_mode;
|
||||
private String play_volume;
|
||||
private String system_state;
|
||||
private String work_mode;
|
||||
private int[] values;
|
||||
|
||||
private String tts_language;
|
||||
private String tts_speed;
|
||||
private String tts_type;
|
||||
private String tts_volume;
|
||||
|
||||
|
||||
public String getTts_language() {
|
||||
return tts_language;
|
||||
}
|
||||
|
||||
public void setTts_language(String tts_language) {
|
||||
this.tts_language = tts_language;
|
||||
}
|
||||
|
||||
public String getTts_speed() {
|
||||
return tts_speed;
|
||||
}
|
||||
|
||||
public void setTts_speed(String tts_speed) {
|
||||
this.tts_speed = tts_speed;
|
||||
}
|
||||
|
||||
public String getTts_type() {
|
||||
return tts_type;
|
||||
}
|
||||
|
||||
public void setTts_type(String tts_type) {
|
||||
this.tts_type = tts_type;
|
||||
}
|
||||
|
||||
public String getTts_volume() {
|
||||
return tts_volume;
|
||||
}
|
||||
|
||||
public void setTts_volume(String tts_volume) {
|
||||
this.tts_volume = tts_volume;
|
||||
}
|
||||
|
||||
public String getPsdk_index() {
|
||||
return psdk_index;
|
||||
}
|
||||
|
||||
public void setPsdk_index(String psdk_index) {
|
||||
this.psdk_index = psdk_index;
|
||||
}
|
||||
|
||||
public String getPsdk_lib_version() {
|
||||
return psdk_lib_version;
|
||||
}
|
||||
|
||||
public void setPsdk_lib_version(String psdk_lib_version) {
|
||||
this.psdk_lib_version = psdk_lib_version;
|
||||
}
|
||||
|
||||
public String getPsdk_name() {
|
||||
return psdk_name;
|
||||
}
|
||||
|
||||
public void setPsdk_name(String psdk_name) {
|
||||
this.psdk_name = psdk_name;
|
||||
}
|
||||
|
||||
public String getPsdk_sn() {
|
||||
return psdk_sn;
|
||||
}
|
||||
|
||||
public void setPsdk_sn(String psdk_sn) {
|
||||
this.psdk_sn = psdk_sn;
|
||||
}
|
||||
|
||||
public String getPsdk_type() {
|
||||
return psdk_type;
|
||||
}
|
||||
|
||||
public void setPsdk_type(String psdk_type) {
|
||||
this.psdk_type = psdk_type;
|
||||
}
|
||||
|
||||
public String getPsdk_version() {
|
||||
return psdk_version;
|
||||
}
|
||||
|
||||
public void setPsdk_version(String psdk_version) {
|
||||
this.psdk_version = psdk_version;
|
||||
}
|
||||
|
||||
public String getPlay_file_md5() {
|
||||
return play_file_md5;
|
||||
}
|
||||
|
||||
public void setPlay_file_md5(String play_file_md5) {
|
||||
this.play_file_md5 = play_file_md5;
|
||||
}
|
||||
|
||||
public String getPlay_file_name() {
|
||||
return play_file_name;
|
||||
}
|
||||
|
||||
public void setPlay_file_name(String play_file_name) {
|
||||
this.play_file_name = play_file_name;
|
||||
}
|
||||
|
||||
public String getPlay_mode() {
|
||||
return play_mode;
|
||||
}
|
||||
|
||||
public void setPlay_mode(String play_mode) {
|
||||
this.play_mode = play_mode;
|
||||
}
|
||||
|
||||
public String getPlay_volume() {
|
||||
return play_volume;
|
||||
}
|
||||
|
||||
public void setPlay_volume(String play_volume) {
|
||||
this.play_volume = play_volume;
|
||||
}
|
||||
|
||||
public String getSystem_state() {
|
||||
return system_state;
|
||||
}
|
||||
|
||||
public void setSystem_state(String system_state) {
|
||||
this.system_state = system_state;
|
||||
}
|
||||
|
||||
public String getWork_mode() {
|
||||
return work_mode;
|
||||
}
|
||||
|
||||
public void setWork_mode(String work_mode) {
|
||||
this.work_mode = work_mode;
|
||||
}
|
||||
|
||||
public int[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(int[] values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public boolean isCamera3stick() {
|
||||
return camera3stick;
|
||||
|
|
@ -413,7 +568,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public String getTTS_status() {
|
||||
return TTS_status;
|
||||
}
|
||||
|
|
@ -423,7 +577,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public String getStep_key() {
|
||||
return step_key;
|
||||
}
|
||||
|
|
@ -433,7 +586,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public int getGPSSatelliteCount() {
|
||||
return GPSSatelliteCount;
|
||||
}
|
||||
|
|
@ -445,6 +597,7 @@ public class Movement {
|
|||
public double getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public void setSpeed(double speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
|
@ -508,7 +661,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public float getTakeoff_remaining_distance() {
|
||||
return takeoff_remaining_distance;
|
||||
}
|
||||
|
|
@ -550,9 +702,9 @@ public class Movement {
|
|||
}
|
||||
|
||||
//用来判断是什么起飞type
|
||||
private volatile int flightmode=0; //0代表没有操作 1代表航线飞行 2一键代表指令飞行 3flyto(一键和flyto)
|
||||
private volatile int flightmode = 0; //0代表没有操作 1代表航线飞行 2一键代表指令飞行 3flyto(一键和flyto)
|
||||
private boolean takeofftopoint;
|
||||
private boolean opendrc=false; //true 是开启 false 是关闭
|
||||
private boolean opendrc = false; //true 是开启 false 是关闭
|
||||
|
||||
public boolean isTakeofftopoint() {
|
||||
return takeofftopoint;
|
||||
|
|
@ -1743,7 +1895,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public String getFirmware_version() {
|
||||
return firmware_version;
|
||||
}
|
||||
|
|
@ -2182,7 +2333,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public boolean isPlaneWing() {
|
||||
return planeWing;
|
||||
}
|
||||
|
|
@ -2366,7 +2516,6 @@ public class Movement {
|
|||
}
|
||||
|
||||
|
||||
|
||||
public String getFlightPathName() {
|
||||
return flightPathName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -911,6 +911,7 @@ public class Osd {
|
|||
private int camera_mode;
|
||||
private int ir_metering_mode;
|
||||
private IrMeteringPoint ir_metering_point;
|
||||
private IrMeteringArea ir_metering_area;
|
||||
private int ir_zoom_factor;
|
||||
private LiveviewWorldRegion liveview_world_region;
|
||||
private String payload_index;
|
||||
|
|
@ -962,6 +963,14 @@ public class Osd {
|
|||
this.ir_metering_point = ir_metering_point;
|
||||
}
|
||||
|
||||
public IrMeteringArea getIr_metering_area() {
|
||||
return ir_metering_area;
|
||||
}
|
||||
|
||||
public void setIr_metering_area(IrMeteringArea ir_metering_area) {
|
||||
this.ir_metering_area = ir_metering_area;
|
||||
}
|
||||
|
||||
public int getIr_zoom_factor() {
|
||||
return ir_zoom_factor;
|
||||
}
|
||||
|
|
@ -1200,6 +1209,102 @@ public class Osd {
|
|||
}
|
||||
}
|
||||
|
||||
public static class IrMeteringArea {
|
||||
private double x;
|
||||
private double y;
|
||||
private double width;
|
||||
private double height;
|
||||
private double aver_temperature;
|
||||
private IrTemperaturePoint min_temperature_point;
|
||||
private IrTemperaturePoint max_temperature_point;
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(double width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public double getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(double height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public double getAver_temperature() {
|
||||
return aver_temperature;
|
||||
}
|
||||
|
||||
public void setAver_temperature(double aver_temperature) {
|
||||
this.aver_temperature = aver_temperature;
|
||||
}
|
||||
|
||||
public IrTemperaturePoint getMin_temperature_point() {
|
||||
return min_temperature_point;
|
||||
}
|
||||
|
||||
public void setMin_temperature_point(IrTemperaturePoint min_temperature_point) {
|
||||
this.min_temperature_point = min_temperature_point;
|
||||
}
|
||||
|
||||
public IrTemperaturePoint getMax_temperature_point() {
|
||||
return max_temperature_point;
|
||||
}
|
||||
|
||||
public void setMax_temperature_point(IrTemperaturePoint max_temperature_point) {
|
||||
this.max_temperature_point = max_temperature_point;
|
||||
}
|
||||
}
|
||||
|
||||
public static class IrTemperaturePoint {
|
||||
private double x;
|
||||
private double y;
|
||||
private double temperature;
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public void setX(double x) {
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(double y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public double getTemperature() {
|
||||
return temperature;
|
||||
}
|
||||
|
||||
public void setTemperature(double temperature) {
|
||||
this.temperature = temperature;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LiveviewWorldRegion {
|
||||
private double bottom;
|
||||
private double left;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.aros.apron.base.BaseManager;
|
||||
import com.aros.apron.constant.Constant;
|
||||
import com.aros.apron.entity.FlightMission;
|
||||
import com.aros.apron.entity.MessageDown;
|
||||
import com.aros.apron.entity.MissionPoint;
|
||||
|
|
@ -309,6 +310,13 @@ public class AlternateLandingManager extends BaseManager {
|
|||
PreferenceUtils.getInstance().setNeedTriggerAlterArucoLand(false);
|
||||
PreferenceUtils.getInstance().setNeedTriggerApronArucoLand(false);
|
||||
sendEvent2Server( "开始飞往备降点",1);
|
||||
|
||||
|
||||
sendFlyToAlternateLandPointEvent();
|
||||
|
||||
Movement.getInstance().setTakeoff_result(316052);
|
||||
Movement.getInstance().setResult(316052);
|
||||
|
||||
//设置为未开始识别二维码状态
|
||||
FlightManager.getInstance().setSendDetect(false);
|
||||
EventBus.getDefault().post(FLAG_STOP_ARUCO);
|
||||
|
|
|
|||
|
|
@ -3,15 +3,24 @@ package com.aros.apron.manager;
|
|||
|
||||
import static dji.sdk.keyvalue.key.KeyTools.createKey;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.aros.apron.base.BaseManager;
|
||||
import com.aros.apron.entity.Movement;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.aros.apron.tools.LogUtil;
|
||||
import com.aros.apron.tools.PreferenceUtils;
|
||||
|
||||
import dji.sdk.keyvalue.key.BatteryKey;
|
||||
import dji.sdk.keyvalue.key.FlightControllerKey;
|
||||
import dji.sdk.keyvalue.key.KeyTools;
|
||||
import dji.sdk.keyvalue.value.battery.IndustryBatteryType;
|
||||
import dji.sdk.keyvalue.value.common.EmptyMsg;
|
||||
import dji.sdk.keyvalue.value.flightcontroller.LowBatteryRTHInfo;
|
||||
import dji.v5.common.callback.CommonCallbacks;
|
||||
import dji.v5.manager.KeyManager;
|
||||
|
|
@ -33,10 +42,13 @@ public class BatteryManager extends BaseManager {
|
|||
}
|
||||
|
||||
private boolean sendLowBatteryRTHPosition2Server;
|
||||
//标识是否已触发强制返航,避免重复触发
|
||||
private boolean forcedRTHTriggered;
|
||||
|
||||
private static final long SECONDS_PER_DAY = 24 * 60 * 60; // 86400
|
||||
|
||||
|
||||
|
||||
public void initBatteryInfo() {
|
||||
Boolean isConnect = KeyManager.getInstance().getValue(createKey(FlightControllerKey.KeyConnection));
|
||||
if (isConnect != null && isConnect) {
|
||||
|
|
@ -80,6 +92,7 @@ public class BatteryManager extends BaseManager {
|
|||
if (newValue != null) {
|
||||
Movement.getInstance().setBattery_a_capacity_percent(newValue);
|
||||
Movement.getInstance().setCapacity_percent(newValue);
|
||||
checkForcedBatteryRTH(newValue, Movement.getInstance().getBattery_b_capacity_percent());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -90,6 +103,7 @@ public class BatteryManager extends BaseManager {
|
|||
public void onValueChange(@Nullable Integer oldValue, @Nullable Integer newValue) {
|
||||
if (newValue != null) {
|
||||
Movement.getInstance().setBattery_b_capacity_percent(newValue);
|
||||
checkForcedBatteryRTH(Movement.getInstance().getBattery_a_capacity_percent(), newValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -273,6 +287,64 @@ public class BatteryManager extends BaseManager {
|
|||
|
||||
|
||||
|
||||
/**
|
||||
* 检查双电池平均电量是否低于强制返航阈值,低于则触发返航
|
||||
*/
|
||||
private void checkForcedBatteryRTH(int batteryA, int batteryB) {
|
||||
|
||||
|
||||
if (forcedRTHTriggered) return;
|
||||
|
||||
if (!getGimbalAndCameraEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 过滤空值/无效电量(0或负数不参与计算)
|
||||
if (batteryA <= 0 || batteryB <= 0) return;
|
||||
|
||||
String forcedBatteryStr = PreferenceUtils.getInstance().getForcedBattery();
|
||||
if (TextUtils.isEmpty(forcedBatteryStr)) return;
|
||||
|
||||
int forcedBattery;
|
||||
try {
|
||||
forcedBattery = Integer.parseInt(forcedBatteryStr);
|
||||
} catch (NumberFormatException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
int avgBattery = (batteryA + batteryB) / 2;
|
||||
if (avgBattery >= forcedBattery) return;
|
||||
|
||||
// 仅在飞行中触发
|
||||
if (!Movement.getInstance().isPlaneWing()) return;
|
||||
|
||||
forcedRTHTriggered = true;
|
||||
LogUtil.log(TAG, "平均电量" + avgBattery + "%低于强制返航阈值" + forcedBattery + "%,触发自动返航");
|
||||
sendEvent2Server("触发低电量返航",1);
|
||||
|
||||
Movement.getInstance().setTakeoff_result(321773);
|
||||
Movement.getInstance().setResult(321773);
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
dji.v5.manager.KeyManager.getInstance().performAction(
|
||||
dji.sdk.keyvalue.key.KeyTools.createKey(dji.sdk.keyvalue.key.FlightControllerKey.KeyStartGoHome),
|
||||
new CommonCallbacks.CompletionCallbackWithParam<EmptyMsg>() {
|
||||
@Override
|
||||
public void onSuccess(EmptyMsg emptyMsg) {
|
||||
LogUtil.log(TAG, "强制返航触发成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull dji.v5.common.error.IDJIError error) {
|
||||
LogUtil.log(TAG, "强制返航触发失败: " + error);
|
||||
}
|
||||
}
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
public void releaseBatteryKey() {
|
||||
KeyManager.getInstance().cancelListen(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ import dji.sdk.keyvalue.value.camera.ZoomTargetPointInfo;
|
|||
import dji.sdk.keyvalue.value.common.CameraLensType;
|
||||
import dji.sdk.keyvalue.value.common.ComponentIndexType;
|
||||
import dji.sdk.keyvalue.value.common.DoublePoint2D;
|
||||
import dji.sdk.keyvalue.value.common.DoubleRect;
|
||||
import dji.sdk.keyvalue.value.common.EmptyMsg;
|
||||
import dji.sdk.keyvalue.value.product.ProductType;
|
||||
import dji.v5.common.callback.CommonCallbacks;
|
||||
|
|
@ -313,11 +314,13 @@ public class CameraManager extends BaseManager {
|
|||
@Override
|
||||
public void onValueChange(@Nullable ThermalTemperatureMeasureMode thermalTemperatureMeasureMode, @Nullable ThermalTemperatureMeasureMode t1) {
|
||||
if (t1 != null && thermalTemperatureMeasureMode != null) {
|
||||
Movement.getInstance().setIr_metering_mode(thermalTemperatureMeasureMode.value());
|
||||
if(t1!=ThermalTemperatureMeasureMode.CENTRAL_POINT_METERING&&t1!=ThermalTemperatureMeasureMode.UNKNOWN){
|
||||
Movement.getInstance().setIr_metering_mode(t1.value());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//当前测温的点的位置
|
||||
KeyManager.getInstance().listen(KeyTools.createCameraKey(CameraKey.KeyThermalSpotMetersurePoint,
|
||||
ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL), this, new CommonCallbacks.KeyListener<DoublePoint2D>() {
|
||||
|
|
@ -1791,85 +1794,85 @@ public class CameraManager extends BaseManager {
|
|||
// }
|
||||
// }
|
||||
|
||||
// //设置测温模式
|
||||
// public void setThermalTemperatureMeasureMode(MQMessage message) {
|
||||
// Boolean isConnect =KeyManager.getInstance().getValue(KeyTools.createKey(CameraKey.
|
||||
// KeyConnection,ComponentIndexType.PORT_1));
|
||||
// if (isConnect != null && isConnect && getGimbalAndCameraEnabled()) {
|
||||
// KeyManager.getInstance().setValue(KeyTools.createCameraKey(CameraKey.KeyThermalTemperatureMeasureMode,
|
||||
// ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL),
|
||||
// ThermalTemperatureMeasureMode.find(message.getThermalTemperatureMeasureMode()), new CommonCallbacks.CompletionCallback() {
|
||||
// @Override
|
||||
// public void onSuccess() {
|
||||
// sendMsg2Server(message);
|
||||
// }
|
||||
//设置测温模式
|
||||
public void setThermalTemperatureMeasureMode(MessageDown message) {
|
||||
Boolean isConnect =KeyManager.getInstance().getValue(KeyTools.createKey(CameraKey.
|
||||
KeyConnection,ComponentIndexType.PORT_1));
|
||||
if (isConnect != null && isConnect && getGimbalAndCameraEnabled()) {
|
||||
KeyManager.getInstance().setValue(KeyTools.createCameraKey(CameraKey.KeyThermalTemperatureMeasureMode,
|
||||
ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL),
|
||||
ThermalTemperatureMeasureMode.find(message.getData().getMode()), new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
sendMsg2Server(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
LogUtil.log(TAG,"设置测温模式:"+new Gson().toJson(error));
|
||||
sendFailMsg2Server(message, "设置测温模式:" + getIDJIErrorMsg(error));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LogUtil.log(TAG, "设置测温模式失败:当前状态相机禁止操作");
|
||||
}
|
||||
}
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(@NonNull IDJIError error) {
|
||||
// LogUtil.log(TAG,"设置测温模式:"+new Gson().toJson(error));
|
||||
// sendMsg2Server(message, "设置测温模式:" + getIDJIErrorMsg(error));
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// LogUtil.log(TAG, "设置测温模式失败:当前状态相机禁止操作");
|
||||
// }
|
||||
// }
|
||||
//设置需要测温的点的位置
|
||||
public void setThermalSpotMetersurePoint(MessageDown message) {
|
||||
Boolean isConnect =KeyManager.getInstance().getValue(KeyTools.createKey(CameraKey.
|
||||
KeyConnection,ComponentIndexType.PORT_1));
|
||||
if (isConnect != null && isConnect && getGimbalAndCameraEnabled()) {
|
||||
DoublePoint2D doublePoint2D = new DoublePoint2D();
|
||||
doublePoint2D.setX(Double.parseDouble(message.getData().getX()));
|
||||
doublePoint2D.setY(Double.parseDouble(message.getData().getY()));
|
||||
KeyManager.getInstance().setValue(KeyTools.createCameraKey(CameraKey.KeyThermalSpotMetersurePoint,
|
||||
ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL), doublePoint2D, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
sendMsg2Server(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
LogUtil.log(TAG,"设置点测温失败:"+new Gson().toJson(error));
|
||||
sendFailMsg2Server(message, "设置点测温失败:" + getIDJIErrorMsg(error));
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
LogUtil.log(TAG, "测温点设置失败:当前状态相机禁止操作");
|
||||
}
|
||||
}
|
||||
//
|
||||
// //设置需要测温的点的位置
|
||||
// public void setThermalSpotMetersurePoint(MQMessage message) {
|
||||
// Boolean isConnect =KeyManager.getInstance().getValue(KeyTools.createKey(CameraKey.
|
||||
// KeyConnection,ComponentIndexType.PORT_1));
|
||||
// if (isConnect != null && isConnect && getGimbalAndCameraEnabled()) {
|
||||
// DoublePoint2D doublePoint2D = new DoublePoint2D();
|
||||
// doublePoint2D.setX(Double.parseDouble(message.getMetersurePointX()));
|
||||
// doublePoint2D.setY(Double.parseDouble(message.getMetersurePointY()));
|
||||
// KeyManager.getInstance().setValue(KeyTools.createCameraKey(CameraKey.KeyThermalSpotMetersurePoint,
|
||||
// ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL), doublePoint2D, new CommonCallbacks.CompletionCallback() {
|
||||
// @Override
|
||||
// public void onSuccess() {
|
||||
// sendMsg2Server(message);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(@NonNull IDJIError error) {
|
||||
// LogUtil.log(TAG,"设置点测温失败:"+new Gson().toJson(error));
|
||||
// sendMsg2Server(message, "设置点测温失败:" + getIDJIErrorMsg(error));
|
||||
//
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// } else {
|
||||
// LogUtil.log(TAG, "测温点设置失败:当前状态相机禁止操作");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// //设置需要测温的区域位置
|
||||
// public void setThermalRegionMetersureArea(MQMessage message) {
|
||||
// Boolean isConnect =KeyManager.getInstance().getValue(KeyTools.createKey(CameraKey.
|
||||
// KeyConnection,ComponentIndexType.PORT_1));
|
||||
// if (isConnect != null && isConnect) {
|
||||
// DoubleRect doubleRect = new DoubleRect();
|
||||
// doubleRect.setX(Double.parseDouble(message.getMetersureAreaX()));
|
||||
// doubleRect.setY(Double.parseDouble(message.getMetersureAreaY()));
|
||||
// doubleRect.setHeight(Double.parseDouble(message.getMetersureAreaHeight()));
|
||||
// doubleRect.setWidth(Double.parseDouble(message.getMetersureAreaWidth()));
|
||||
// KeyManager.getInstance().setValue(KeyTools.createCameraKey(CameraKey.KeyThermalRegionMetersureArea,
|
||||
// ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL), doubleRect, new CommonCallbacks.CompletionCallback() {
|
||||
// @Override
|
||||
// public void onSuccess() {
|
||||
// sendMsg2Server(message);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void onFailure(@NonNull IDJIError error) {
|
||||
// LogUtil.log(TAG,"设置区域测温失败:"+new Gson().toJson(error));
|
||||
// sendMsg2Server(message, "设置区域测温失败:" + getIDJIErrorMsg(error));
|
||||
// }
|
||||
// });
|
||||
// } else {
|
||||
// LogUtil.log(TAG, "测温区域设置失败:当前状态相机禁止操作");
|
||||
// }
|
||||
// }
|
||||
//设置需要测温的区域位置
|
||||
public void setThermalRegionMetersureArea(MessageDown message) {
|
||||
Boolean isConnect =KeyManager.getInstance().getValue(KeyTools.createKey(CameraKey.
|
||||
KeyConnection,ComponentIndexType.PORT_1));
|
||||
if (isConnect != null && isConnect) {
|
||||
DoubleRect doubleRect = new DoubleRect();
|
||||
doubleRect.setX(Double.parseDouble(message.getData().getX()));
|
||||
doubleRect.setY(Double.parseDouble(message.getData().getY()));
|
||||
doubleRect.setHeight((double) message.getData().getHeight());
|
||||
doubleRect.setWidth((double) message.getData().getWidth());
|
||||
KeyManager.getInstance().setValue(KeyTools.createCameraKey(CameraKey.KeyThermalRegionMetersureArea,
|
||||
ComponentIndexType.PORT_1, CameraLensType.CAMERA_LENS_THERMAL), doubleRect, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
sendMsg2Server(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
LogUtil.log(TAG,"设置区域测温失败:"+new Gson().toJson(error));
|
||||
sendFailMsg2Server(message, "设置区域测温失败:" + getIDJIErrorMsg(error));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LogUtil.log(TAG, "测温区域设置失败:当前状态相机禁止操作");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.aros.apron.constant.AMSConfig;
|
|||
import com.aros.apron.entity.ApronExecutionStatus;
|
||||
import com.aros.apron.entity.MessageDown;
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.activity.MainActivity;
|
||||
import com.aros.apron.mix.Aprondown;
|
||||
import com.aros.apron.mix.Aprongim;
|
||||
import com.aros.apron.tools.AlternateArucoDetect;
|
||||
|
|
@ -26,6 +27,7 @@ import com.aros.apron.tools.LocationUtils;
|
|||
import com.aros.apron.tools.LogUtil;
|
||||
import com.aros.apron.tools.MqttManager;
|
||||
import com.aros.apron.tools.PreferenceUtils;
|
||||
import com.aros.apron.tools.PsdkWidgetScheduler;
|
||||
import com.aros.apron.tools.TakeoffProgressScheduler;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
|
|
@ -36,6 +38,7 @@ import java.util.Objects;
|
|||
|
||||
import dji.sdk.keyvalue.key.AirLinkKey;
|
||||
import dji.sdk.keyvalue.key.CameraKey;
|
||||
import dji.sdk.keyvalue.key.FlightAssistantKey;
|
||||
import dji.sdk.keyvalue.key.FlightControllerKey;
|
||||
import dji.sdk.keyvalue.key.GimbalKey;
|
||||
import dji.sdk.keyvalue.key.KeyTools;
|
||||
|
|
@ -51,6 +54,7 @@ import dji.sdk.keyvalue.value.common.EmptyMsg;
|
|||
import dji.sdk.keyvalue.value.common.LocationCoordinate2D;
|
||||
import dji.sdk.keyvalue.value.common.LocationCoordinate3D;
|
||||
import dji.sdk.keyvalue.value.common.Velocity3D;
|
||||
import dji.sdk.keyvalue.value.flightassistant.AuxiliaryLightMode;
|
||||
import dji.sdk.keyvalue.value.flightassistant.VisionAssistDirection;
|
||||
import dji.sdk.keyvalue.value.flightcontroller.FailsafeAction;
|
||||
import dji.sdk.keyvalue.value.flightcontroller.FlightMode;
|
||||
|
|
@ -120,8 +124,12 @@ public class FlightManager extends BaseManager {
|
|||
OSDManager.getInstance().pushFlightAttitude();
|
||||
Movement.getInstance().setMissionFinish(true);
|
||||
|
||||
PsdkWidgetScheduler.getInstance().stop();
|
||||
|
||||
} else {
|
||||
|
||||
PsdkWidgetScheduler.getInstance().start();
|
||||
|
||||
PerceptionManager.getInstance().setPerceptionEnable(false);
|
||||
PerceptionManager.getInstance().setObstacleAvoidanceupEnabled(false);
|
||||
PerceptionManager.getInstance().setObstacleAvoidancedownEnabled(false);
|
||||
|
|
@ -790,6 +798,8 @@ public class FlightManager extends BaseManager {
|
|||
private int goHomeExecutionState;
|
||||
//确保每次流程只发送触发降落一次,降落完成后设置为false,而不是最后开始landing时触发,如果不再landing时设置为false,那么在landing的途中,也可能再次触发landing
|
||||
private boolean isSendDetect;
|
||||
// 强制触发ArUco检测标志(无视高度条件,与高度条件 || 关系)
|
||||
public static boolean forceTriggerDetection;
|
||||
//飞机飞走后是否发送关舱门(确保只发送一次)
|
||||
private boolean sendCloseCabinDoorMsg;
|
||||
//飞机飞回后是否发送开舱门(确保只发送一次)
|
||||
|
|
@ -904,16 +914,19 @@ public class FlightManager extends BaseManager {
|
|||
double distance = Movement.getInstance().getHome_distance();
|
||||
double flyingHeight = Movement.getInstance().getElevation();
|
||||
boolean isDebugMode = PreferenceUtils.getInstance().getIsDebugMode();
|
||||
|
||||
boolean isDistanceAndHeightValid = distance < DISTANCE_THRESHOLD &&
|
||||
flyingHeight > FLYING_HEIGHT_THRESHOLD && !sendOpenCabinDoorMsg;
|
||||
|
||||
|
||||
|
||||
if (!PreferenceUtils.getInstance().getTriggerToAlternatePoint() &&
|
||||
isReturningHome && isDistanceAndHeightValid && !isDebugMode) {
|
||||
LogUtil.log(TAG, "返航距离:" + distance + "---当前高度:" + flyingHeight);
|
||||
|
||||
sendOpenCabinDoorMsg = true;
|
||||
DockCloseManager.getInstance().stopRetry();
|
||||
DockOpenManager.getInstance().sendDockOpenMsg2Server();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -921,9 +934,9 @@ public class FlightManager extends BaseManager {
|
|||
//降落时将云台朝下
|
||||
private void gimbalDownwards() {
|
||||
if (goHomeExecutionState == GoHomeState.LANDING.value()
|
||||
&& Movement.getInstance().getElevation() > 11 && !isGimbalDownwards) {
|
||||
|
||||
|
||||
&& Movement.getInstance().getElevation() > 5 && !isGimbalDownwards) {
|
||||
// 切换为强制扫码模式:扫到任何ArUco码就触发刹停
|
||||
MainActivity.setStartArucoType(3);
|
||||
|
||||
MediaDataCenter.getInstance().getCameraStreamManager().setVisionAssistViewDirection(VisionAssistDirection.DOWN, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
|
|
@ -941,6 +954,7 @@ public class FlightManager extends BaseManager {
|
|||
leDsSettings.setNavigationLEDsOn(false);
|
||||
leDsSettings.setStatusIndicatorLEDsOn(true);
|
||||
leDsSettings.setFrontLEDsOn(true);
|
||||
|
||||
KeyManager.getInstance().setValue(KeyTools.createKey(FlightControllerKey.KeyLEDsSettings), leDsSettings, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
|
@ -953,12 +967,28 @@ public class FlightManager extends BaseManager {
|
|||
}
|
||||
});
|
||||
|
||||
KeyManager.getInstance().setValue(KeyTools.createKey(FlightAssistantKey.KeyBottomAuxiliaryLightMode), AuxiliaryLightMode.OFF, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
LogUtil.log(TAG,"下视辅助灯开启成功");
|
||||
}
|
||||
|
||||
DroneHelper.getInstance().setGimbalPitchDegree();
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError idjiError) {
|
||||
LogUtil.log(TAG,"夜航灯关闭失败");
|
||||
}
|
||||
});
|
||||
|
||||
//DroneHelper.getInstance().setGimbalPitchDegree();
|
||||
//将镜头设置为自动对焦
|
||||
DroneHelper.getInstance().setCameraFocusMode();
|
||||
|
||||
//DroneHelper.getInstance().setCameraFocusMode();
|
||||
|
||||
new android.os.Handler().postDelayed(() -> {
|
||||
DroneHelper.getInstance().setGimbalPitchDegree();
|
||||
}, 500);
|
||||
new android.os.Handler().postDelayed(() -> {
|
||||
DroneHelper.getInstance().setCameraFocusMode();
|
||||
}, 500);
|
||||
|
||||
|
||||
new android.os.Handler().postDelayed(() -> {
|
||||
|
|
@ -967,7 +997,6 @@ public class FlightManager extends BaseManager {
|
|||
new android.os.Handler().postDelayed(() -> {
|
||||
setCameraExposureMode();
|
||||
}, 500);
|
||||
|
||||
new android.os.Handler().postDelayed(() -> {
|
||||
EventBus.getDefault().post("REFRESH_VIDEO_SOURCE");
|
||||
}, 2000);
|
||||
|
|
@ -986,13 +1015,21 @@ public class FlightManager extends BaseManager {
|
|||
|
||||
isGimbalDownwards = true;
|
||||
|
||||
|
||||
//重复设置防止重复
|
||||
new android.os.Handler().postDelayed(() -> {
|
||||
DroneHelper.getInstance().setGimbalPitchDegree();
|
||||
}, 1000);
|
||||
|
||||
PerceptionManager.getInstance().setPerceptionEnable(false);
|
||||
PerceptionManager.getInstance().setObstacleAvoidanceupEnabled(false);
|
||||
PerceptionManager.getInstance().setObstacleAvoidancedownEnabled(false);
|
||||
PerceptionManager.getInstance().closeRadarManager(false);
|
||||
|
||||
if (Movement.getInstance().getIsRecording() == 1) {
|
||||
CameraManager.getInstance().stopRecordVideo(null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1136,6 +1173,10 @@ public class FlightManager extends BaseManager {
|
|||
private static final double FLYING_HEIGHT_THRESHOLD_MIN = -2;
|
||||
private static final double FLYING_HEIGHT_THRESHOLD_MIN_ALTERNATE = 2.0;
|
||||
|
||||
// 打印相对高度的节流计数器(约每秒一次)
|
||||
private int heightLogCounter = 0;
|
||||
private static final int HEIGHT_LOG_INTERVAL = 30; // startVisionLanding大约每秒回调一次
|
||||
|
||||
private void startVisionLanding() {
|
||||
|
||||
boolean isDebugMode = PreferenceUtils.getInstance().getIsDebugMode();
|
||||
|
|
@ -1143,29 +1184,36 @@ public class FlightManager extends BaseManager {
|
|||
boolean needTriggerApronArucoLand = PreferenceUtils.getInstance().getNeedTriggerApronArucoLand();
|
||||
boolean needTriggerAlterArucoLand = PreferenceUtils.getInstance().getNeedTriggerAlterArucoLand();
|
||||
double thresholdMax = triggerToAlternatePoint ? FLYING_HEIGHT_THRESHOLD_MAX_ALTERNATE : FLYING_HEIGHT_THRESHOLD_MAX;
|
||||
//LogUtil.log(TAG,"相对高度"+Movement.getInstance().getElevation()+"超声波高度"+Movement.getInstance().getUltrasonicHeight());
|
||||
|
||||
// 节流打印相对高度和超声波高度(约每秒1次)
|
||||
if (++heightLogCounter >= HEIGHT_LOG_INTERVAL) {
|
||||
LogUtil.log(TAG, "[高度监控] 相对高度=" + Movement.getInstance().getElevation() + "m, 超声波高度=" + Movement.getInstance().getUltrasonicHeight() + "dm");
|
||||
heightLogCounter = 0;
|
||||
}
|
||||
|
||||
|
||||
if (isFlying && (Movement.getInstance().getElevation() < 15 && Movement.getInstance().getUltrasonicHeight() < 50) && !isSendDetect) {
|
||||
// LogUtil.log(TAG,"相对高度"+Movement.getInstance().getElevation()+"超声波高度"+Movement.getInstance().getUltrasonicHeight());
|
||||
|
||||
|
||||
if (isFlying && (Movement.getInstance().getElevation() < 15 && Movement.getInstance().getUltrasonicHeight() < 50 || forceTriggerDetection) && !isSendDetect) {
|
||||
double flyingHeight = Movement.getInstance().getElevation();
|
||||
double thresholdMin = triggerToAlternatePoint ? FLYING_HEIGHT_THRESHOLD_MIN_ALTERNATE : FLYING_HEIGHT_THRESHOLD_MIN;
|
||||
|
||||
|
||||
|
||||
if (flyingHeight > thresholdMin) {
|
||||
//LogUtil.log(TAG,"相对高度"+Movement.getInstance().getElevation()+"超声波高度"+Movement.getInstance().getUltrasonicHeight());
|
||||
if (flyingHeight > thresholdMin || forceTriggerDetection) {
|
||||
|
||||
boolean shouldTriggerDetection;
|
||||
|
||||
if (isDebugMode) {
|
||||
|
||||
shouldTriggerDetection = goHomeExecutionState == 2;
|
||||
|
||||
} else {
|
||||
|
||||
shouldTriggerDetection = goHomeExecutionState == 2 || needTriggerApronArucoLand || needTriggerAlterArucoLand;
|
||||
}
|
||||
if (shouldTriggerDetection) {
|
||||
if (shouldTriggerDetection || forceTriggerDetection) {
|
||||
|
||||
triggerArucoDetection();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1174,6 +1222,8 @@ public class FlightManager extends BaseManager {
|
|||
|
||||
private void triggerArucoDetection() {
|
||||
//当电池电量大于40时,允许复降次数设置为10次
|
||||
isSendDetect = true;
|
||||
|
||||
if (Movement.getInstance().getBattery_a_capacity_percent() > 40) {
|
||||
AMSConfig.getInstance().setAlternateLandingTimes(10 + "");
|
||||
}
|
||||
|
|
@ -1192,7 +1242,7 @@ public class FlightManager extends BaseManager {
|
|||
LogUtil.log(TAG, "开始识别机库二维码,椭球高度:" + Movement.getInstance().getElevation() + "米" + "--超声波高度:" + Movement.getInstance().getUltrasonicHeight() + "分米");
|
||||
sendEvent2Server("开始视觉降落", 1);
|
||||
}
|
||||
isSendDetect = true;
|
||||
|
||||
//PerceptionManager.getInstance().setPerceptionEnable(false);
|
||||
|
||||
}
|
||||
|
|
@ -1242,6 +1292,8 @@ public class FlightManager extends BaseManager {
|
|||
isGimbalReset = false;
|
||||
isTriggerLanding = true;
|
||||
isGimbalDownwards = false;
|
||||
forceTriggerDetection = false;
|
||||
MainActivity.setStartArucoType(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -1283,6 +1335,8 @@ public class FlightManager extends BaseManager {
|
|||
isSendDetect = false;
|
||||
isTriggerLanding = false;
|
||||
sendCloseCabinDoorMsg = false;
|
||||
forceTriggerDetection = false;
|
||||
MainActivity.setStartArucoType(0);
|
||||
ApronArucoDetect.getInstance().setCanLanding(false);
|
||||
ApronArucoDetectPort.getInstance().setCanLanding(false);
|
||||
Aprondown.getInstance().setCanLanding(false);
|
||||
|
|
@ -1325,6 +1379,7 @@ public class FlightManager extends BaseManager {
|
|||
}
|
||||
// 上传媒体文件
|
||||
SystemManager.getInstance().upLoadMedia(MqttManager.getInstance().mqttAndroidClient);
|
||||
|
||||
sendEvent2Server("开始上传日志文件", 1);
|
||||
//上传日志文件
|
||||
LogUploadManager.getInstance().uploadTodayLog();
|
||||
|
|
|
|||
|
|
@ -102,27 +102,44 @@ public class MediaManager extends BaseManager {
|
|||
|
||||
private int enterPlayBackFailTimes;
|
||||
private boolean isEnablePlayback;
|
||||
private volatile boolean isPlaybackEnabling; // 防止并发调用
|
||||
private Handler playbackTimeoutHandler = new Handler(android.os.Looper.getMainLooper());
|
||||
private Runnable playbackTimeoutRunnable = null;
|
||||
private static final int PLAYBACK_ENABLE_TIMEOUT_MS = 15000; // 进入媒体模式超时 15 秒
|
||||
|
||||
public void enablePlayback() {
|
||||
// 每次进入媒体模式时清空已上传文件集合
|
||||
uploadedFileNames.clear();
|
||||
bucketChecked = false;
|
||||
downloadFailTimes = 0;
|
||||
// 重置失败计数和标志
|
||||
enterPlayBackFailTimes = 0;
|
||||
isEnablePlayback = false;
|
||||
// 重置拉取文件列表相关的计数器
|
||||
pullMediaFileListFromCameraFailTimes = 0;
|
||||
updatingWaitCount = 0;
|
||||
pullqwq = false;
|
||||
pullStartTime = System.currentTimeMillis(); // 开始计时
|
||||
if (isPlaybackEnabling) {
|
||||
LogUtil.log(TAG, "媒体模式已在启用中,跳过重复调用");
|
||||
return;
|
||||
}
|
||||
boolean isFirstEntry = !isPlaybackEnabling;
|
||||
isPlaybackEnabling = true;
|
||||
|
||||
LogUtil.log(TAG, "已清空上传文件集合");
|
||||
// 停止端口扫描和 RTSP 刷新,关闭 RTSP 推流,确保媒体上传稳定
|
||||
StreamManager.getInstance().stopStreamRefreshTimer();
|
||||
com.aros.apron.tools.SimplePortScanner.getInstance().stopScan();
|
||||
StreamManager.getInstance().stopstream();
|
||||
|
||||
// 仅首次调用时清理状态,重试时保留计数器
|
||||
if (isFirstEntry) {
|
||||
|
||||
uploadedFileNames.clear();
|
||||
bucketChecked = false;
|
||||
downloadFailTimes = 0;
|
||||
enterPlayBackFailTimes = 0;
|
||||
isEnablePlayback = false;
|
||||
pullMediaFileListFromCameraFailTimes = 0;
|
||||
updatingWaitCount = 0;
|
||||
pullqwq = false;
|
||||
pullStartTime = System.currentTimeMillis();
|
||||
LogUtil.log(TAG, "已清空上传文件集合");
|
||||
}
|
||||
|
||||
MediaDataCenter.getInstance().getMediaManager().enable(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
LogUtil.log(TAG, "进入媒体模式成功");
|
||||
isPlaybackEnabling = false; // 成功后释放锁
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
@ -149,8 +166,60 @@ public class MediaManager extends BaseManager {
|
|||
public void run() {
|
||||
if (enterPlayBackFailTimes < 10) {
|
||||
enterPlayBackFailTimes++;
|
||||
enablePlayback();
|
||||
isPlaybackEnabling = false; // 释放锁
|
||||
LogUtil.log(TAG, "第" + enterPlayBackFailTimes + "次重试进入媒体模式");
|
||||
retryEnablePlayback();
|
||||
} else {
|
||||
isPlaybackEnabling = false; // 失败放弃后释放锁
|
||||
ApronExecutionStatus.getInstance().setAircraftWaitShutDown(true);
|
||||
sendEvent2Server("媒体模式进入失败:关机",1);
|
||||
}
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 重试进入媒体模式(不清理状态,保留计数器) */
|
||||
private void retryEnablePlayback() {
|
||||
isPlaybackEnabling = true;
|
||||
MediaDataCenter.getInstance().getMediaManager().enable(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
LogUtil.log(TAG, "进入媒体模式成功(重试)");
|
||||
isPlaybackEnabling = false;
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MediaFileListDataSource source = new
|
||||
MediaFileListDataSource.Builder().setIndexType(ComponentIndexType.PORT_1).build();
|
||||
MediaDataCenter.getInstance().getMediaManager().setMediaFileDataSource(source);
|
||||
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
pullMediaFileListFromCamera();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError idjiError) {
|
||||
LogUtil.log(TAG, "第" + enterPlayBackFailTimes + "次进入媒体模式失败:" + new Gson().toJson(idjiError));
|
||||
if (!isEnablePlayback) {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (enterPlayBackFailTimes < 10) {
|
||||
enterPlayBackFailTimes++;
|
||||
isPlaybackEnabling = false;
|
||||
LogUtil.log(TAG, "第" + enterPlayBackFailTimes + "次重试进入媒体模式");
|
||||
retryEnablePlayback();
|
||||
} else {
|
||||
isPlaybackEnabling = false;
|
||||
ApronExecutionStatus.getInstance().setAircraftWaitShutDown(true);
|
||||
sendEvent2Server("媒体模式进入失败:关机",1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -909,9 +909,15 @@ public class MissionV3Manager extends BaseManager {
|
|||
Boolean isConnect = KeyManager.getInstance().getValue(KeyTools.createKey(FlightControllerKey.
|
||||
KeyConnection));
|
||||
if (isConnect != null && isConnect) {
|
||||
if (isConnect == null || !isConnect || !getGimbalAndCameraEnabled()) {
|
||||
LogUtil.log(TAG,"当前状态禁止恢复");
|
||||
sendFailMsg2Server(message, "当前状态禁止恢复");
|
||||
return;
|
||||
}
|
||||
if (Movement.getInstance().getFlightmode() == 1) {
|
||||
Movement.getInstance().setMode_code(5);
|
||||
}
|
||||
|
||||
IWaypointMissionManager missionManager = WaypointMissionManager.getInstance();
|
||||
//如果航线是暂停状态,直接恢复航线,否则查询断点信息
|
||||
if (Movement.getInstance().getMissionStateCode() == 7) {
|
||||
|
|
|
|||
|
|
@ -185,6 +185,23 @@ public class OSDManager extends BaseManager {
|
|||
irMeteringPoint.setX(Movement.getInstance().getX());
|
||||
irMeteringPoint.setY(Movement.getInstance().getY());
|
||||
cameraA.setIr_metering_point(irMeteringPoint);
|
||||
Osd.Data.Cameras.IrMeteringArea irMeteringArea = new Osd.Data.Cameras.IrMeteringArea();
|
||||
irMeteringArea.setX(0);
|
||||
irMeteringArea.setY(0);
|
||||
irMeteringArea.setWidth(0);
|
||||
irMeteringArea.setHeight(0);
|
||||
irMeteringArea.setAver_temperature(0);
|
||||
Osd.Data.Cameras.IrTemperaturePoint irMinTemp = new Osd.Data.Cameras.IrTemperaturePoint();
|
||||
irMinTemp.setX(0);
|
||||
irMinTemp.setY(0);
|
||||
irMinTemp.setTemperature(0);
|
||||
irMeteringArea.setMin_temperature_point(irMinTemp);
|
||||
Osd.Data.Cameras.IrTemperaturePoint irMaxTemp = new Osd.Data.Cameras.IrTemperaturePoint();
|
||||
irMaxTemp.setX(0);
|
||||
irMaxTemp.setY(0);
|
||||
irMaxTemp.setTemperature(0);
|
||||
irMeteringArea.setMax_temperature_point(irMaxTemp);
|
||||
cameraA.setIr_metering_area(irMeteringArea);
|
||||
cameraA.setIr_zoom_factor(Movement.getInstance().getIr_zoom_factor());
|
||||
cameraA.setPayload_index("53-0-0");
|
||||
cameraA.setPhoto_state(Movement.getInstance().getPhoto_state());
|
||||
|
|
|
|||
|
|
@ -2,9 +2,18 @@ package com.aros.apron.manager;
|
|||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.aros.apron.base.BaseManager;
|
||||
import com.aros.apron.constant.AMSConfig;
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.entity.PayloadInfo;
|
||||
import com.aros.apron.entity.PsdkWidgetValuesReport;
|
||||
import com.aros.apron.tools.LogUtil;
|
||||
import com.aros.apron.tools.MqttManager;
|
||||
import com.aros.apron.xclog.XcFileLog;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
|
@ -12,6 +21,7 @@ import com.google.gson.GsonBuilder;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import dji.sdk.keyvalue.key.FlightControllerKey;
|
||||
import dji.sdk.keyvalue.key.KeyTools;
|
||||
|
|
@ -33,6 +43,16 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
|
||||
private List<PayloadInfo> payloadInfos = new ArrayList<>();
|
||||
|
||||
private PayloadBasicInfo cachedBasicInfo;
|
||||
private PayloadWidgetInfo cachedWidgetInfo;
|
||||
private Handler psdkReportHandler;
|
||||
private Runnable psdkReportRunnable;
|
||||
private static final long PSDK_REPORT_INTERVAL = 5000; // 5秒
|
||||
|
||||
private StringBuffer payloadBuffer = new StringBuffer();
|
||||
private volatile boolean hasSpeaker = false;
|
||||
private volatile boolean speakerPortSet = false;
|
||||
|
||||
private PayloadWidgetManager() {
|
||||
}
|
||||
|
||||
|
|
@ -58,7 +78,6 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
if (payloadManager != null) {
|
||||
IPayloadManager iPayloadManager = payloadManager.get(PayloadIndexType.PORT_2);
|
||||
if (iPayloadManager != null) {
|
||||
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_2).pullWidgetInfoFromPayload(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
|
@ -66,16 +85,42 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_2).addPayloadBasicInfoListener(new PayloadBasicInfoListener() {
|
||||
@Override
|
||||
public void onPayloadBasicInfoUpdate(PayloadBasicInfo info) {
|
||||
LogUtil.log(TAG, "左侧负载基础信息:" + info.toString());
|
||||
XcFileLog.getInstace().w(TAG, "左侧负载基础信息:" + info.toString());
|
||||
//LogUtil.log(TAG, "左侧负载基础信息:" + info.toString());
|
||||
// XcFileLog.getInstace().w(TAG, "左侧负载基础信息:" + info.toString());
|
||||
//cachedBasicInfo = info;
|
||||
//startPsdkWidgetReport();
|
||||
}
|
||||
});
|
||||
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_2).addPayloadDataListener(new PayloadDataListener() {
|
||||
@Override
|
||||
public void onDataFromPayloadUpdate(byte[] data) {
|
||||
LogUtil.log(TAG, "左侧负载数据信息:" +new String(data, java.nio.charset.StandardCharsets.UTF_8).replaceAll("[^\\x20-\\x7E\\u4e00-\\u9fa5]", ""));
|
||||
XcFileLog.getInstace().w(TAG, "左侧负载数据信息:" +new String(data, java.nio.charset.StandardCharsets.UTF_8).replaceAll("[^\\x20-\\x7E\\u4e00-\\u9fa5]", ""));
|
||||
String cleanData = new String(data, java.nio.charset.StandardCharsets.UTF_8)
|
||||
.replaceAll("[^\\x20-\\x7E\\u4e00-\\u9fa5]", "");
|
||||
// LogUtil.log(TAG, "左侧负载数据信息:" + cleanData);
|
||||
// XcFileLog.getInstace().w(TAG, "左侧负载数据信息:" + cleanData);
|
||||
|
||||
|
||||
if (cleanData.contains("jwD")) {
|
||||
payloadBuffer.setLength(0);
|
||||
payloadBuffer.append(cleanData);
|
||||
// LogUtil.log(TAG, "负载数据开始,清空缓存");
|
||||
}
|
||||
|
||||
|
||||
if (cleanData.contains("}")) {
|
||||
if (payloadBuffer.length() > 0) {
|
||||
payloadBuffer.append(cleanData);
|
||||
String fullJson = payloadBuffer.toString();
|
||||
payloadBuffer.setLength(0); // 清空,准备下次
|
||||
// LogUtil.log(TAG, "负载完整数据: " + fullJson);
|
||||
sendFloatingWindowText(fullJson);
|
||||
} else {
|
||||
//LogUtil.log(TAG, "负载数据: 缓存为空,忽略孤立}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -83,9 +128,12 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_2).addPayloadWidgetInfoListener(new PayloadWidgetInfoListener() {
|
||||
@Override
|
||||
public void onPayloadWidgetInfoUpdate(PayloadWidgetInfo info) {
|
||||
LogUtil.log(TAG, "左侧负载控件信息:" + info.toString());
|
||||
XcFileLog.getInstace().w(TAG, "左侧负载控件信息:" + info.toString());
|
||||
//LogUtil.log(TAG, "左侧负载控件信息:" + info.toString());
|
||||
//XcFileLog.getInstace().w(TAG, "左侧负载控件信息:" + info.toString());
|
||||
//cachedWidgetInfo = info;
|
||||
//如果负载为空
|
||||
detectSpeaker(info, 2);
|
||||
|
||||
if (info.getConfigInterfaceWidgetList() == null || info.getMainInterfaceWidgetList() == null) {
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_2).pullWidgetInfoFromPayload(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
|
|
@ -112,13 +160,52 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
} else {
|
||||
LogUtil.log(TAG, "监听LEFT_OR_MAIN PSDK数据失败:设备未连接");
|
||||
}
|
||||
|
||||
|
||||
IPayloadManager iPayloadManager3 = payloadManager.get(PayloadIndexType.PORT_3);
|
||||
if (iPayloadManager3 != null) {
|
||||
//基础信息
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_3).addPayloadBasicInfoListener(new PayloadBasicInfoListener() {
|
||||
@Override
|
||||
public void onPayloadBasicInfoUpdate(PayloadBasicInfo info) {
|
||||
// LogUtil.log(TAG, "3号负载基础信息:" + info.toString());
|
||||
}
|
||||
});
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_3).addPayloadDataListener(new PayloadDataListener() {
|
||||
@Override
|
||||
public void onDataFromPayloadUpdate(byte[] data) {
|
||||
// LogUtil.log(TAG, "3号Data负载基础信息:" + data);
|
||||
}
|
||||
});
|
||||
//可以把负载设备控件打印
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_3).addPayloadWidgetInfoListener(new PayloadWidgetInfoListener() {
|
||||
@Override
|
||||
public void onPayloadWidgetInfoUpdate(PayloadWidgetInfo info) {
|
||||
// LogUtil.log(TAG, "3号负载控件信息:" + info.toString());
|
||||
detectSpeaker(info, 3);
|
||||
if (info.getConfigInterfaceWidgetList() == null || info.getMainInterfaceWidgetList() == null) {
|
||||
PayloadCenter.getInstance().getPayloadManager().get(PayloadIndexType.PORT_3).pullWidgetInfoFromPayload(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
// LogUtil.log(TAG, "负载重复拉取成功");
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError idjiError) {
|
||||
//LogUtil.log(TAG, "负载重复拉取失败");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LogUtil.log(TAG, "监听right_OR_MAIN PSDK数据失败:设备未连接");
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
LogUtil.log(TAG, "监听psdk数据失败:未检测到设备");
|
||||
}
|
||||
|
||||
//负载基础信息
|
||||
|
||||
|
||||
} else {
|
||||
LogUtil.log(TAG, "设备未连接");
|
||||
}
|
||||
|
|
@ -425,6 +512,53 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
// }
|
||||
|
||||
|
||||
/**
|
||||
* 检测负载控件信息中是否有喊话器,检测到后自动设置喊话器位置
|
||||
* @param info 负载控件信息
|
||||
* @param portIndex 端口编号 2或3
|
||||
*/
|
||||
private void detectSpeaker(PayloadWidgetInfo info, int portIndex) {
|
||||
if (info == null || speakerPortSet) return;
|
||||
try {
|
||||
Object speakerWidget = info.getSpeakerWidget();
|
||||
if (speakerWidget != null) {
|
||||
String infoStr = info.toString();
|
||||
// speakerWidget=SpeakerWidget{isTTSEnabled=true, isVoiceEnabled=true} 代表有喊话器
|
||||
if (infoStr.contains("SpeakerWidget") && infoStr.contains("isTTSEnabled=true")) {
|
||||
hasSpeaker = true;
|
||||
LogUtil.log(TAG, "检测到喊话器负载(SpeakerWidget)在 PORT_" + portIndex);
|
||||
// 自动设置喊话器位置,设置成功后不再重复设置
|
||||
dji.v5.manager.aircraft.megaphone.MegaphoneIndex targetIndex =
|
||||
portIndex == 2 ? dji.v5.manager.aircraft.megaphone.MegaphoneIndex.PORT_2
|
||||
: dji.v5.manager.aircraft.megaphone.MegaphoneIndex.PORT_3;
|
||||
LogUtil.log(TAG, "自动设置喊话器位置到: " + targetIndex);
|
||||
dji.v5.manager.aircraft.megaphone.MegaphoneManager.getInstance().setMegaphoneIndex(targetIndex,
|
||||
new dji.v5.common.callback.CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
speakerPortSet = true;
|
||||
LogUtil.log(TAG, "喊话器位置设置成功(从负载控件自动设置): " + targetIndex + ",不再重复设置");
|
||||
// 同步到 SpeakerManager
|
||||
com.aros.apron.manager.SpeakerManager.psdkindex = portIndex;
|
||||
com.aros.apron.manager.SpeakerManager.getInstance().getMegaphoneIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull dji.v5.common.error.IDJIError error) {
|
||||
LogUtil.log(TAG, "喊话器位置设置失败(从负载控件自动设置): " + error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.log(TAG, "检测喊话器异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSpeaker() {
|
||||
return hasSpeaker;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查列表中是否已经存在指定payloadIndexType的PayloadInfo对象。
|
||||
*/
|
||||
|
|
@ -436,4 +570,140 @@ public class PayloadWidgetManager extends BaseManager {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 启动5秒定时上报
|
||||
*/
|
||||
private void startPsdkWidgetReport() {
|
||||
if (psdkReportHandler == null) {
|
||||
psdkReportHandler = new Handler(Looper.getMainLooper());
|
||||
}
|
||||
if (psdkReportRunnable != null) {
|
||||
return; // 已经在运行
|
||||
}
|
||||
psdkReportRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
psdkReportHandler.postDelayed(this, PSDK_REPORT_INTERVAL);
|
||||
}
|
||||
};
|
||||
psdkReportHandler.post(psdkReportRunnable);
|
||||
LogUtil.log(TAG, "启动PSDK负载控件5秒定时上报");
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止定时上报
|
||||
*/
|
||||
private void stopPsdkWidgetReport() {
|
||||
if (psdkReportHandler != null && psdkReportRunnable != null) {
|
||||
psdkReportHandler.removeCallbacks(psdkReportRunnable);
|
||||
psdkReportRunnable = null;
|
||||
LogUtil.log(TAG, "停止PSDK负载控件定时上报");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送PSDK负载控件值到MQTT
|
||||
*/
|
||||
// private void sendPsdkWidgetValues() {
|
||||
// try {
|
||||
// if (MqttManager.getInstance().mqttAndroidClient == null || !MqttManager.getInstance().mqttAndroidClient.isConnected()) {
|
||||
// return;
|
||||
// }
|
||||
// if (cachedBasicInfo == null && cachedWidgetInfo == null) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// PsdkWidgetValuesReport report = new PsdkWidgetValuesReport();
|
||||
// report.setTid(UUID.randomUUID().toString());
|
||||
// report.setBid(UUID.randomUUID().toString());
|
||||
// report.setTimestamp(System.currentTimeMillis());
|
||||
// report.setMethod("psdk_widget_values");
|
||||
// report.setGateway(Movement.getInstance().getSerialNumber());
|
||||
//
|
||||
// PsdkWidgetValuesReport.Data data = new PsdkWidgetValuesReport.Data();
|
||||
// List<PsdkWidgetValuesReport.PsdkWidgetValue> widgetList = new ArrayList<>();
|
||||
//
|
||||
// // 从cachedBasicInfo和cachedWidgetInfo组装数据
|
||||
// if (cachedBasicInfo != null) {
|
||||
// PsdkWidgetValuesReport.PsdkWidgetValue w = new PsdkWidgetValuesReport.PsdkWidgetValue();
|
||||
// w.setPsdk_index(2);
|
||||
// w.setPsdk_lib_version(cachedBasicInfo.getVersion() != null ? cachedBasicInfo.getVersion() : "");
|
||||
// w.setPsdk_name(cachedBasicInfo.getProductName() != null ? cachedBasicInfo.getProductName() : "");
|
||||
// w.setPsdk_sn(cachedBasicInfo.getDeviceSN() != null ? cachedBasicInfo.getDeviceSN() : "");
|
||||
// w.setPsdk_type(5); // Speaker type
|
||||
// w.setPsdk_version(cachedBasicInfo.getVersion() != null ? cachedBasicInfo.getVersion() : "");
|
||||
//
|
||||
// // Speaker数据
|
||||
// PsdkWidgetValuesReport.SpeakerData speaker = new PsdkWidgetValuesReport.SpeakerData();
|
||||
// speaker.setPlay_file_md5("");
|
||||
// speaker.setPlay_file_name("");
|
||||
// speaker.setPlay_mode(1);
|
||||
// speaker.setPlay_volume(30);
|
||||
// speaker.setSystem_state(0);
|
||||
// speaker.setWork_mode(1);
|
||||
//
|
||||
// // 从widgetInfo中尝试获取实际的widget值
|
||||
// if (cachedWidgetInfo != null) {
|
||||
// if (cachedWidgetInfo.getMainInterfaceWidgetList() != null) {
|
||||
// for (Object widget : cachedWidgetInfo.getMainInterfaceWidgetList()) {
|
||||
// // 根据widget类型设置speaker字段
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// w.setSpeaker(speaker);
|
||||
// w.setValues(new ArrayList<>());
|
||||
// widgetList.add(w);
|
||||
// }
|
||||
//
|
||||
// data.setPsdk_widget_values(widgetList);
|
||||
// report.setData(data);
|
||||
//
|
||||
// String json = gson.toJson(report);
|
||||
// LogUtil.log(TAG, "上报psdk_widget_values: " + json);
|
||||
//
|
||||
// MqttMessage msg = new MqttMessage(json.getBytes("UTF-8"));
|
||||
// msg.setQos(0);
|
||||
// MqttManager.getInstance().mqttAndroidClient.publish(AMSConfig.UP_UAV_EVENT, msg);
|
||||
// } catch (Exception e) {
|
||||
// LogUtil.log(TAG, "发送psdk_widget_values异常: " + e.getMessage());
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 发送负载浮窗文本到MQTT
|
||||
*/
|
||||
private void sendFloatingWindowText(String value) {
|
||||
try {
|
||||
if (MqttManager.getInstance().mqttAndroidClient == null || !MqttManager.getInstance().mqttAndroidClient.isConnected()) {
|
||||
LogUtil.log(TAG, "MQTT未连接,跳过浮窗文本上报");
|
||||
return;
|
||||
}
|
||||
|
||||
java.util.Map<String, Object> report = new java.util.LinkedHashMap<>();
|
||||
report.put("bid", UUID.randomUUID().toString());
|
||||
report.put("tid", UUID.randomUUID().toString());
|
||||
report.put("timestamp", System.currentTimeMillis());
|
||||
report.put("method", "psdk_floating_window_text_jdw");
|
||||
report.put("gateway", Movement.getInstance().getSerialNumber());
|
||||
|
||||
java.util.Map<String, Object> data = new java.util.LinkedHashMap<>();
|
||||
data.put("psdk_index", 2);
|
||||
data.put("value", value);
|
||||
report.put("data", data);
|
||||
|
||||
String json = new Gson().toJson(report);
|
||||
LogUtil.log(TAG, "发送浮窗文本: " + json);
|
||||
|
||||
org.eclipse.paho.client.mqttv3.MqttMessage msg = new org.eclipse.paho.client.mqttv3.MqttMessage(json.getBytes("UTF-8"));
|
||||
msg.setQos(1);
|
||||
MqttManager.getInstance().mqttAndroidClient.publish(AMSConfig.UP_UAV_EVENT, msg);
|
||||
} catch (Exception e) {
|
||||
LogUtil.log(TAG, "发送浮窗文本异常: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,9 +37,16 @@ import okhttp3.Request;
|
|||
import okhttp3.Response;
|
||||
|
||||
|
||||
|
||||
public class SpeakerManager extends BaseManager {
|
||||
|
||||
private boolean issetok=false;
|
||||
private volatile boolean issetok=false;
|
||||
private android.os.Handler retryHandler;
|
||||
private Runnable psdkIndexRetry;
|
||||
public static int psdkindex=0;
|
||||
|
||||
private static final int MAX_MEGAPHONE_RETRIES = 10;
|
||||
private int megaphoneRetryCount = 0;
|
||||
private SpeakerManager() {
|
||||
}
|
||||
private static class SpeakerHolder {
|
||||
|
|
@ -52,36 +59,215 @@ public class SpeakerManager extends BaseManager {
|
|||
private XTtsPcmGenerator ttsGenerator;
|
||||
|
||||
public void initMegaphoneInfo() {
|
||||
MegaphoneManager.getInstance().addMegaphoneInfoListener(new MegaphoneInfoListener() {
|
||||
@Override
|
||||
public void onUpdateMegaphoneInfo(MegaphoneInfo megaphoneInfo) {
|
||||
if (megaphoneInfo != null) {
|
||||
MegaphoneStatus megaphoneStatus = megaphoneInfo.getStatus();
|
||||
//LogUtil.log(TAG,"喊话器"+megaphoneStatus);
|
||||
// 当喊话器状态为"已连接"时(status=2),再设置位置
|
||||
if (megaphoneStatus == MegaphoneStatus.IDLE && issetok==false) {
|
||||
|
||||
MegaphoneManager.getInstance().setMegaphoneIndex(MegaphoneIndex.PORT_2, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
issetok=true;
|
||||
LogUtil.log(TAG,"喊话器位置设置成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
LogUtil.log(TAG,"喊话器位置设置失败:"+getIDJIErrorMsg(error));
|
||||
}
|
||||
});
|
||||
addMegaphoneInfoListener();
|
||||
// 延迟10秒再触发查询,等待喊话器硬件完全初始化
|
||||
if (!issetok) {
|
||||
if (retryHandler == null) {
|
||||
retryHandler = new android.os.Handler(android.os.Looper.getMainLooper());
|
||||
}
|
||||
retryHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!issetok) {
|
||||
startMegaphoneIndexQuery();
|
||||
}
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动查询并设置喊话器位置,失败后3秒重试,成功后不再重试
|
||||
*/
|
||||
public void startMegaphoneIndexQuery() {
|
||||
MegaphoneManager.getInstance().getMegaphoneIndex(new CommonCallbacks.CompletionCallbackWithParam<MegaphoneIndex>() {
|
||||
@Override
|
||||
public void onSuccess(MegaphoneIndex megaphoneIndex) {
|
||||
LogUtil.log(TAG,"喊话器位置"+megaphoneIndex);
|
||||
if (megaphoneIndex == MegaphoneIndex.UNKNOWN || megaphoneIndex == MegaphoneIndex.PORTSIDE) {
|
||||
LogUtil.log(TAG, "喊话器位置查询返回不确定(" + megaphoneIndex + "),直接进入重试");
|
||||
if (!issetok) {
|
||||
retryMegaphoneIndex();
|
||||
}
|
||||
return;
|
||||
}
|
||||
MegaphoneIndex targetIndex = megaphoneIndex;
|
||||
MegaphoneManager.getInstance().setMegaphoneIndex(targetIndex, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
issetok=true;
|
||||
LogUtil.log(TAG,"喊话器位置设置成功");
|
||||
getMegaphoneIndex();
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
LogUtil.log(TAG,"喊话器位置设置失败:"+getIDJIErrorMsg(error));
|
||||
if (!issetok) {
|
||||
retryMegaphoneIndex();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError idjiError) {
|
||||
LogUtil.log(TAG, "startMegaphoneIndexQuery失败: " + getIDJIErrorMsg(idjiError) + ",3秒后重试");
|
||||
if (!issetok) {
|
||||
retryMegaphoneIndex();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void retryMegaphoneIndex() {
|
||||
if (megaphoneRetryCount >= MAX_MEGAPHONE_RETRIES) {
|
||||
LogUtil.log(TAG, "喊话器位置设置已达到最大重试次数(" + MAX_MEGAPHONE_RETRIES + "),尝试盲设 PORT_2 / PORT_3");
|
||||
blindSetMegaphoneIndex();
|
||||
return;
|
||||
}
|
||||
if (retryHandler == null) {
|
||||
retryHandler = new android.os.Handler(android.os.Looper.getMainLooper());
|
||||
}
|
||||
retryHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!issetok) {
|
||||
megaphoneRetryCount++;
|
||||
LogUtil.log(TAG, "喊话器位置设置重试第 " + megaphoneRetryCount + " 次");
|
||||
startMegaphoneIndexQuery();
|
||||
}
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
private boolean isBlindSetting = false;
|
||||
|
||||
/**
|
||||
* 10次查询重试都失败后,依次盲试 PORT_2 和 PORT_3,只要有一个成功即可
|
||||
*/
|
||||
private void blindSetMegaphoneIndex() {
|
||||
if (issetok || isBlindSetting) return;
|
||||
isBlindSetting = true;
|
||||
MegaphoneIndex[] candidates = {MegaphoneIndex.PORT_2, MegaphoneIndex.PORT_3};
|
||||
blindSetWithIndex(candidates, 0);
|
||||
}
|
||||
|
||||
private void blindSetWithIndex(MegaphoneIndex[] candidates, int index) {
|
||||
if (index >= candidates.length || issetok) {
|
||||
if (!issetok) {
|
||||
LogUtil.log(TAG, "盲设喊话器位置全部失败,PORT_2 和 PORT_3 均不可用");
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
final MegaphoneIndex target = candidates[index];
|
||||
final int targetIdx = (target == MegaphoneIndex.PORT_2) ? 2 : 3;
|
||||
LogUtil.log(TAG, "盲设喊话器位置: " + target);
|
||||
MegaphoneManager.getInstance().setMegaphoneIndex(target, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
issetok = true;
|
||||
psdkindex = targetIdx;
|
||||
Movement.getInstance().setPsdk_index(String.valueOf(targetIdx));
|
||||
LogUtil.log(TAG, "盲设喊话器位置成功: " + target + ",psdk_index=" + targetIdx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
psdkindex=0;
|
||||
LogUtil.log(TAG, "盲设 " + target + " 失败,尝试下一个");
|
||||
blindSetWithIndex(candidates, index + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//喊话器位置
|
||||
public void getMegaphoneIndex(){
|
||||
MegaphoneManager.getInstance().getMegaphoneIndex(new CommonCallbacks.CompletionCallbackWithParam<MegaphoneIndex>() {
|
||||
@Override
|
||||
public void onSuccess(MegaphoneIndex megaphoneIndex) {
|
||||
int index = switch (megaphoneIndex) {
|
||||
case PORT_1 -> 1;
|
||||
case PORT_2 -> 2;
|
||||
case PORT_3 -> 3;
|
||||
case PORTSIDE, UNKNOWN -> 0; // PORTSIDE和UNKNOWN都视为不确定,进入盲设
|
||||
default -> 0;
|
||||
};
|
||||
if (index != 0) {
|
||||
Movement.getInstance().setPsdk_index(String.valueOf(index));
|
||||
issetok = true;
|
||||
LogUtil.log(TAG, "psdk_index已设置并保持: " + index);
|
||||
psdkindex=index;
|
||||
} else {
|
||||
LogUtil.log(TAG, "getMegaphoneIndex回调位置不确定(" + megaphoneIndex + "),进入盲设流程");
|
||||
blindSetMegaphoneIndex();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError idjiError) {
|
||||
LogUtil.log(TAG, "getMegaphoneIndex失败: " + getIDJIErrorMsg(idjiError) + ",10秒后重试");
|
||||
if (retryHandler == null) {
|
||||
retryHandler = new android.os.Handler(android.os.Looper.getMainLooper());
|
||||
}
|
||||
retryHandler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!issetok) {
|
||||
getMegaphoneIndex();
|
||||
}
|
||||
}
|
||||
}, 10000);
|
||||
}
|
||||
});
|
||||
}
|
||||
//喊话器基础信息
|
||||
public void addMegaphoneInfoListener(){
|
||||
MegaphoneManager.getInstance().addMegaphoneInfoListener(new MegaphoneInfoListener() {
|
||||
@Override
|
||||
public void onUpdateMegaphoneInfo(MegaphoneInfo megaphoneInfo) {
|
||||
if (megaphoneInfo == null) return;
|
||||
//工作模式 音量 状态 播放模式
|
||||
Movement.getInstance().setPlay_mode(String.valueOf(megaphoneInfo.getPlayMode().value));
|
||||
Movement.getInstance().setPlay_volume(String.valueOf(megaphoneInfo.getVolume()));
|
||||
Movement.getInstance().setWork_mode(String.valueOf(megaphoneInfo.getWorkMode().co_a));
|
||||
Movement.getInstance().setSystem_state(String.valueOf(megaphoneInfo.getStatus().co_a));
|
||||
|
||||
MegaphoneStatus status = megaphoneInfo.getStatus();
|
||||
if (status == MegaphoneStatus.PLAYING) {
|
||||
LogUtil.log(TAG, "喊话器状态: 正在播放中");
|
||||
Movement.getInstance().setStep_key("play");
|
||||
Movement.getInstance().setSpeakerpercent(100);
|
||||
Movement.getInstance().setTTS_status("success");
|
||||
|
||||
} else if (status == MegaphoneStatus.IDLE) {
|
||||
LogUtil.log(TAG, "喊话器状态: 空闲");
|
||||
speakerStop();//暂停上传
|
||||
|
||||
} else if (status == MegaphoneStatus.IN_TRANSMISSION) {
|
||||
LogUtil.log(TAG, "喊话器状态: 数据传输");
|
||||
Movement.getInstance().setStep_key("upload");
|
||||
Movement.getInstance().setTTS_status("in_progress");
|
||||
|
||||
|
||||
} else if (status == MegaphoneStatus.TTS_IN_CONVERSION) {
|
||||
LogUtil.log(TAG, "喊话器状态: tTTS正在转换成语音中");
|
||||
Movement.getInstance().setTTS_status("in_progress");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
//
|
||||
|
||||
public void speakerAudioPlayStart(MessageDown message) {
|
||||
sendMsg2Server(message);
|
||||
//暂停播放
|
||||
stop();
|
||||
|
||||
|
||||
stopspeaker();
|
||||
|
||||
|
||||
if (TextUtils.isEmpty(message.getData().getFile().getUrl())) {
|
||||
sendFailMsg2Server(message, "喊话文件下载地址为空");
|
||||
|
|
@ -133,61 +319,61 @@ public class SpeakerManager extends BaseManager {
|
|||
null);
|
||||
MegaphoneManager.getInstance().startPushingFileToMegaphone(fileInfo,
|
||||
new CommonCallbacks.CompletionCallbackWithProgress<Integer>() {
|
||||
private boolean isCallbackExecuted = false; // 确保回调只执行一次
|
||||
|
||||
private boolean hasTriggeredPlay = false;
|
||||
@Override
|
||||
public void onProgressUpdate(Integer integer) {
|
||||
|
||||
Movement.getInstance().setSpeakerpercent(integer);
|
||||
|
||||
LogUtil.log(TAG, "喊话器内容上传进度:" + integer + "%");
|
||||
|
||||
if (integer >= 99 && !hasTriggeredPlay) {
|
||||
hasTriggeredPlay = true;
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MegaphoneManager.getInstance().startPlay(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Movement.getInstance().setStep_key("play");
|
||||
Movement.getInstance().setTTS_status("success");
|
||||
sendMsg2Server(message);
|
||||
SpeakerProgressReporter.getInstance().sendNowAudioProgress(psdkindex);
|
||||
sendEvent2Server("喊话器播放音频成功", 1);
|
||||
LogUtil.log(TAG,"喊话器播放音频成功");
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
|
||||
sendFailMsg2Server(message, "喊话器播放音频失败:" + getIDJIErrorMsg(error));
|
||||
sendEvent2Server("喊话器播放音频失败:" + getIDJIErrorMsg(error), 2);
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
LogUtil.log(TAG,"喊话器播放音频失败"+ getIDJIErrorMsg(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
// 确保回调只执行一次
|
||||
if (isCallbackExecuted) {
|
||||
return;
|
||||
}
|
||||
isCallbackExecuted = true;
|
||||
|
||||
LogUtil.log(TAG, "喊话器内容上传成功");
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MegaphoneManager.getInstance().startPlay(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Movement.getInstance().setTTS_status("ok");
|
||||
sendEvent2Server("喊话器播放音频成功", 1);
|
||||
LogUtil.log(TAG,"喊话器播放音频成功");
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
sendEvent2Server("喊话器播放音频失败:" + getIDJIErrorMsg(error), 2);
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
LogUtil.log(TAG,"喊话器播放音频失败"+ getIDJIErrorMsg(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
// 确保回调只执行一次
|
||||
if (isCallbackExecuted) {
|
||||
return;
|
||||
}
|
||||
isCallbackExecuted = true;
|
||||
|
||||
sendFailMsg2Server(message, "喊话器内容上传失败:" + getIDJIErrorMsg(error));
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
sendEvent2Server("喊话.pcm文件下载失败:" + e.toString(), 2);
|
||||
Synchronizedstatus.setSpeakrunning(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -213,6 +399,22 @@ public class SpeakerManager extends BaseManager {
|
|||
|
||||
}
|
||||
|
||||
public void stopspeaker(){
|
||||
MegaphoneManager.getInstance().stopPlay(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Synchronizedstatus.setSpeaksetrunning(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
Synchronizedstatus.setSpeaksetrunning(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void speakerStop(MessageDown message) {
|
||||
MegaphoneManager.getInstance().stopPlay(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
|
|
@ -232,7 +434,8 @@ public class SpeakerManager extends BaseManager {
|
|||
}
|
||||
});
|
||||
}
|
||||
public void stop(){
|
||||
|
||||
public void speakerStop() {
|
||||
MegaphoneManager.getInstance().stopPlay(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
|
@ -246,9 +449,10 @@ public class SpeakerManager extends BaseManager {
|
|||
SpeakerProgressReporter.getInstance().stopTTSReport();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void speakerPlayModeSet(MessageDown message) {
|
||||
MegaphoneManager.getInstance().setPlayMode(message.getData().getPlay_mode() == 0 ?
|
||||
PlayMode.SINGLE : PlayMode.LOOP, new CommonCallbacks.CompletionCallback() {
|
||||
|
|
@ -285,8 +489,8 @@ public class SpeakerManager extends BaseManager {
|
|||
private String text;
|
||||
|
||||
public void speakerTTSPlayStart(MessageDown message, int type) {
|
||||
//暂停播放
|
||||
stop();
|
||||
|
||||
stopspeaker();
|
||||
|
||||
LogUtil.log(TAG, "开始执行 speakerTTSPlayStart 方法,type: " + type);
|
||||
// 检查是否正在运行
|
||||
|
|
@ -376,7 +580,7 @@ public class SpeakerManager extends BaseManager {
|
|||
LogUtil.log(TAG, "开始上传文件到喊话器");
|
||||
MegaphoneManager.getInstance().startPushingFileToMegaphone(fileInfo,
|
||||
new CommonCallbacks.CompletionCallbackWithProgress<Integer>() {
|
||||
private boolean isCallbackExecuted = false; // 确保回调只执行一次
|
||||
|
||||
|
||||
@Override
|
||||
public void onProgressUpdate(Integer integer) {
|
||||
|
|
@ -388,11 +592,6 @@ public class SpeakerManager extends BaseManager {
|
|||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
// 确保回调只执行一次
|
||||
if (isCallbackExecuted) {
|
||||
return;
|
||||
}
|
||||
isCallbackExecuted = true;
|
||||
|
||||
LogUtil.log(TAG, "喊话器内容上传成功");
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
|
|
@ -403,8 +602,10 @@ public class SpeakerManager extends BaseManager {
|
|||
@Override
|
||||
public void onSuccess() {
|
||||
sendMsg2Server(message);
|
||||
Movement.getInstance().setTTS_status("ok");
|
||||
Movement.getInstance().setTTS_status("success");
|
||||
Movement.getInstance().setStep_key("play");
|
||||
|
||||
SpeakerProgressReporter.getInstance().sendNowTTSProgress(psdkindex);
|
||||
sendEvent2Server("喊话器播放TTS音频成功", 1);
|
||||
LogUtil.log(TAG,"喊话器播放TTS音频成功");
|
||||
Synchronizedstatus.setSpeakTTSrunning(false);
|
||||
|
|
@ -424,36 +625,37 @@ public class SpeakerManager extends BaseManager {
|
|||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
// 确保回调只执行一次
|
||||
if (isCallbackExecuted) {
|
||||
return;
|
||||
}
|
||||
isCallbackExecuted = true;
|
||||
|
||||
sendFailMsg2Server(message, "喊话器内容上传失败:" + getIDJIErrorMsg(error));
|
||||
LogUtil.log(TAG, "喊话器内容上传失败:" + getIDJIErrorMsg(error));
|
||||
Synchronizedstatus.setSpeakTTSrunning(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
LogUtil.log(TAG, "合成失败,PCM 文件为空");
|
||||
sendFailMsg2Server(message, "合成失败,PCM 文件为空");
|
||||
Synchronizedstatus.setSpeakTTSrunning(false);
|
||||
}
|
||||
|
||||
LogUtil.log(TAG, "设置 speakTTSrunning 为 false");
|
||||
LogUtil.log(TAG, "speakerTTSPlayStart 方法执行完成");
|
||||
Synchronizedstatus.setSpeakTTSrunning(false);
|
||||
}
|
||||
|
||||
public void setindex(){
|
||||
MegaphoneManager.getInstance().setMegaphoneIndex(MegaphoneIndex.PORT_2, new CommonCallbacks.CompletionCallback() {
|
||||
MegaphoneIndex targetIndex = psdkindex == 2 ? MegaphoneIndex.PORT_2 :
|
||||
psdkindex == 3 ? MegaphoneIndex.PORT_3 : MegaphoneIndex.PORT_2;
|
||||
LogUtil.log(TAG, "setindex 根据 psdkindex=" + psdkindex + " 设置位置: " + targetIndex);
|
||||
MegaphoneManager.getInstance().setMegaphoneIndex(targetIndex, new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
issetok=true;
|
||||
LogUtil.log(TAG,"喊话器位置设置成功");
|
||||
issetok = true;
|
||||
LogUtil.log(TAG, "喊话器位置设置成功");
|
||||
getMegaphoneIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
LogUtil.log(TAG,"喊话器位置设置失败:"+getIDJIErrorMsg(error));
|
||||
LogUtil.log(TAG, "喊话器位置设置失败:" + getIDJIErrorMsg(error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class StreamManager extends BaseManager {
|
|||
private final Handler streamRefreshHandler = new Handler(Looper.getMainLooper());
|
||||
private Runnable streamRefreshRunnable = null;
|
||||
private volatile boolean isStreamRefreshing = false;
|
||||
private static final long STREAM_REFRESH_INTERVAL_MS = 5000;
|
||||
private static final long STREAM_REFRESH_INTERVAL_MS = 8000;
|
||||
|
||||
private StreamManager() {
|
||||
}
|
||||
|
|
@ -112,10 +112,12 @@ public class StreamManager extends BaseManager {
|
|||
|
||||
// ========== 【新增】重置推流状态,用于端口关闭后重启 ==========
|
||||
public void resetStreamState() {
|
||||
stopStreamRefreshTimer(); // 重置时也停止定时器
|
||||
stopStreamRefreshTimer();
|
||||
SimplePortScanner.getInstance().stopScan(); // 同时停止端口扫描
|
||||
mainHandler.removeCallbacksAndMessages(null); // 清理所有待执行的回调
|
||||
startLiveFailTimes = 0;
|
||||
isLiveStreamAlreadyStart = false;
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
isStartingRTSP = false;
|
||||
LogUtil.log(TAG, "推流状态已重置");
|
||||
}
|
||||
|
||||
|
|
@ -201,9 +203,14 @@ public class StreamManager extends BaseManager {
|
|||
}
|
||||
|
||||
|
||||
private int startLiveFailTimes;
|
||||
private boolean isLiveStreamAlreadyStart;
|
||||
private boolean isStartingRTSP = false; // 防止并发调用
|
||||
private volatile int startLiveFailTimes;
|
||||
private volatile boolean isLiveStreamAlreadyStart;
|
||||
private volatile boolean isStartingRTSP = false; // 防止并发调用
|
||||
|
||||
// 无限重试:指数退避控制
|
||||
private static final long RETRY_BASE_MS = 3000; // 初始 3 秒
|
||||
private static final long RETRY_MAX_MS = 30000; // 最大 30 秒
|
||||
private static final int LOG_INTERVAL = 10; // 每 10 次打一次日志
|
||||
|
||||
// 知眸测试
|
||||
public void startLiveWithCustom() {
|
||||
|
|
@ -247,16 +254,18 @@ public class StreamManager extends BaseManager {
|
|||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
mainHandler.post(() -> {
|
||||
LogUtil.log(TAG, "第" + startLiveFailTimes + "次开始推流失败:" + new Gson().toJson(error));
|
||||
LogUtil.log(TAG, "第" + startLiveFailTimes + "次自定义推流失败:" + new Gson().toJson(error));
|
||||
if (!isLiveStreamAlreadyStart) {
|
||||
long retryDelay = Math.min(RETRY_BASE_MS * (1L << Math.min(startLiveFailTimes, 4)), RETRY_MAX_MS);
|
||||
startLiveFailTimes++;
|
||||
if (startLiveFailTimes <= 3 || startLiveFailTimes % LOG_INTERVAL == 0) {
|
||||
LogUtil.log(TAG, "自定义推流准备第" + startLiveFailTimes + "次重试,间隔 " + retryDelay + "ms");
|
||||
}
|
||||
isStartingRTSP = false;
|
||||
isLiveStreamAlreadyStart = false;
|
||||
mainHandler.postDelayed(() -> {
|
||||
streamExecutor.execute(() -> {
|
||||
if (startLiveFailTimes < 10) {
|
||||
startLiveFailTimes++;
|
||||
startLiveWithCustom();
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
streamExecutor.execute(() -> startLiveWithCustom());
|
||||
}, retryDelay);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -321,8 +330,9 @@ public class StreamManager extends BaseManager {
|
|||
streamExecutor.execute(() -> {
|
||||
isStartingRTSP = true;
|
||||
|
||||
// 重置失败计数(每次新尝试时)
|
||||
// 每次新推流尝试:清零所有状态标志(修复 Bug 1, 2, 5)
|
||||
if (startLiveFailTimes == 0) {
|
||||
isLiveStreamAlreadyStart = false;
|
||||
LogUtil.log(TAG, "========== 开始 RTSP 推流流程 ==========");
|
||||
}
|
||||
|
||||
|
|
@ -345,8 +355,6 @@ public class StreamManager extends BaseManager {
|
|||
LogUtil.log(TAG, "RTSP 推流已在运行,无需重复启动");
|
||||
isLiveStreamAlreadyStart = true;
|
||||
isStartingRTSP = false;
|
||||
startStreamRefreshTimer(); // 推流在运行就启动定时器
|
||||
SimplePortScanner.getInstance().startScan(); // 确保端口扫描在运行
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -362,16 +370,11 @@ public class StreamManager extends BaseManager {
|
|||
|
||||
mainHandler.postDelayed(() -> {
|
||||
streamExecutor.execute(() -> {
|
||||
if (startLiveFailTimes < 3) {
|
||||
startLiveFailTimes++;
|
||||
startLiveFailTimes++;
|
||||
if (startLiveFailTimes <= 3 || startLiveFailTimes % LOG_INTERVAL == 0) {
|
||||
LogUtil.log(TAG, "相机流未准备好,第" + startLiveFailTimes + "次重试");
|
||||
isStartingRTSP = false; // 先重置标志,让重试调用能正常进入
|
||||
startLiveWithRTSP();
|
||||
} else {
|
||||
LogUtil.log(TAG, "相机流未准备好,重试次数已达上限,强制启动");
|
||||
forceStartLive();
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
}
|
||||
startLiveWithRTSP();
|
||||
});
|
||||
}, 2000);
|
||||
isStartingRTSP = false; // 释放锁,让重试能正常进入
|
||||
|
|
@ -386,7 +389,7 @@ public class StreamManager extends BaseManager {
|
|||
if (rtspUser == null || rtspPort == null || rtspPass == null ||
|
||||
rtspUser.isEmpty() || rtspPort.isEmpty() || rtspPass.isEmpty()) {
|
||||
LogUtil.log(TAG, "RTSP 配置参数有误:user=" + rtspUser + ", port=" + rtspPort);
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
isStartingRTSP = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -430,8 +433,7 @@ public class StreamManager extends BaseManager {
|
|||
if (finalLiveStreamManager.isStreaming()) {
|
||||
LogUtil.log(TAG, "推流已在运行,跳过启动");
|
||||
isLiveStreamAlreadyStart = true;
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
SimplePortScanner.getInstance().startScan(); // 确保端口扫描在运行
|
||||
isStartingRTSP = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -445,63 +447,6 @@ public class StreamManager extends BaseManager {
|
|||
|
||||
|
||||
|
||||
private void forceStartLive() {
|
||||
streamExecutor.execute(() -> {
|
||||
if (PreferenceUtils.getInstance().getRtspUserName() != null &&
|
||||
PreferenceUtils.getInstance().getRtspPort() != null &&
|
||||
PreferenceUtils.getInstance().getRtspPassWord() != null) {
|
||||
|
||||
ILiveStreamManager liveStreamManager = MediaDataCenter.getInstance().getLiveStreamManager();
|
||||
|
||||
// 直接设置参数并启动,不检查相机流状态
|
||||
mainHandler.post(() -> {
|
||||
LiveStreamSettings.Builder streamSettingBuilder = new LiveStreamSettings.Builder();
|
||||
LiveStreamSettings streamSettings = streamSettingBuilder.setLiveStreamType(LiveStreamType.RTSP)
|
||||
.setRtspSettings(new RtspSettings.Builder().setPassWord(PreferenceUtils.getInstance().getRtspPassWord()).
|
||||
setPort(Integer.parseInt(PreferenceUtils.getInstance().getRtspPort())).
|
||||
setUserName(PreferenceUtils.getInstance().getRtspUserName()).build()).build();
|
||||
|
||||
liveStreamManager.setLiveStreamSettings(streamSettings);
|
||||
liveStreamManager.setCameraIndex(isliveindex == 1 ? ComponentIndexType.PORT_1 : ComponentIndexType.FPV);
|
||||
liveStreamManager.setLiveStreamQuality(StreamQuality.FULL_HD);
|
||||
liveStreamManager.setLiveVideoBitrateMode(LiveVideoBitrateMode.AUTO);
|
||||
|
||||
// 直接启动
|
||||
liveStreamManager.startStream(new CommonCallbacks.CompletionCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
mainHandler.post(() -> {
|
||||
LogUtil.log(TAG, "强制启动 RTSP 推流成功");
|
||||
// 统一刷新视频流
|
||||
MainActivity mainActivity = MainActivity.Companion.getInstance();
|
||||
if (mainActivity != null) {
|
||||
mainActivity.smartRefreshVideoStream();
|
||||
}
|
||||
isLiveStreamAlreadyStart = true;
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
startStreamRefreshTimer(); // 强制启动成功也启动定时器
|
||||
SimplePortScanner.getInstance().startScan();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull IDJIError error) {
|
||||
mainHandler.post(() -> {
|
||||
LogUtil.log(TAG, "强制启动 RTSP 推流失败:" + new Gson().toJson(error));
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
// 失败后也尝试重置状态并重启
|
||||
resetStreamState();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
LogUtil.log(TAG, "RTSP 配置参数有误,无法强制启动");
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ========== 【新增】实际启动流的私有方法,确保在子线程执行 ==========
|
||||
private void doStartLiveWithRTSP(ILiveStreamManager liveStreamManager, boolean isRestart) {
|
||||
// 启动前再次检查状态
|
||||
|
|
@ -509,8 +454,6 @@ public class StreamManager extends BaseManager {
|
|||
LogUtil.log(TAG, "推流已在运行,跳过启动 (isRestart=" + isRestart + ")");
|
||||
isLiveStreamAlreadyStart = true;
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
startStreamRefreshTimer();
|
||||
SimplePortScanner.getInstance().startScan(); // 确保端口扫描在运行
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -540,34 +483,25 @@ public class StreamManager extends BaseManager {
|
|||
LogUtil.log(TAG, detailedError);
|
||||
LogUtil.log(TAG, "完整错误:" + new Gson().toJson(error));
|
||||
|
||||
// 错误类型判断
|
||||
if (error.hint() != null) {
|
||||
if (error.hint().contains("CAMERA")) {
|
||||
LogUtil.log(TAG, "[诊断] 相机相关错误:可能是相机模式不对/相机被占用/SD 卡异常");
|
||||
} else if (error.hint().contains("LIVE")) {
|
||||
LogUtil.log(TAG, "[诊断] 推流相关错误:可能是推流配置问题/端口被占用");
|
||||
} else if (error.hint().contains("PORT") || error.hint().contains("port")) {
|
||||
LogUtil.log(TAG, "[诊断] 端口相关错误:请检查 RTSP 端口是否被占用");
|
||||
}
|
||||
}
|
||||
|
||||
if (!isLiveStreamAlreadyStart) {
|
||||
// 延时 3 秒后重试,重试也提交到线程池
|
||||
// 计算指数退避间隔:3s → 6s → 12s → 24s → 30s(封顶)
|
||||
long retryDelay = Math.min(RETRY_BASE_MS * (1L << Math.min(startLiveFailTimes, 4)), RETRY_MAX_MS);
|
||||
startLiveFailTimes++;
|
||||
if (startLiveFailTimes <= 3 || startLiveFailTimes % LOG_INTERVAL == 0) {
|
||||
LogUtil.log(TAG, "准备第" + startLiveFailTimes + "次重试,间隔 " + retryDelay + "ms");
|
||||
}
|
||||
|
||||
// 重置所有状态,让下次重试是干净的
|
||||
isStartingRTSP = false;
|
||||
isLiveStreamAlreadyStart = false;
|
||||
stopstream(); // 先关再开,避免端口占用
|
||||
|
||||
// 指数退避重试
|
||||
mainHandler.postDelayed(() -> {
|
||||
streamExecutor.execute(() -> {
|
||||
if (startLiveFailTimes < 10) {
|
||||
startLiveFailTimes++;
|
||||
LogUtil.log(TAG, "准备第" + startLiveFailTimes + "次重试 RTSP 推流");
|
||||
startLiveWithRTSP();
|
||||
} else {
|
||||
LogUtil.log(TAG, "RTSP 推流重试次数已达上限 (10 次),放弃");
|
||||
LogUtil.log(TAG, "========== RTSP 推流失败 ==========");
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
}
|
||||
});
|
||||
}, 3000);
|
||||
startLiveWithRTSP();
|
||||
}, retryDelay);
|
||||
} else {
|
||||
isStartingRTSP = false; // 重置并发标志
|
||||
isStartingRTSP = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -385,8 +385,11 @@ public class TakeOffToPointManager extends BaseManager {
|
|||
PreferenceUtils.getInstance().setTriggerToAlternatePoint(false);
|
||||
|
||||
// 重置开舱门标志,避免上次任务未正常落地导致本次无法开舱
|
||||
FlightManager.getInstance().resetOpenCabinDoorState();
|
||||
DockOpenManager.getInstance().resetState();
|
||||
// 只在首次尝试时重置,避免重试时反复触发
|
||||
if (startMissionFailTimes == 0) {
|
||||
FlightManager.getInstance().resetOpenCabinDoorState();
|
||||
DockOpenManager.getInstance().resetState();
|
||||
}
|
||||
|
||||
PreferenceUtils.getInstance().setFlightId(message.getData().getFlight_id());
|
||||
CommonCallbacks.CompletionCallback callback = new CommonCallbacks.CompletionCallback() {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import android.os.Looper;
|
|||
import com.aros.apron.constant.AMSConfig;
|
||||
import com.aros.apron.entity.ArucoMarker;
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.entity.Synchronizedstatus;
|
||||
import com.aros.apron.manager.AlternateLandingManager;
|
||||
import com.aros.apron.manager.FlightManager;
|
||||
import com.aros.apron.tools.DroneHelper;
|
||||
import com.aros.apron.tools.LogUtil;
|
||||
import com.aros.apron.tools.PIDControl;
|
||||
|
|
@ -66,6 +68,21 @@ public class Aprondown {
|
|||
isStartAruco = startAruco;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换相机时停止检测
|
||||
*/
|
||||
public void stopDetection() {
|
||||
if (lastFuture != null && !lastFuture.isDone()) {
|
||||
lastFuture.cancel(true);
|
||||
}
|
||||
isStartAruco = false;
|
||||
startFastStick = false;
|
||||
arucoNotFoundTag = false;
|
||||
isHeightStableMonitoring = false;
|
||||
frameCounter = 0;
|
||||
LogUtil.log(TAG_LOG, "【停止检测】下视相机模块已停止");
|
||||
}
|
||||
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
|
||||
Future<?> lastFuture = null;
|
||||
private String TAG_LOG = getClass().getSimpleName();
|
||||
|
|
@ -131,12 +148,50 @@ public class Aprondown {
|
|||
pidControlX.reset();
|
||||
pidControlY.reset();
|
||||
}
|
||||
|
||||
public boolean startFastStick;
|
||||
public boolean canLanding;
|
||||
|
||||
// ========== 【核心修改】主入口:2帧一处理 + 预处理回退 ==========
|
||||
public void detectForceTriggerTags(int height, int width, byte[] data, Dictionary dictionary) {
|
||||
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
|
||||
yuvMat.put(0, 0, data);
|
||||
Mat grayImgMat = yuvMat.submat(0, height, 0, width);
|
||||
grayImgMat = grayImgMat.clone();
|
||||
|
||||
MatOfInt ids = new MatOfInt();
|
||||
List<Mat> corners = new ArrayList<>();
|
||||
DetectorParameters parameters = new DetectorParameters();
|
||||
ArucoDetector detector = new ArucoDetector(dictionary, parameters);
|
||||
|
||||
detector.detectMarkers(grayImgMat, corners, ids);
|
||||
|
||||
if (!ids.empty()) {
|
||||
int[] idArray = ids.toArray();
|
||||
double relativeHeight = Movement.getInstance().getElevation();
|
||||
boolean has6 = false;
|
||||
|
||||
for (int id : idArray) {
|
||||
if (id == 6) {
|
||||
has6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has6 && relativeHeight < 20) {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】高度=" + relativeHeight + ",检测到6号,满足条件");
|
||||
FlightManager.forceTriggerDetection = true;
|
||||
} else {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】不满足条件:高度=" + relativeHeight + ", 6号=" + has6);
|
||||
}
|
||||
}
|
||||
|
||||
yuvMat.release();
|
||||
grayImgMat.release();
|
||||
ids.release();
|
||||
}
|
||||
|
||||
|
||||
public void detectArucoTags(int height, int width, byte[] data, Dictionary dictionary) {
|
||||
if (Synchronizedstatus.isAprongim()) return;
|
||||
isTriggerSuccess = true;
|
||||
Movement.getInstance().setVirtualStickEnableReason(2);
|
||||
|
||||
|
|
@ -149,12 +204,12 @@ public class Aprondown {
|
|||
return;
|
||||
}
|
||||
|
||||
// ========== 【关键】2帧一处理:0处理-1跳过-2处理-3跳过 ==========
|
||||
|
||||
int currentFrame = frameCounter++;
|
||||
boolean shouldProcess = (currentFrame % 2 == 0); // 偶数帧处理,奇数帧跳过
|
||||
|
||||
if (!shouldProcess) {
|
||||
// 【关键】跳过的帧:啥也不干,直接return,让飞机自己飘
|
||||
// 让飞机自己飘
|
||||
LogUtil.log(TAG_LOG, "【跳过帧】" + currentFrame + " 让飞机自稳");
|
||||
return;
|
||||
}
|
||||
|
|
@ -168,7 +223,7 @@ public class Aprondown {
|
|||
lastFuture.cancel(true);
|
||||
}
|
||||
|
||||
// 动态偏移(原有)
|
||||
// 动态偏移
|
||||
int ultHeight = Movement.getInstance().getUltrasonicHeight();
|
||||
if (ultHeight >= 30) { LENS_OFFSET_X = 0; LENS_OFFSET_Y = 0; }
|
||||
else if (ultHeight >= 20) { LENS_OFFSET_X = 0; LENS_OFFSET_Y = 0; }
|
||||
|
|
@ -176,7 +231,7 @@ public class Aprondown {
|
|||
else if (ultHeight >= 5) { LENS_OFFSET_X = 30; LENS_OFFSET_Y = 20; }
|
||||
else { LENS_OFFSET_X = 55; LENS_OFFSET_Y = 35; }
|
||||
|
||||
// ========== 【核心逻辑】先预处理检测,失败回退原图 ==========
|
||||
|
||||
lastFuture = executor.schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
@ -195,12 +250,12 @@ public class Aprondown {
|
|||
DetectorParameters parameters = new DetectorParameters();
|
||||
ArucoDetector detector = new ArucoDetector(dictionary, parameters);
|
||||
|
||||
// ========== 【关键修改】第1次:预处理图检测 ==========
|
||||
|
||||
Mat processedMat = fixedPreprocess(grayImgMat);
|
||||
detector.detectMarkers(processedMat, corners, ids);
|
||||
LogUtil.log(TAG_LOG, "预处理图检测: " + (ids.empty() ? "未检出" : "成功,数量=" + corners.size()));
|
||||
|
||||
// ========== 【关键修改】第2次:如果失败,回退原图检测 ==========
|
||||
|
||||
if (ids.empty()) {
|
||||
LogUtil.log(TAG_LOG, "预处理失败,回退原图检测");
|
||||
corners.clear(); // 清空之前的结果
|
||||
|
|
@ -208,17 +263,17 @@ public class Aprondown {
|
|||
LogUtil.log(TAG_LOG, "原图检测: " + (ids.empty() ? "仍未检出" : "成功,数量=" + corners.size()));
|
||||
}
|
||||
|
||||
// 释放预处理图(必须!避免内存泄漏)
|
||||
|
||||
processedMat.release();
|
||||
|
||||
// 只保留6号码
|
||||
|
||||
ids = keepOnly6(ids, corners);
|
||||
|
||||
boolean marker6Found = false;
|
||||
|
||||
int ultrasonicHeight = Movement.getInstance().getUltrasonicHeight();
|
||||
|
||||
// ========== 【新增】高度稳定性监测 ==========
|
||||
|
||||
if (ultrasonicHeight > 0) {
|
||||
if (!isHeightStableMonitoring) {
|
||||
// 开始监测
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ import android.os.Looper;
|
|||
import com.aros.apron.constant.AMSConfig;
|
||||
import com.aros.apron.entity.ArucoMarker;
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.entity.Synchronizedstatus;
|
||||
import com.aros.apron.manager.AlternateLandingManager;
|
||||
import com.aros.apron.manager.FlightManager;
|
||||
import com.aros.apron.tools.DroneHelper;
|
||||
import com.aros.apron.tools.LogUtil;
|
||||
import com.aros.apron.tools.PIDControl;
|
||||
|
|
@ -115,6 +117,22 @@ public class Aprongim {
|
|||
public void setStartAruco(boolean startAruco) {
|
||||
isStartAruco = startAruco;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换相机时停止检测
|
||||
*/
|
||||
public void stopDetection() {
|
||||
if (lastFuture != null && !lastFuture.isDone()) {
|
||||
lastFuture.cancel(true);
|
||||
}
|
||||
isStartAruco = false;
|
||||
startFastStick = false;
|
||||
arucoNotFoundTag = false;
|
||||
isHeightStableMonitoring = false;
|
||||
frameCounter = 0;
|
||||
LogUtil.log(TAG_LOG, "【停止检测】云台相机模块已停止");
|
||||
}
|
||||
|
||||
public void setCanLanding(boolean v) {
|
||||
startTime = 0;
|
||||
endTime = 0;
|
||||
|
|
@ -155,8 +173,50 @@ public class Aprongim {
|
|||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
public void detectForceTriggerTags(int height, int width, byte[] data, Dictionary dictionary) {
|
||||
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
|
||||
yuvMat.put(0, 0, data);
|
||||
Mat grayImgMat = yuvMat.submat(0, height, 0, width);
|
||||
grayImgMat = grayImgMat.clone();
|
||||
|
||||
MatOfInt ids = new MatOfInt();
|
||||
List<Mat> corners = new ArrayList<>();
|
||||
DetectorParameters parameters = new DetectorParameters();
|
||||
ArucoDetector detector = new ArucoDetector(dictionary, parameters);
|
||||
|
||||
detector.detectMarkers(grayImgMat, corners, ids);
|
||||
|
||||
if (!ids.empty()) {
|
||||
int ultHeight = Movement.getInstance().getUltrasonicHeight();
|
||||
int[] idArray = ids.toArray();
|
||||
boolean has6 = false;
|
||||
int count1to4 = 0;
|
||||
|
||||
for (int id : idArray) {
|
||||
if (id == 6) {
|
||||
has6 = true;
|
||||
} else if (id >= 1 && id <= 4) {
|
||||
count1to4++;
|
||||
}
|
||||
}
|
||||
|
||||
if (has6 && count1to4 >= 3 && ultHeight <= 60) {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】满足条件:6号 + 1-4号中" + count1to4 + "个 高度=" + ultHeight + "dm");
|
||||
FlightManager.forceTriggerDetection = true;
|
||||
} else {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】不满足条件:6号=" + has6 + ", 1-4号数量=" + count1to4 + ", 高度=" + ultHeight + "dm(阈值80)");
|
||||
}
|
||||
}
|
||||
|
||||
yuvMat.release();
|
||||
grayImgMat.release();
|
||||
ids.release();
|
||||
}
|
||||
|
||||
// ========== 【核心】主入口:3帧一处理 ==========
|
||||
public void detectArucoTags(int height, int width, byte[] data, Dictionary dictionary) {
|
||||
if (!Synchronizedstatus.isAprongim()) return;
|
||||
isTriggerSuccess = true;
|
||||
Movement.getInstance().setVirtualStickEnableReason(2);
|
||||
|
||||
|
|
@ -250,7 +310,7 @@ public class Aprongim {
|
|||
// 高度有明显变化,重置计时器
|
||||
lastHeightCheckTime = currentTime;
|
||||
lastUltrasonicHeight = ultHeight;
|
||||
// LogUtil.log(TAG_LOG, "高度变化: " + heightChange + " 分米,重置计时器");
|
||||
// LogUtil.log(TAG_LOG, "高度变化: " + heightChange + " 分米,重置计时器");
|
||||
} else if (currentTime - lastHeightCheckTime > HEIGHT_STABLE_THRESHOLD) {
|
||||
// 45秒内高度没有变动,执行拉高切换
|
||||
LogUtil.log(TAG_LOG, "45秒内高度未变动,执行拉高切换到下视觉");
|
||||
|
|
|
|||
|
|
@ -33,9 +33,11 @@ public class Apronmixvalue {
|
|||
|
||||
public void switchthemmodefpv(){
|
||||
Synchronizedstatus.setAprongim(false);
|
||||
Aprongim.getInstance().stopDetection();
|
||||
}
|
||||
public void switchthemmodeGime(){
|
||||
Synchronizedstatus.setAprongim(true);
|
||||
Aprondown.getInstance().stopDetection();
|
||||
}
|
||||
|
||||
public boolean isIsaglinetrue() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
package com.aros.apron.tools;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.aros.apron.constant.AMSConfig;
|
||||
import com.aros.apron.entity.ArucoMarker;
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.manager.AlternateLandingManager;
|
||||
import com.aros.apron.manager.FlightManager;
|
||||
|
||||
import org.opencv.calib3d.Calib3d;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
|
|
@ -17,11 +19,13 @@ import org.opencv.core.MatOfPoint2f;
|
|||
import org.opencv.core.Point;
|
||||
import org.opencv.core.Scalar;
|
||||
import org.opencv.core.Size;
|
||||
import org.opencv.imgcodecs.Imgcodecs;
|
||||
import org.opencv.imgproc.Imgproc;
|
||||
import org.opencv.objdetect.ArucoDetector;
|
||||
import org.opencv.objdetect.DetectorParameters;
|
||||
import org.opencv.objdetect.Dictionary;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -93,6 +97,19 @@ public class ApronArucoDetect {
|
|||
private static final double HEIGHT_CHANGE_THRESHOLD =1; // 10厘米
|
||||
private boolean isHeightStableMonitoring = false;
|
||||
|
||||
// ========== 【新增】强光预处理动态回退 ==========
|
||||
// 连续"检测到轮廓但无法解码"的次数
|
||||
private int undecodableFrameCount = 0;
|
||||
private static final int UNDECODABLE_THRESHOLD = 5; // 连续5帧解码失败则跳过预处理
|
||||
private boolean forceSkipPreprocess = false; // 强制跳过预处理标志
|
||||
private int skipPreprocessFrameCount = 0; // 强制跳过预处理的持续帧数
|
||||
private static final int SKIP_PREPROCESS_DURATION = 30; // 强制跳过30帧后恢复尝试预处理
|
||||
|
||||
// ========== 【新增】识别失败截图保存 ==========
|
||||
private boolean saveFailScreenshot = false; // 是否开启识别失败截图保存
|
||||
private int failScreenshotIndex = 0; // 截图文件序号
|
||||
private static final int MAX_SCREENSHOTS = 50; // 最多保存50张截图
|
||||
|
||||
// ========== 原有方法 ==========
|
||||
public int getCheckThrowingErrorsTimes() { return checkThrowingErrorsTimes; }
|
||||
public void setCheckThrowingErrorsTimes(int v) { checkThrowingErrorsTimes = v; }
|
||||
|
|
@ -100,6 +117,8 @@ public class ApronArucoDetect {
|
|||
public void setTriggerSuccess(boolean v) { isTriggerSuccess = v; }
|
||||
public boolean isDoublePayload() { return isDoublePayload; }
|
||||
public void setDoublePayload(boolean v) { isDoublePayload = v; }
|
||||
public boolean isSaveFailScreenshot() { return saveFailScreenshot; }
|
||||
public void setSaveFailScreenshot(boolean v) { saveFailScreenshot = v; }
|
||||
public boolean isStartFastStick() { return startFastStick; }
|
||||
public void setStartFastStick(boolean v) { startFastStick = v; }
|
||||
public boolean isCanLanding() { return canLanding; }
|
||||
|
|
@ -174,10 +193,12 @@ public class ApronArucoDetect {
|
|||
try {
|
||||
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
|
||||
yuvMat.put(0, 0, data);
|
||||
Mat rgbMat = new Mat();
|
||||
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2BGR_I420);
|
||||
Mat grayImgMat = new Mat();
|
||||
Imgproc.cvtColor(rgbMat, grayImgMat, Imgproc.COLOR_BGR2GRAY);
|
||||
|
||||
// ========== 【关键修改】直接用Y通道,跳过RGB转换 ==========
|
||||
// YUV420的Y通道就是灰度图,质量比YUV→BGR→GRAY高很多
|
||||
// BGR转换需要插值UV通道,在低分辨率FPV上损失细节
|
||||
Mat grayImgMat = yuvMat.submat(0, height, 0, width);
|
||||
grayImgMat = grayImgMat.clone(); // 克隆,避免和yuvMat共享数据
|
||||
|
||||
MatOfInt ids = new MatOfInt();
|
||||
List<Mat> corners = new ArrayList<>();
|
||||
|
|
@ -186,21 +207,67 @@ public class ApronArucoDetect {
|
|||
DetectorParameters parameters = new DetectorParameters();
|
||||
ArucoDetector detector = new ArucoDetector(dictionary, parameters);
|
||||
|
||||
// ========== 【关键修改】第1次:预处理图检测 ==========
|
||||
Mat processedMat = fixedPreprocess(grayImgMat);
|
||||
detector.detectMarkers(processedMat, corners, ids);
|
||||
LogUtil.log(TAG_LOG, "预处理图检测: " + (ids.empty() ? "未检出" : "成功,数量=" + corners.size()));
|
||||
// ========== 【关键修改】计算亮度,决定预处理策略 ==========
|
||||
Scalar meanValue = Core.mean(grayImgMat);
|
||||
double currentBrightness = meanValue.val[0];
|
||||
|
||||
// ========== 【关键修改】第2次:如果失败,回退原图检测 ==========
|
||||
if (ids.empty()) {
|
||||
// ========== 【关键修改】第1次:预处理图检测 ==========
|
||||
boolean shouldUsePreprocess = !forceSkipPreprocess;
|
||||
Mat processedMat;
|
||||
boolean preprocessed = false;
|
||||
|
||||
if (shouldUsePreprocess) {
|
||||
processedMat = fixedPreprocess(grayImgMat, currentBrightness);
|
||||
preprocessed = true;
|
||||
} else {
|
||||
processedMat = grayImgMat;
|
||||
LogUtil.log(TAG_LOG, "【强光回退】连续解码失败,跳过预处理,使用原图(" + (int)currentBrightness + ")");
|
||||
}
|
||||
|
||||
detector.detectMarkers(processedMat, corners, ids);
|
||||
LogUtil.log(TAG_LOG, (preprocessed ? "预处理图检测" : "原图检测(回退)") +
|
||||
": " + (ids.empty() ? "未检出" : "成功,数量=" + corners.size()));
|
||||
|
||||
// ========== 【关键修改】第2次:如果预处理失败,回退原图检测 ==========
|
||||
if (ids.empty() && preprocessed) {
|
||||
LogUtil.log(TAG_LOG, "预处理失败,回退原图检测");
|
||||
corners.clear(); // 清空之前的结果
|
||||
corners.clear();
|
||||
detector.detectMarkers(grayImgMat, corners, ids);
|
||||
LogUtil.log(TAG_LOG, "原图检测: " + (ids.empty() ? "仍未检出" : "成功,数量=" + corners.size()));
|
||||
}
|
||||
|
||||
// 释放预处理图(必须!避免内存泄漏)
|
||||
processedMat.release();
|
||||
if (preprocessed && processedMat != null) {
|
||||
processedMat.release();
|
||||
}
|
||||
|
||||
// 【新增】追踪连续解码失败帧
|
||||
if (ids.empty()) {
|
||||
undecodableFrameCount++;
|
||||
if (undecodableFrameCount >= UNDECODABLE_THRESHOLD && !forceSkipPreprocess) {
|
||||
forceSkipPreprocess = true;
|
||||
skipPreprocessFrameCount = 0;
|
||||
LogUtil.log(TAG_LOG, "【强光检测】连续" + UNDECODABLE_THRESHOLD + "帧解码失败,强制跳过预处理" +
|
||||
"(当前亮度=" + (int)currentBrightness + ")");
|
||||
}
|
||||
} else {
|
||||
undecodableFrameCount = 0;
|
||||
}
|
||||
|
||||
// 【新增】强制跳过预处理模式:持续一定帧数后恢复尝试
|
||||
if (forceSkipPreprocess) {
|
||||
skipPreprocessFrameCount++;
|
||||
if (skipPreprocessFrameCount >= SKIP_PREPROCESS_DURATION) {
|
||||
forceSkipPreprocess = false;
|
||||
undecodableFrameCount = 0;
|
||||
LogUtil.log(TAG_LOG, "【强光检测】恢复预处理尝试,观察效果");
|
||||
}
|
||||
}
|
||||
|
||||
// 【新增】高亮度曝光补偿提示
|
||||
if (currentBrightness > 130) {
|
||||
LogUtil.log(TAG_LOG, "【曝光提示】亮度过高(" + (int)currentBrightness + "),建议降低EV或锁定AE");
|
||||
}
|
||||
|
||||
// 只保留6号码
|
||||
ids = keepOnly6(ids, corners);
|
||||
|
|
@ -273,9 +340,9 @@ public class ApronArucoDetect {
|
|||
double pixelWidth = calculateDistance(points[0], points[1]);
|
||||
|
||||
double centerX = (points[0].x + points[1].x + points[2].x + points[3].x) / 4.0 + LENS_OFFSET_X;
|
||||
double errX = Math.abs(centerX - rgbMat.width() / 2.0);
|
||||
double errX = Math.abs(centerX - width / 2.0);
|
||||
double centerY = (points[0].y + points[1].y + points[2].y + points[3].y) / 4.0 + LENS_OFFSET_Y;
|
||||
double errY = Math.abs(centerY - rgbMat.height() / 2.0);
|
||||
double errY = Math.abs(centerY - height / 2.0);
|
||||
|
||||
LogUtil.log(TAG_LOG, "像素" + (int) pixelWidth +
|
||||
" errX=" + (int) errX + " errY=" + (int) errY + " (含偏移" + LENS_OFFSET_X + ")");
|
||||
|
|
@ -318,13 +385,19 @@ public class ApronArucoDetect {
|
|||
|
||||
// 原有修正逻辑
|
||||
if (marker6Found) {
|
||||
moveOnArucoDetected(new ArucoMarker(1, corner6, 0.24f), rgbMat.width(), rgbMat.height());
|
||||
moveOnArucoDetected(new ArucoMarker(1, corner6, 0.24f), width, height);
|
||||
}
|
||||
dropTimesTag = true;
|
||||
|
||||
} else {
|
||||
// 原有丢失处理
|
||||
LogUtil.log(TAG_LOG, "找不到了二维码");
|
||||
|
||||
// 【新增】识别失败截图保存
|
||||
if (saveFailScreenshot) {
|
||||
saveFailScreenshot(grayImgMat, width, height);
|
||||
|
||||
}
|
||||
if (!arucoNotFoundTag) {
|
||||
startTime = System.currentTimeMillis();
|
||||
arucoNotFoundTag = true;
|
||||
|
|
@ -359,7 +432,6 @@ public class ApronArucoDetect {
|
|||
|
||||
// 释放资源
|
||||
grayImgMat.release();
|
||||
rgbMat.release();
|
||||
yuvMat.release();
|
||||
ids.release();
|
||||
|
||||
|
|
@ -395,13 +467,108 @@ public class ApronArucoDetect {
|
|||
return new MatOfInt(newIds);
|
||||
}
|
||||
|
||||
private Mat fixedPreprocess(Mat src) {
|
||||
/**
|
||||
* 识别失败时保存原图截图到外部存储
|
||||
*/
|
||||
private void saveFailScreenshot(Mat grayMat, int width, int height) {
|
||||
if (failScreenshotIndex >= MAX_SCREENSHOTS) {
|
||||
LogUtil.log(TAG_LOG, "【截图保存】已达到最大数量 " + MAX_SCREENSHOTS + ",停止保存");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String dirPath = getScreenshotDirPath();
|
||||
if (dirPath == null) {
|
||||
LogUtil.log(TAG_LOG, "【截图保存】无法获取存储目录");
|
||||
return;
|
||||
}
|
||||
|
||||
String fileName = String.format("aruco_fail_%03d_%d.png",
|
||||
failScreenshotIndex, System.currentTimeMillis());
|
||||
String fullPath = dirPath + "/" + fileName;
|
||||
|
||||
boolean success = Imgcodecs.imwrite(fullPath, grayMat);
|
||||
if (success) {
|
||||
failScreenshotIndex++;
|
||||
LogUtil.log(TAG_LOG, "【截图保存】已保存: " + fileName +
|
||||
" (亮度=" + (int) Core.mean(grayMat).val[0] + ")");
|
||||
} else {
|
||||
LogUtil.log(TAG_LOG, "【截图保存】保存失败: " + fullPath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.log(TAG_LOG, "【截图保存】异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取截图保存目录
|
||||
*/
|
||||
private String getScreenshotDirPath() {
|
||||
File storageDir;
|
||||
if (checkSDCard()) {
|
||||
storageDir = new File(Environment.getExternalStorageDirectory(), "apronPic/aruco_fail_screenshots");
|
||||
} else {
|
||||
storageDir = new File(Environment.getExternalStorageDirectory().getParentFile(), "apronPic/aruco_fail_screenshots");
|
||||
}
|
||||
|
||||
if (!storageDir.exists()) {
|
||||
boolean created = storageDir.mkdirs();
|
||||
if (!created) {
|
||||
LogUtil.log(TAG_LOG, "【截图保存】创建目录失败: " + storageDir.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return storageDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
private boolean checkSDCard() {
|
||||
return TextUtils.equals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
|
||||
}
|
||||
|
||||
/**
|
||||
* startArucoType == 3 专用:扫到6号 + 相对高度小于20才触发刹停
|
||||
*/
|
||||
public void detectForceTriggerTags(int height, int width, byte[] data, Dictionary dictionary) {
|
||||
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
|
||||
yuvMat.put(0, 0, data);
|
||||
Mat grayImgMat = yuvMat.submat(0, height, 0, width);
|
||||
grayImgMat = grayImgMat.clone();
|
||||
|
||||
MatOfInt ids = new MatOfInt();
|
||||
List<Mat> corners = new ArrayList<>();
|
||||
DetectorParameters parameters = new DetectorParameters();
|
||||
ArucoDetector detector = new ArucoDetector(dictionary, parameters);
|
||||
|
||||
detector.detectMarkers(grayImgMat, corners, ids);
|
||||
|
||||
if (!ids.empty()) {
|
||||
int[] idArray = ids.toArray();
|
||||
double relativeHeight = Movement.getInstance().getElevation();
|
||||
boolean has6 = false;
|
||||
|
||||
for (int id : idArray) {
|
||||
if (id == 6) {
|
||||
has6 = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has6 && relativeHeight < 20) {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】高度=" + relativeHeight + ",检测到6号,满足条件");
|
||||
FlightManager.forceTriggerDetection = true;
|
||||
} else {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】不满足条件:高度=" + relativeHeight + ", 6号=" + has6);
|
||||
}
|
||||
}
|
||||
|
||||
yuvMat.release();
|
||||
grayImgMat.release();
|
||||
ids.release();
|
||||
}
|
||||
|
||||
private Mat fixedPreprocess(Mat src, double meanBrightness) {
|
||||
Mat result = new Mat();
|
||||
try {
|
||||
// 【新增】判断亮度,区分白天/夜景
|
||||
Scalar meanValue = Core.mean(src);
|
||||
double meanBrightness = meanValue.val[0];
|
||||
|
||||
if (meanBrightness < 80) { // 夜景模式(FPV晚上通常<80)
|
||||
LogUtil.log(TAG_LOG, "【夜景预处理】亮度=" + (int)meanBrightness);
|
||||
|
||||
|
|
@ -442,34 +609,58 @@ public class ApronArucoDetect {
|
|||
return result;
|
||||
}
|
||||
|
||||
// 【原逻辑】白天模式(亮度>=80)
|
||||
// ========== 【关键修改】白天模式:分亮度区间处理 ==========
|
||||
LogUtil.log(TAG_LOG, "【白天预处理】亮度=" + (int)meanBrightness);
|
||||
|
||||
// Step 1: 双边滤波(保边缘降噪)
|
||||
// Step 1: 双边滤波(保边缘降噪)— 所有白天场景通用
|
||||
Mat filtered = new Mat();
|
||||
Imgproc.bilateralFilter(src, filtered, 9, 75, 75);
|
||||
|
||||
// Step 2: CLAHE 局部对比度增强
|
||||
Mat clahe = new Mat();
|
||||
Imgproc.createCLAHE(4.0, new Size(8, 8)).apply(filtered, clahe);
|
||||
Mat clahe;
|
||||
double amount;
|
||||
|
||||
if (meanBrightness > 130) {
|
||||
// 【强光模式】亮度>130:大幅降低CLAHE,避免眩光被增强成伪marker
|
||||
LogUtil.log(TAG_LOG, "【强光处理】亮度过高,降低CLAHE增益,跳过锐化");
|
||||
clahe = new Mat();
|
||||
// clipLimit降到1.5(默认4.0),tileSize加大到16x16(更平滑)
|
||||
Imgproc.createCLAHE(1.5, new Size(16, 16)).apply(filtered, clahe);
|
||||
amount = 0; // 强光下跳过锐化,避免放大眩光边缘
|
||||
} else if (meanBrightness > 100) {
|
||||
// 【中亮模式】亮度100-130:适度CLAHE,轻度锐化
|
||||
LogUtil.log(TAG_LOG, "【中亮处理】适度CLAHE+轻度锐化");
|
||||
clahe = new Mat();
|
||||
Imgproc.createCLAHE(2.5, new Size(8, 8)).apply(filtered, clahe);
|
||||
amount = 0.8;
|
||||
} else {
|
||||
// 【正常白天】亮度80-100:原逻辑
|
||||
clahe = new Mat();
|
||||
Imgproc.createCLAHE(4.0, new Size(8, 8)).apply(filtered, clahe);
|
||||
amount = 1.2;
|
||||
}
|
||||
|
||||
filtered.release();
|
||||
|
||||
// Step 3: Unsharp Mask(反锐化掩膜)
|
||||
Mat blurred = new Mat();
|
||||
Imgproc.GaussianBlur(clahe, blurred, new Size(0, 0), 2.0);
|
||||
// Step 3: Unsharp Mask(反锐化掩膜)— 强光模式跳过
|
||||
if (amount > 0) {
|
||||
Mat blurred = new Mat();
|
||||
Imgproc.GaussianBlur(clahe, blurred, new Size(0, 0), 2.0);
|
||||
|
||||
Mat detail = new Mat();
|
||||
Core.subtract(clahe, blurred, detail);
|
||||
Mat detail = new Mat();
|
||||
Core.subtract(clahe, blurred, detail);
|
||||
|
||||
double amount = 1.2; // 白天可以用1.2
|
||||
Core.multiply(detail, new Scalar(amount), detail);
|
||||
Core.add(clahe, detail, result);
|
||||
Core.multiply(detail, new Scalar(amount), detail);
|
||||
Core.add(clahe, detail, result);
|
||||
|
||||
blurred.release();
|
||||
detail.release();
|
||||
} else {
|
||||
clahe.copyTo(result);
|
||||
}
|
||||
|
||||
Core.min(result, new Scalar(255), result);
|
||||
Core.max(result, new Scalar(0), result);
|
||||
|
||||
blurred.release();
|
||||
detail.release();
|
||||
clahe.release();
|
||||
|
||||
LogUtil.log(TAG_LOG, "白天处理完成:双边滤波+CLAHE+UnsharpMask(amount=" + amount + ")");
|
||||
|
|
@ -497,6 +688,13 @@ public class ApronArucoDetect {
|
|||
isHeightStableMonitoring = false;
|
||||
lastHeightCheckTime = 0;
|
||||
lastUltrasonicHeight = 0;
|
||||
// 【新增】重置强光回退状态
|
||||
undecodableFrameCount = 0;
|
||||
forceSkipPreprocess = false;
|
||||
skipPreprocessFrameCount = 0;
|
||||
// 【新增】重置截图保存状态
|
||||
saveFailScreenshot = false;
|
||||
failScreenshotIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
package com.aros.apron.tools;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.aros.apron.constant.AMSConfig;
|
||||
import com.aros.apron.entity.ArucoMarker;
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.manager.AlternateLandingManager;
|
||||
import com.aros.apron.manager.FlightManager;
|
||||
|
||||
import org.opencv.core.CvType;
|
||||
import org.opencv.core.Mat;
|
||||
|
|
@ -43,8 +46,6 @@ public class ApronArucoDetectPort {
|
|||
private static final float SLOW_LAND_SPEED = -0.3f;
|
||||
private static final float SLOW_SUPER_SPEED = -0.15f;
|
||||
private static final int PIXEL_TRIGGER = 360;
|
||||
private static final int TRIGGER_FRAME_THRESHOLD = 2;
|
||||
|
||||
// ========== 原有状态 ==========
|
||||
private boolean isTriggerSuccess;
|
||||
private boolean arucoNotFoundTag = false;
|
||||
|
|
@ -64,7 +65,6 @@ public class ApronArucoDetectPort {
|
|||
public PIDControl pidControlX = null;
|
||||
public PIDControl pidControlY = null;
|
||||
public int checkThrowingErrorsTimes;
|
||||
private int consecutiveTriggerCount = 0;
|
||||
private int frameCounter = 0;
|
||||
|
||||
// ========== 【新增】阶段控制标志 ==========
|
||||
|
|
@ -73,9 +73,9 @@ public class ApronArucoDetectPort {
|
|||
// 当前激活的降落模式:0=旋转阶段(6号), 1=小码, 2=1-4大码, 3=6号纯下降
|
||||
private int currentLandingMode = 0;
|
||||
|
||||
// ========== 【新增】连续降落条件判断 ==========
|
||||
// 记录上一帧满足的降落条件类型:0=无, 1=单码精准降落(15/17号), 2=几何中心降落
|
||||
private int lastLandingCondition = 0;
|
||||
// ========== 【新增】6个独立分支计数器 ==========
|
||||
private int[] landingCounters = new int[22]; // ID作为索引
|
||||
private int counterRound = 0;
|
||||
|
||||
// ========== 【新增】高度稳定性监测 ==========
|
||||
private long lastHeightCheckTime = 0;
|
||||
|
|
@ -177,19 +177,19 @@ public class ApronArucoDetectPort {
|
|||
if(isDoublePayload){
|
||||
if (ultHeight <=5)
|
||||
{
|
||||
LENS_OFFSET_X=-130;
|
||||
LENS_OFFSET_X=-180;
|
||||
LENS_OFFSET_Y=120;
|
||||
}else if(ultHeight<10){
|
||||
LENS_OFFSET_X=-90;
|
||||
LENS_OFFSET_X=-120;
|
||||
LENS_OFFSET_Y=80;
|
||||
} else if (ultHeight<20) {
|
||||
LENS_OFFSET_X=-70;
|
||||
LENS_OFFSET_X=-80;
|
||||
LENS_OFFSET_Y=60;
|
||||
}
|
||||
}else{
|
||||
if (ultHeight <=5)
|
||||
{
|
||||
LENS_OFFSET_X=120;
|
||||
LENS_OFFSET_X=100;
|
||||
LENS_OFFSET_Y = 100;
|
||||
}else if(ultHeight<10){
|
||||
LENS_OFFSET_X=80;
|
||||
|
|
@ -207,10 +207,9 @@ public class ApronArucoDetectPort {
|
|||
// ========== 图像预处理(原有) ==========
|
||||
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
|
||||
yuvMat.put(0, 0, data);
|
||||
Mat rgbMat = new Mat();
|
||||
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2BGR_I420);
|
||||
Mat grayImgMat = new Mat();
|
||||
Imgproc.cvtColor(rgbMat, grayImgMat, Imgproc.COLOR_BGR2GRAY);
|
||||
// ========== 【关键修改】直接用Y通道,跳过RGB转换 ==========
|
||||
Mat grayImgMat = yuvMat.submat(0, height, 0, width);
|
||||
grayImgMat = grayImgMat.clone();
|
||||
|
||||
MatOfInt ids = new MatOfInt();
|
||||
List<Mat> corners = new ArrayList<>();
|
||||
|
|
@ -317,24 +316,24 @@ public class ApronArucoDetectPort {
|
|||
// 只要看到6号且还没对准,就只用6号旋转,不进入其他任何阶段
|
||||
if (!bigMarker6.isEmpty() && !isYawAligned) {
|
||||
currentLandingMode = 0;
|
||||
processMarker6YawOnly(bigMarker6.get(0), rgbMat.width(), rgbMat.height(), ultHeight);
|
||||
processMarker6YawOnly(bigMarker6.get(0), width, height, ultHeight);
|
||||
}
|
||||
// ========== 【关键】只有旋转完成后(isYawAligned=true),才识别其他码 ==========
|
||||
else if (isYawAligned) {
|
||||
// 优先级1:小码模式(高度<25dm且≥3个码)
|
||||
if (!smallMarkers.isEmpty() && ultHeight < 25) {
|
||||
currentLandingMode = 1;
|
||||
processSmallMarkers(smallMarkers, rgbMat.width(), rgbMat.height(), ultHeight);
|
||||
processSmallMarkers(smallMarkers, width, height, ultHeight);
|
||||
}
|
||||
// 优先级2:1-4号几何中心下降
|
||||
else if (!bigMarkers1_4.isEmpty()) {
|
||||
currentLandingMode = 2;
|
||||
processBigMarkersCenter(bigMarkers1_4, rgbMat.width(), rgbMat.height(), ultHeight);
|
||||
processBigMarkersCenter(bigMarkers1_4, width, height, ultHeight);
|
||||
}
|
||||
// 优先级3:6号纯下降(旋转完成后,没看到1-4和小码时)
|
||||
else if (!bigMarker6.isEmpty()) {
|
||||
currentLandingMode = 3;
|
||||
processLandingOnly(bigMarker6.get(0), rgbMat.width(), rgbMat.height(), ultHeight);
|
||||
processLandingOnly(bigMarker6.get(0), width, height, ultHeight);
|
||||
}
|
||||
// 旋转完成后丢失其他码,但还有6号:继续用6号下降
|
||||
else {
|
||||
|
|
@ -350,7 +349,7 @@ public class ApronArucoDetectPort {
|
|||
handleLostMarker(ultHeight);
|
||||
}
|
||||
grayImgMat.release();
|
||||
rgbMat.release();
|
||||
grayImgMat.release();
|
||||
yuvMat.release();
|
||||
ids.release();
|
||||
if (!corners.isEmpty()) {
|
||||
|
|
@ -459,92 +458,209 @@ public class ApronArucoDetectPort {
|
|||
double absY = Math.abs(offsetY);
|
||||
double z = ultHeight / 10.0;
|
||||
|
||||
// 降落条件判断(连续帧)
|
||||
// ========== 【6个独立分支计数器降落判定】 ==========
|
||||
boolean isSpecialLanding = false;
|
||||
double singleErrX = 0, singleErrY = 0;
|
||||
|
||||
// 单挂模式:检查15号
|
||||
if (!isDoublePayload && target15 != null && ultHeight <= 4) {
|
||||
Mat corner = target15.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double[] p = corner.get(0, i);
|
||||
cx += p[0];
|
||||
cy += p[1];
|
||||
if (ultHeight <= 4) {
|
||||
// 当前帧可见码的Map
|
||||
java.util.HashMap<Integer, ArucoMarker> markerMap = new java.util.HashMap<>();
|
||||
for (ArucoMarker m : markers) {
|
||||
markerMap.put(m.getId(), m);
|
||||
}
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
singleErrX = cx - imgWidth / 2.0 - LENS_OFFSET_X;
|
||||
singleErrY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
|
||||
if (Math.abs(singleErrX) < 60 && Math.abs(singleErrY) < 60) {
|
||||
isSpecialLanding = true;
|
||||
offsetX = singleErrX;
|
||||
offsetY = singleErrY;
|
||||
absX = Math.abs(singleErrX);
|
||||
absY = Math.abs(singleErrY);
|
||||
boolean foundLanding = false;
|
||||
int landingId = 0;
|
||||
|
||||
// ===== 单挂模式:13 15 17 14 16 18 =====
|
||||
if (!isDoublePayload) {
|
||||
// ---- 13号 ----
|
||||
ArucoMarker marker13 = markerMap.get(13);
|
||||
if (marker13 != null) {
|
||||
Mat corner = marker13.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 + 200.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 55 && Math.abs(errY) < 70) { landingCounters[13]++; } else { landingCounters[13] = 0; }
|
||||
if (landingCounters[13] >= 2 && !foundLanding) { foundLanding = true; landingId = 13; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[13] = 0; }
|
||||
|
||||
// ---- 15号 ----
|
||||
ArucoMarker marker15 = markerMap.get(15);
|
||||
if (marker15 != null) {
|
||||
Mat corner = marker15.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - LENS_OFFSET_X;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 70 && Math.abs(errY) < 70) { landingCounters[15]++; } else { landingCounters[15] = 0; }
|
||||
if (landingCounters[15] >= 2 && !foundLanding) { foundLanding = true; landingId = 15; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[15] = 0; }
|
||||
|
||||
// ---- 17号 ----
|
||||
ArucoMarker marker17 = markerMap.get(17);
|
||||
if (marker17 != null) {
|
||||
Mat corner = marker17.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - 200.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 55 && Math.abs(errY) < 70) { landingCounters[17]++; } else { landingCounters[17] = 0; }
|
||||
if (landingCounters[17] >= 2 && !foundLanding) { foundLanding = true; landingId = 17; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[17] = 0; }
|
||||
|
||||
// ---- 14号 ----
|
||||
ArucoMarker marker14 = markerMap.get(14);
|
||||
if (marker14 != null) {
|
||||
Mat corner = marker14.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 + 200.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 70 && Math.abs(errY) < 70) { landingCounters[14]++; } else { landingCounters[14] = 0; }
|
||||
if (landingCounters[14] >= 2 && !foundLanding) { foundLanding = true; landingId = 14; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[14] = 0; }
|
||||
|
||||
// ---- 16号 ----
|
||||
ArucoMarker marker16 = markerMap.get(16);
|
||||
if (marker16 != null) {
|
||||
Mat corner = marker16.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - LENS_OFFSET_X;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 70 && Math.abs(errY) < 70) { landingCounters[16]++; } else { landingCounters[16] = 0; }
|
||||
if (landingCounters[16] >= 2 && !foundLanding) { foundLanding = true; landingId = 16; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[16] = 0; }
|
||||
|
||||
// ---- 18号 ----
|
||||
ArucoMarker marker18 = markerMap.get(18);
|
||||
if (marker18 != null) {
|
||||
Mat corner = marker18.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - 200.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 55 && Math.abs(errY) < 70) { landingCounters[18]++; } else { landingCounters[18] = 0; }
|
||||
if (landingCounters[18] >= 2 && !foundLanding) { foundLanding = true; landingId = 18; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[18] = 0; }
|
||||
}
|
||||
}
|
||||
// ===== 双挂模式:15 17 19 16 18 20 =====
|
||||
else {
|
||||
// ---- 15号 ----
|
||||
ArucoMarker marker15d = markerMap.get(15);
|
||||
if (marker15d != null) {
|
||||
Mat corner = marker15d.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 +220.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 55 && Math.abs(errY) < 70) { landingCounters[15]++; } else { landingCounters[15] = 0; }
|
||||
if (landingCounters[15] >= 2 && !foundLanding) { foundLanding = true; landingId = 15; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[15] = 0; }
|
||||
|
||||
// 双挂模式:检查17号
|
||||
if (isDoublePayload && target17 != null && ultHeight <= 4) {
|
||||
Mat corner = target17.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
double[] p = corner.get(0, i);
|
||||
cx += p[0];
|
||||
cy += p[1];
|
||||
// ---- 17号 ----
|
||||
ArucoMarker marker17d = markerMap.get(17);
|
||||
if (marker17d != null) {
|
||||
Mat corner = marker17d.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - LENS_OFFSET_X;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 70 && Math.abs(errY) < 70) { landingCounters[17]++; } else { landingCounters[17] = 0; }
|
||||
if (landingCounters[17] >= 2 && !foundLanding) { foundLanding = true; landingId = 17; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[17] = 0; }
|
||||
|
||||
// ---- 19号 ----
|
||||
ArucoMarker marker19d = markerMap.get(19);
|
||||
if (marker19d != null) {
|
||||
Mat corner = marker19d.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - 200.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 55 && Math.abs(errY) < 70) { landingCounters[19]++; } else { landingCounters[19] = 0; }
|
||||
if (landingCounters[19] >= 2 && !foundLanding) { foundLanding = true; landingId = 19; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[19] = 0; }
|
||||
|
||||
// ---- 16号 ----
|
||||
ArucoMarker marker16d = markerMap.get(16);
|
||||
if (marker16d != null) {
|
||||
Mat corner = marker16d.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 +220.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 70 && Math.abs(errY) < 70) { landingCounters[16]++; } else { landingCounters[16] = 0; }
|
||||
if (landingCounters[16] >= 2 && !foundLanding) { foundLanding = true; landingId = 16; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[16] = 0; }
|
||||
|
||||
// ---- 18号 ----
|
||||
ArucoMarker marker18d = markerMap.get(18);
|
||||
if (marker18d != null) {
|
||||
Mat corner = marker18d.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - LENS_OFFSET_X;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 70 && Math.abs(errY) < 70) { landingCounters[18]++; } else { landingCounters[18] = 0; }
|
||||
if (landingCounters[18] >= 2 && !foundLanding) { foundLanding = true; landingId = 18; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[18] = 0; }
|
||||
|
||||
// ---- 20号 ----
|
||||
ArucoMarker marker20d = markerMap.get(20);
|
||||
if (marker20d != null) {
|
||||
Mat corner = marker20d.getConner();
|
||||
double cx = 0, cy = 0;
|
||||
for (int i = 0; i < 4; i++) { double[] p = corner.get(0, i); cx += p[0]; cy += p[1]; }
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
double errX = cx - imgWidth / 2.0 - 200.0;
|
||||
double errY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
if (Math.abs(errX) < 55 && Math.abs(errY) < 70) { landingCounters[20]++; } else { landingCounters[20] = 0; }
|
||||
if (landingCounters[20] >= 2 && !foundLanding) { foundLanding = true; landingId = 20; isSpecialLanding = true; singleErrX = errX; singleErrY = errY; offsetX = singleErrX; offsetY = singleErrY; absX = Math.abs(singleErrX); absY = Math.abs(singleErrY); }
|
||||
} else { landingCounters[20] = 0; }
|
||||
}
|
||||
cx /= 4.0; cy /= 4.0;
|
||||
singleErrX = cx - imgWidth / 2.0 - LENS_OFFSET_X;
|
||||
singleErrY = cy - imgHeight / 2.0 + LENS_OFFSET_Y;
|
||||
|
||||
if (Math.abs(singleErrX) < 60 && Math.abs(singleErrY) < 60) {
|
||||
isSpecialLanding = true;
|
||||
offsetX = singleErrX;
|
||||
offsetY = singleErrY;
|
||||
absX = Math.abs(singleErrX);
|
||||
absY = Math.abs(singleErrY);
|
||||
}
|
||||
}
|
||||
LogUtil.log(TAG_LOG, "【计数器】13=" + landingCounters[13] + " 14=" + landingCounters[14] + " 15=" + landingCounters[15] + " 16=" + landingCounters[16] + " 17=" + landingCounters[17] + " 18=" + landingCounters[18]+ " 19=" + landingCounters[19]+ " 20=" + landingCounters[20]);
|
||||
|
||||
// 连续帧判断
|
||||
int currentCondition = 0;
|
||||
if (isSpecialLanding) {
|
||||
currentCondition = 1;
|
||||
} else if (ultHeight <= 4 && absX < 60 && absY < 60 && validCount >= 8) {
|
||||
currentCondition = 2;
|
||||
}
|
||||
|
||||
if (currentCondition == 0 || currentCondition != lastLandingCondition) {
|
||||
if (consecutiveTriggerCount > 0) {
|
||||
consecutiveTriggerCount = 0;
|
||||
LogUtil.log(TAG_LOG, "【计数器清零】条件变化 last=" + lastLandingCondition + " curr=" + currentCondition);
|
||||
}
|
||||
lastLandingCondition = currentCondition;
|
||||
}
|
||||
|
||||
if (currentCondition > 0) {
|
||||
consecutiveTriggerCount++;
|
||||
if (consecutiveTriggerCount >= TRIGGER_FRAME_THRESHOLD) {
|
||||
// 触发降落
|
||||
if (foundLanding) {
|
||||
LogUtil.log(TAG_LOG, "【降落触发】ID=" + landingId + " 连续2帧满足条件");
|
||||
DroneHelper.getInstance().moveVxVyYawrateHeight(0f, 0f, 0f, 0f);
|
||||
startFastStick = true;
|
||||
consecutiveTriggerCount = 0;
|
||||
lastLandingCondition = 0;
|
||||
|
||||
LogUtil.log(TAG_LOG, currentCondition == 1 ?
|
||||
"【降落触发】单双挂精准条件满足" :
|
||||
"【降落触发】几何中心对准,码数量=" + validCount);
|
||||
|
||||
for (int i = 0; i < landingCounters.length; i++) { landingCounters[i] = 0; }
|
||||
handler.postDelayed(() -> handler.post(runnable), 300);
|
||||
return;
|
||||
}
|
||||
|
||||
// 第二帧结束,没有触发 → 重置
|
||||
counterRound++;
|
||||
if (counterRound >= 2) {
|
||||
counterRound = 0;
|
||||
for (int i = 0; i < landingCounters.length; i++) { landingCounters[i] = 0; }
|
||||
LogUtil.log(TAG_LOG, "【计数器重置】2帧未触发,重新从0开始");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double outX = 0, outY = 0, outZ = 0;
|
||||
|
||||
// 分段PID控制(原有逻辑保持不变)
|
||||
if(z <= 0.4){
|
||||
pidControlX.setInputFilterAll((float)offsetX/1750);
|
||||
pidControlY.setInputFilterAll(-(float)offsetY/1750);
|
||||
pidControlX.setInputFilterAll((float)offsetX/1800);
|
||||
pidControlY.setInputFilterAll(-(float)offsetY/1800);
|
||||
if (pidControlX.get_pid()<0){
|
||||
if (pidControlX.get_pid()<-0.125){
|
||||
outX=absX<120?0:-0.125;
|
||||
|
|
@ -691,7 +807,7 @@ public class ApronArucoDetectPort {
|
|||
outY = absY < 50 ? 0 : pidControlY.get_pid();
|
||||
outX = Math.max(-0.20, Math.min(0.20, outX));
|
||||
outY = Math.max(-0.20, Math.min(0.20, outY));
|
||||
outZ = (absX < 100 && absY < 100) ? -0.4 : 0;
|
||||
outZ = (absX < 100 && absY < 100) ? -0.8 : 0;
|
||||
}else if(z>=4.0){
|
||||
pidControlX.setInputFilterAll((float)offsetX/600);
|
||||
pidControlY.setInputFilterAll(-(float)offsetY/600);
|
||||
|
|
@ -748,7 +864,7 @@ public class ApronArucoDetectPort {
|
|||
float vz;
|
||||
|
||||
if (ultHeight >= 50) {
|
||||
vz = -0.7f;
|
||||
vz = -0.8f;
|
||||
} else if(ultHeight<=20){
|
||||
vz = 0.0f;
|
||||
}else{
|
||||
|
|
@ -839,19 +955,59 @@ public class ApronArucoDetectPort {
|
|||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
|
||||
public void detectForceTriggerTags(int height, int width, byte[] data, Dictionary dictionary) {
|
||||
Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1);
|
||||
yuvMat.put(0, 0, data);
|
||||
Mat grayImgMat = yuvMat.submat(0, height, 0, width);
|
||||
grayImgMat = grayImgMat.clone();
|
||||
|
||||
MatOfInt ids = new MatOfInt();
|
||||
List<Mat> corners = new ArrayList<>();
|
||||
DetectorParameters parameters = new DetectorParameters();
|
||||
ArucoDetector detector = new ArucoDetector(dictionary, parameters);
|
||||
|
||||
detector.detectMarkers(grayImgMat, corners, ids);
|
||||
|
||||
if (!ids.empty()) {
|
||||
int ultHeight = Movement.getInstance().getUltrasonicHeight();
|
||||
int[] idArray = ids.toArray();
|
||||
boolean has6 = false;
|
||||
int count1to4 = 0;
|
||||
|
||||
for (int id : idArray) {
|
||||
if (id == 6) {
|
||||
has6 = true;
|
||||
} else if (id >= 1 && id <= 4) {
|
||||
count1to4++;
|
||||
}
|
||||
}
|
||||
|
||||
if (has6 && count1to4 >= 3 && ultHeight <= 60) {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】满足条件:6号 + 1-4号中" + count1to4 + "个 高度=" + ultHeight + "dm");
|
||||
FlightManager.forceTriggerDetection = true;
|
||||
} else {
|
||||
LogUtil.log(TAG_LOG, "【强制刹停】不满足条件:6号=" + has6 + ", 1-4号数量=" + count1to4 + ", 高度=" + ultHeight + "dm(阈值80)");
|
||||
}
|
||||
}
|
||||
|
||||
yuvMat.release();
|
||||
grayImgMat.release();
|
||||
ids.release();
|
||||
}
|
||||
|
||||
public void setDetectedBigMarkers() {
|
||||
startFastStick = false;
|
||||
isStartAruco = false;
|
||||
consecutiveTriggerCount = 0;
|
||||
frameCounter = 0;
|
||||
// 【关键】重置旋转标志,下次降落重新用6号旋转
|
||||
isYawAligned = false;
|
||||
currentLandingMode = 0;
|
||||
lastLandingCondition = 0;
|
||||
// 【新增】重置高度稳定性监测状态
|
||||
isHeightStableMonitoring = false;
|
||||
lastHeightCheckTime = 0;
|
||||
lastUltrasonicHeight = 0;
|
||||
// 重置分支计数器
|
||||
counterRound = 0;
|
||||
for (int i = 0; i < landingCounters.length; i++) {
|
||||
landingCounters[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private int handlerCallbackCount = 0;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,12 @@ package com.aros.apron.tools;
|
|||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.aros.apron.app.ApronApp;
|
||||
import com.aros.apron.callback.MqttActionCallBack;
|
||||
|
|
@ -59,48 +63,74 @@ public class MqttManager {
|
|||
* 连接MQTT服务器
|
||||
*/
|
||||
private void doClientConnection() {
|
||||
if (!mqttAndroidClient.isConnected() && isConnectIsNomarl()) {
|
||||
try {
|
||||
mqttAndroidClient.connect(mMqttConnectOptions, null, new MqttActionCallBack(mqttAndroidClient,mMqttConnectOptions));
|
||||
} catch (MqttException e) {
|
||||
LogUtil.log(TAG,"mqtt连接异常:"+e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (mqttAndroidClient.isConnected()) {
|
||||
return;
|
||||
}
|
||||
// 先检查网络状态(仅用于日志和延迟策略,不阻塞连接尝试)
|
||||
boolean networkAvailable = isConnectIsNomarl();
|
||||
if (networkAvailable) {
|
||||
tryConnect();
|
||||
} else {
|
||||
// 网络检查未通过时,仍尝试连接(防止网络误判导致永久断连)
|
||||
LogUtil.log(TAG, "网络检查未通过,但仍尝试连接");
|
||||
tryConnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void tryConnect() {
|
||||
if (mqttAndroidClient.isConnected()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mqttAndroidClient.connect(mMqttConnectOptions, null, new MqttActionCallBack(mqttAndroidClient, mMqttConnectOptions));
|
||||
} catch (MqttException e) {
|
||||
LogUtil.log(TAG, "mqtt连接异常:" + e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断网络是否连接
|
||||
* 判断网络是否连接(仅做日志记录,不阻塞连接尝试)
|
||||
*/
|
||||
private boolean isConnectIsNomarl() {
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager) ApronApp.Companion.getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
@SuppressLint("MissingPermission") NetworkInfo info = connectivityManager.getActiveNetworkInfo();
|
||||
if (info != null && info.isAvailable()) {
|
||||
String name = info.getTypeName();
|
||||
LogUtil.log(TAG, "当前网络名称:" + name);
|
||||
return true;
|
||||
} else {
|
||||
LogUtil.log(TAG, "没有可用Mqtt网络,延迟三秒后重连");
|
||||
/*没有可用网络的时候,延迟3秒再尝试重连*/
|
||||
doConnectionDelay();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
Network network = connectivityManager.getActiveNetwork();
|
||||
if (network != null) {
|
||||
NetworkCapabilities capabilities = connectivityManager.getNetworkCapabilities(network);
|
||||
if (capabilities != null) {
|
||||
String transportInfo;
|
||||
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||
transportInfo = "ETHERNET";
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||
transportInfo = "WIFI";
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||
transportInfo = "CELLULAR";
|
||||
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
|
||||
transportInfo = "VPN";
|
||||
} else {
|
||||
transportInfo = "OTHER";
|
||||
}
|
||||
LogUtil.log(TAG, "当前网络可用:" + transportInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LogUtil.log(TAG, "没有可用Mqtt网络(API 23+)");
|
||||
return false;
|
||||
} else {
|
||||
@SuppressLint("MissingPermission") NetworkInfo info = connectivityManager.getActiveNetworkInfo();
|
||||
if (info != null && info.isAvailable()) {
|
||||
String name = info.getTypeName();
|
||||
LogUtil.log(TAG, "当前网络名称:" + name);
|
||||
return true;
|
||||
} else {
|
||||
LogUtil.log(TAG, "没有可用Mqtt网络");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 没有可用网络的时候,延迟5秒再尝试重连
|
||||
*/
|
||||
private void doConnectionDelay() {
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
doClientConnection();
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
private String generateRandomString(int length) {
|
||||
String characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ public class SimplePortScanner {
|
|||
private static final long SCAN_INTERVAL = 3000L;
|
||||
private static final int CONNECT_TIMEOUT = 500;
|
||||
|
||||
private volatile boolean isScanning = false; // 防止重复启动扫描
|
||||
|
||||
private OnPortCheckListener checkListener;
|
||||
// 主线程 Handler,用于切换回调到 UI 线程和定时扫描
|
||||
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
|
@ -51,11 +53,14 @@ public class SimplePortScanner {
|
|||
closedCount++;
|
||||
Log.i(TAG, "端口关闭次数:" + closedCount + "/" + MAX_CLOSED_COUNT);
|
||||
if (closedCount >= MAX_CLOSED_COUNT) {
|
||||
Log.i(TAG, "端口连续关闭 " + closedCount + " 次,尝试重启 RTSP 推流");
|
||||
closedCount = 0; // 重置计数
|
||||
// 先重置状态,让 startLiveWithRTSP 能真正执行
|
||||
Log.i(TAG, "端口连续关闭 " + closedCount + " 次,停止扫描并重启推流");
|
||||
closedCount = 0;
|
||||
isScanning = false; // 停止自身扫描
|
||||
mainHandler.removeCallbacks(scanRunnable);
|
||||
StreamManager.getInstance().stopstream();
|
||||
StreamManager.getInstance().resetStreamState();
|
||||
StreamManager.getInstance().startLiveWithRTSP();
|
||||
return; // 阻止 postDelayed 再次循环
|
||||
}
|
||||
if (checkListener != null) {
|
||||
checkListener.onPortClosed();
|
||||
|
|
@ -88,14 +93,24 @@ public class SimplePortScanner {
|
|||
}
|
||||
|
||||
public void startScan() {
|
||||
stopScan();
|
||||
closedCount = 0; // 重置计数
|
||||
if (isScanning) {
|
||||
Log.i(TAG, "扫描已在运行,忽略重复调用");
|
||||
return;
|
||||
}
|
||||
isScanning = true;
|
||||
closedCount = 0;
|
||||
Log.i(TAG, "开始端口扫描");
|
||||
// 立即执行一次,然后延迟 3 秒循环
|
||||
mainHandler.post(scanRunnable);
|
||||
}
|
||||
|
||||
public void stopScan() {
|
||||
if (!isScanning) {
|
||||
return;
|
||||
}
|
||||
isScanning = false;
|
||||
mainHandler.removeCallbacks(scanRunnable);
|
||||
Log.i(TAG, "停止端口扫描");
|
||||
}
|
||||
|
||||
private boolean checkPort(String host, int port) {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,28 @@ public class SpeakerProgressReporter {
|
|||
LogUtil.log(TAG, "停止音频进度上报");
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即发送一次音频进度(不启动定时循环)
|
||||
*/
|
||||
public void sendNowAudioProgress(int psdkIndex) {
|
||||
try {
|
||||
sendAudioProgress(psdkIndex);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 立即发送一次TTS进度(不启动定时循环)
|
||||
*/
|
||||
public void sendNowTTSProgress(int psdkIndex) {
|
||||
try {
|
||||
sendTTSProgress(psdkIndex);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送音频进度(使用 SpeakerAudioPlayProgress 实体类)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.aros.apron.tools;
|
||||
|
||||
import com.aros.apron.entity.Movement;
|
||||
import com.aros.apron.entity.XTTSParams;
|
||||
import com.iflytek.aikit.core.AeeEvent;
|
||||
import com.iflytek.aikit.core.AiHandle;
|
||||
|
|
@ -67,11 +68,18 @@ public class XTtsPcmGenerator {
|
|||
paramBuilder.param("speed", params.speed);
|
||||
paramBuilder.param("volume", params.volume);
|
||||
|
||||
// 构建监听器
|
||||
|
||||
Movement.getInstance().setTts_speed(String.valueOf(params.speed));
|
||||
Movement.getInstance().setTts_language("0");
|
||||
Movement.getInstance().setTts_type("0");
|
||||
Movement.getInstance().setTts_volume(String.valueOf(params.volume));
|
||||
|
||||
|
||||
|
||||
AiListener listener = buildListener();
|
||||
LogUtil.log(TAG, "监听器创建成功");
|
||||
|
||||
// 注册监听器
|
||||
|
||||
try {
|
||||
AiHelper.getInst().registerListener(ABILITY_ID, listener);
|
||||
LogUtil.log(TAG, "监听器注册成功");
|
||||
|
|
@ -82,7 +90,7 @@ public class XTtsPcmGenerator {
|
|||
return null;
|
||||
}
|
||||
|
||||
// 启动 TTS 引擎
|
||||
|
||||
aiHandle = AiHelper.getInst().start(ABILITY_ID, paramBuilder.build(), null);
|
||||
if (aiHandle.getCode() != 0) {
|
||||
LogUtil.log(TAG, "启动 TTS 引擎失败: " + aiHandle.getCode());
|
||||
|
|
@ -91,7 +99,7 @@ public class XTtsPcmGenerator {
|
|||
}
|
||||
LogUtil.log(TAG, "TTS 引擎启动成功");
|
||||
|
||||
// 构建请求
|
||||
|
||||
AiText aiText = AiText.get("text").data(text).valid();
|
||||
AiRequest request = AiRequest.builder().payload(aiText).build();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue