|
|
|
@ -416,6 +416,7 @@ |
|
|
|
v-if="!screenshotMode" |
|
|
|
:visible.sync="showConflictDrawer" |
|
|
|
:conflicts="conflicts" |
|
|
|
:loading="conflictCheckRunning" |
|
|
|
@view-conflict="viewConflict" |
|
|
|
@resolve-conflict="resolveConflict" |
|
|
|
/> |
|
|
|
@ -706,6 +707,8 @@ export default { |
|
|
|
show4TPanel: false, |
|
|
|
/** 冲突列表弹窗(点击左侧冲突按钮即打开并自动执行检测) */ |
|
|
|
showConflictDrawer: false, |
|
|
|
/** 冲突检测进行中(避免主线程长时间阻塞导致页面卡死) */ |
|
|
|
conflictCheckRunning: false, |
|
|
|
/** 定位冲突时让右侧面板展开的航线 ID 列表 */ |
|
|
|
expandRouteIdsForPanel: [], |
|
|
|
/** 打开航线编辑弹窗时默认选中的 tab(解决冲突时传 'waypoints' 直接打开航点列表) */ |
|
|
|
@ -3966,9 +3969,15 @@ export default { |
|
|
|
// 白板:进入/退出白板模式 |
|
|
|
this.toggleWhiteboardMode(); |
|
|
|
} else if (item.id === 'start') { |
|
|
|
// 冲突:打开可拖动冲突列表弹窗,并立即执行检测(无需再点“重新检测”) |
|
|
|
// 冲突:打开弹窗并异步执行检测,避免航线多时主线程阻塞导致页面卡死 |
|
|
|
this.showConflictDrawer = true; |
|
|
|
this.$nextTick(() => { this.runConflictCheck(); }); |
|
|
|
this.conflictCheckRunning = true; |
|
|
|
this.$nextTick(() => { |
|
|
|
setTimeout(() => { |
|
|
|
this.runConflictCheck(); |
|
|
|
this.conflictCheckRunning = false; |
|
|
|
}, 0); |
|
|
|
}); |
|
|
|
} else if (item.id === 'insert') { |
|
|
|
// 如果当前已经是平台标签页,则关闭右侧面板 |
|
|
|
if (this.activeRightTab === 'platform' && !this.isRightPanelHidden) { |
|
|
|
@ -5219,6 +5228,8 @@ export default { |
|
|
|
const waypointStartTimeToMinutes = (s) => this.waypointStartTimeToMinutes(s); |
|
|
|
|
|
|
|
const allRaw = []; |
|
|
|
/** 按航线缓存 timeline(segments+path),供航迹间隔检测复用,避免每 (routeId,t) 重复 buildRouteTimeline 导致航线多时卡死 */ |
|
|
|
const routeIdToTimeline = {}; |
|
|
|
|
|
|
|
// ---------- 时间冲突:单航线内(提前到达、无法按时到达、盘旋时间不足)---------- |
|
|
|
routeIds.forEach(routeId => { |
|
|
|
@ -5231,10 +5242,16 @@ export default { |
|
|
|
pathData = { path: ret.path, segmentEndIndices: ret.segmentEndIndices, holdArcRanges: ret.holdArcRanges || {} }; |
|
|
|
} |
|
|
|
} |
|
|
|
const { earlyArrivalLegs, lateArrivalLegs, holdDelayConflicts } = this.buildRouteTimeline(route.waypoints, minMinutes, maxMinutes, pathData); |
|
|
|
const timeline = this.buildRouteTimeline(route.waypoints, minMinutes, maxMinutes, pathData); |
|
|
|
const { earlyArrivalLegs, lateArrivalLegs, holdDelayConflicts } = timeline; |
|
|
|
routeIdToTimeline[routeId] = { |
|
|
|
segments: timeline.segments, |
|
|
|
path: pathData && pathData.path ? pathData.path : null, |
|
|
|
segmentEndIndices: pathData && pathData.segmentEndIndices ? pathData.segmentEndIndices : null |
|
|
|
}; |
|
|
|
const routeName = route.name || `航线${route.id}`; |
|
|
|
(earlyArrivalLegs || []).forEach(leg => { |
|
|
|
// 提前到达:给出多种可选思路,统一在提示里说明“视定速/定时约束优先调整未受约束量” |
|
|
|
// 提前到达:相对K时=离开该点时间;到达早于该时间时可降速/盘旋等待/调晚下一航点相对K时;受定速/定时约束时只调未锁定参数 |
|
|
|
allRaw.push({ |
|
|
|
type: CONFLICT_TYPE.TIME, |
|
|
|
subType: 'early_arrival', |
|
|
|
@ -5244,12 +5261,12 @@ export default { |
|
|
|
fromWaypoint: leg.fromName, |
|
|
|
toWaypoint: leg.toName, |
|
|
|
time: this.minutesToStartTime(leg.actualArrival), |
|
|
|
suggestion: '该航段将提前到达下一航点。可选措施:① 适当降低本段巡航速度;② 在本段或下一航点前增加盘旋等待;③ 视任务需要调整下一航点相对K时/计划时间。若存在定速点或定时点,请优先调整未受约束的速度或时间。', |
|
|
|
suggestion: '① 降低本段速度 ② 下一航点为盘旋点时依靠盘旋等待 ③ 将下一航点相对K时调晚', |
|
|
|
severity: 'high' |
|
|
|
}); |
|
|
|
}); |
|
|
|
(lateArrivalLegs || []).forEach(leg => { |
|
|
|
// 无法按时到达:同时提示“提速”和“调整时间/路径”等多种方案 |
|
|
|
// 无法按时到达:需在下一航点相对K时前到达;可提速或调早下一航点相对K时;受定速/定时约束时只调未锁定参数 |
|
|
|
allRaw.push({ |
|
|
|
type: CONFLICT_TYPE.TIME, |
|
|
|
subType: 'late_arrival', |
|
|
|
@ -5258,12 +5275,12 @@ export default { |
|
|
|
routeIds: [routeId], |
|
|
|
fromWaypoint: leg.fromName, |
|
|
|
toWaypoint: leg.toName, |
|
|
|
suggestion: `当前速度不足,理论上需将本段速度提升至 ≥${leg.requiredSpeedKmh} km/h 才能按时到达。可选措施:① 在安全范围内提高本段或前一段速度;② 适当提前下一航点相对K时/计划时间;③ 结合任务需要调整上游航段或加入盘旋缓冲。若存在定速点或定时点,请优先从未锁定的速度或时间入手。`, |
|
|
|
suggestion: `① 将本段速度提升至 ≥${leg.requiredSpeedKmh} km/h ② 将下一航点相对K时调早 ③ 调整上游航段速度或时间`, |
|
|
|
severity: 'high' |
|
|
|
}); |
|
|
|
}); |
|
|
|
(holdDelayConflicts || []).forEach(conf => { |
|
|
|
// 盘旋时间不足:提示可修改盘旋圈数/时间、速度或切出时刻 |
|
|
|
// 盘旋时间不足:项目不控制盘旋圈数。定时盘旋时盘旋时间=该点相对K时-到达时间、速度由半径与时间反算;非定时时速度继承上一航点。只能通过延长相对K时/调半径或上一航点速度等改善 |
|
|
|
allRaw.push({ |
|
|
|
type: CONFLICT_TYPE.TIME, |
|
|
|
subType: 'hold_delay', |
|
|
|
@ -5274,7 +5291,7 @@ export default { |
|
|
|
toWaypoint: conf.toName, |
|
|
|
time: this.minutesToStartTime(conf.setExitTime), |
|
|
|
position: conf.holdCenter ? `经度 ${conf.holdCenter.lng.toFixed(5)}°, 纬度 ${conf.holdCenter.lat.toFixed(5)}°` : undefined, |
|
|
|
suggestion: `警告:设定的盘旋时间不足以支撑战斗机完成最后一圈,实际切出将延迟 ${conf.delaySeconds} 秒。可选措施:① 增加盘旋圈数或调整盘旋结束时间;② 在允许范围内调整盘旋段速度;③ 结合上下游航段,微调相关航点的相对K时/计划时间。若存在定速点或定时点,请优先调整未受约束的参数。`, |
|
|
|
suggestion: `实际切出将延迟 ${conf.delaySeconds} 秒。① 延长该盘旋点相对K时 ② 定时盘旋调转弯半径,非定时调上一航点速度或本点相对K时 ③ 微调上下游航点相对K时`, |
|
|
|
severity: 'high', |
|
|
|
holdCenter: conf.holdCenter, |
|
|
|
positionLng: conf.holdCenter && conf.holdCenter.lng, |
|
|
|
@ -5287,12 +5304,12 @@ export default { |
|
|
|
// 时间窗重叠:仅“时间段有交集”不算冲突(不同地点飞机可同时起飞)。后续若有跑道/频段等资源绑定,再按“同一资源占用时间重叠”报冲突。 |
|
|
|
// 故此处不再调用 detectTimeWindowOverlap。 |
|
|
|
|
|
|
|
// ---------- 空间冲突:航迹最小间隔 ---------- |
|
|
|
// ---------- 空间冲突:航迹最小间隔(使用缓存的 timeline,避免每 (routeId,t) 重复 buildRouteTimeline)---------- |
|
|
|
const getPositionAtMinutesForConflict = (routeId, minutesFromK) => { |
|
|
|
const route = this.routes.find(r => r.id === routeId); |
|
|
|
if (!route || !route.waypoints || route.waypoints.length === 0) return null; |
|
|
|
const { position } = this.getPositionAtMinutesFromK(route.waypoints, minutesFromK, minMinutes, maxMinutes, routeId); |
|
|
|
return position; |
|
|
|
const c = routeIdToTimeline[routeId]; |
|
|
|
if (!c || !c.segments || c.segments.length === 0) return null; |
|
|
|
const pos = this.getPositionFromTimeline(c.segments, minutesFromK, c.path, c.segmentEndIndices); |
|
|
|
return pos || null; |
|
|
|
}; |
|
|
|
const trackConflicts = detectTrackSeparation(routeIds, minMinutes, maxMinutes, getPositionAtMinutesForConflict, config); |
|
|
|
trackConflicts.forEach(c => allRaw.push(c)); |
|
|
|
|