From 7bb910892a5e056f8f7d9296495238a985f40207 Mon Sep 17 00:00:00 2001 From: sladro Date: Wed, 11 Feb 2026 12:17:06 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E4=BF=AE=E5=A4=8D=E5=B9=B3?= =?UTF-8?q?=E6=BB=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- accout.md | 2 +- .../qaup/collision/common/model/Aircraft.java | 5 + .../datacollector/dao/DataCollectorDao.java | 3 + .../service/DataCollectorService.java | 87 ++++++++++++++- .../WebSocketMessageBroadcaster.java | 100 +++++++++--------- 命令.md | 51 ++++++++- 6 files changed, 192 insertions(+), 56 deletions(-) diff --git a/accout.md b/accout.md index 0874e39..1d7b33e 100644 --- a/accout.md +++ b/accout.md @@ -18,7 +18,7 @@ (4)Z31020**&d1231=+33 4.测试平台-- 10.100.23.10 (1)root -(2)Dianxin@222 +Dianxin@222 5.红绿灯测试平台-- 10.98.23.111 windows系统 (1)账密 ①administrator diff --git a/qaup-collision/src/main/java/com/qaup/collision/common/model/Aircraft.java b/qaup-collision/src/main/java/com/qaup/collision/common/model/Aircraft.java index 4c991b1..ae2f045 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/common/model/Aircraft.java +++ b/qaup-collision/src/main/java/com/qaup/collision/common/model/Aircraft.java @@ -25,6 +25,11 @@ public class Aircraft extends MovingObject { * 航班号 */ private String flightNumber; + + /** + * Upstream track identifier for source-track partitioning. + */ + private String trackNumber; /** * 进港跑道编号 diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java index 9ba9f57..cff8f95 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/dao/DataCollectorDao.java @@ -115,6 +115,7 @@ public class DataCollectorDao { Aircraft aircraft = new Aircraft(); aircraft.setObjectId(rawData.getFlightNo()); aircraft.setObjectName(rawData.getFlightNo()); // 使用 flightNo 作为 objectName + aircraft.setTrackNumber(rawData.getTrackNumber()); aircraft.setCurrentPosition(geometryFactory.createPoint(new org.locationtech.jts.geom.Coordinate(rawData.getLongitude(), rawData.getLatitude()))); // 外部API中没有直接的速度和方向,如果需要,可以在DataCollectorService中计算或使用默认值 // 暂时设置为0.0,DataCollectorService会处理 @@ -207,6 +208,8 @@ public class DataCollectorDao { private static class ExternalAircraftData { @JsonProperty("flightNo") private String flightNo; + @JsonProperty("trackNumber") + private String trackNumber; @JsonProperty("longitude") private Double longitude; @JsonProperty("latitude") diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java index 58ac766..a3c57f4 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/DataCollectorService.java @@ -130,6 +130,7 @@ public class DataCollectorService { // 用于缓存所有活跃的MovingObject的最新状态 private final Map activeMovingObjectsCache = new ConcurrentHashMap<>(); + private final Map selectedTrackByFlightNo = new ConcurrentHashMap<>(); // 记录每个对象最近一次更新的时间戳,用于淘汰不活跃对象(防止内存长期增长) private final Map activeMovingObjectsLastUpdatedAtMs = new ConcurrentHashMap<>(); @@ -430,19 +431,30 @@ public class DataCollectorService { return Collections.emptyList(); } - Map latestByFlightNo = new HashMap<>(); + Map> latestByFlightAndTrack = new HashMap<>(); for (Aircraft aircraft : aircrafts) { if (aircraft == null || aircraft.getObjectId() == null || aircraft.getObjectId().isBlank()) { continue; } - Aircraft existing = latestByFlightNo.get(aircraft.getObjectId()); + String flightNo = aircraft.getObjectId(); + String trackKey = normalizeTrackKey(aircraft.getTrackNumber()); + Map byTrack = latestByFlightAndTrack.computeIfAbsent(flightNo, k -> new HashMap<>()); + + Aircraft existing = byTrack.get(trackKey); if (existing == null || isCandidateNewer(existing, aircraft)) { - latestByFlightNo.put(aircraft.getObjectId(), aircraft); + byTrack.put(trackKey, aircraft); } } - return List.copyOf(latestByFlightNo.values()); + List selected = new java.util.ArrayList<>(latestByFlightAndTrack.size()); + for (Map.Entry> entry : latestByFlightAndTrack.entrySet()) { + Aircraft selectedAircraft = selectAircraftTrackCandidate(entry.getKey(), entry.getValue()); + if (selectedAircraft != null) { + selected.add(selectedAircraft); + } + } + return selected; } private boolean isCandidateNewer(Aircraft existing, Aircraft candidate) { @@ -461,6 +473,72 @@ public class DataCollectorService { return candidateTs >= existingTs; } + private Aircraft selectAircraftTrackCandidate(String flightNo, Map byTrack) { + if (byTrack == null || byTrack.isEmpty()) { + return null; + } + if (byTrack.size() == 1) { + Aircraft only = byTrack.values().iterator().next(); + selectedTrackByFlightNo.put(flightNo, normalizeTrackKey(only.getTrackNumber())); + return only; + } + + String selectedTrack = selectedTrackByFlightNo.get(flightNo); + if (selectedTrack != null && byTrack.containsKey(selectedTrack)) { + return byTrack.get(selectedTrack); + } + + MovingObject previous = activeMovingObjectsCache.get(flightNo); + Aircraft best = null; + double bestDistance = Double.MAX_VALUE; + if (previous != null && previous.getCurrentPosition() != null) { + for (Aircraft candidate : byTrack.values()) { + if (candidate == null || candidate.getCurrentPosition() == null) { + continue; + } + double d = calculateDistanceMeters(previous.getCurrentPosition(), candidate.getCurrentPosition()); + if (d < bestDistance) { + best = candidate; + bestDistance = d; + } + } + } + + if (best == null) { + for (Aircraft candidate : byTrack.values()) { + if (best == null || isCandidateNewer(best, candidate)) { + best = candidate; + } + } + } + + if (best != null) { + selectedTrackByFlightNo.put(flightNo, normalizeTrackKey(best.getTrackNumber())); + } + return best; + } + + private String normalizeTrackKey(String trackNumber) { + if (trackNumber == null || trackNumber.isBlank()) { + return "_DEFAULT_TRACK_"; + } + return trackNumber.trim(); + } + + private double calculateDistanceMeters(Point from, Point to) { + double lat1 = Math.toRadians(from.getY()); + double lon1 = Math.toRadians(from.getX()); + double lat2 = Math.toRadians(to.getY()); + double lon2 = Math.toRadians(to.getX()); + + double dLat = lat2 - lat1; + double dLon = lon2 - lon1; + double a = Math.sin(dLat / 2.0) * Math.sin(dLat / 2.0) + + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2.0) * Math.sin(dLon / 2.0); + double c = 2.0 * Math.atan2(Math.sqrt(a), Math.sqrt(1.0 - a)); + return 6371000.0 * c; + } + @Scheduled(fixedRateString = "${data.collector.interval}") public void collectAircraftData() { if (collectorDisabled) { @@ -1274,6 +1352,7 @@ public class DataCollectorService { Long last = entry.getValue(); if (last == null || last < cutoff) { activeMovingObjectsCache.remove(entry.getKey()); + selectedTrackByFlightNo.remove(entry.getKey()); return true; } return false; diff --git a/qaup-collision/src/main/java/com/qaup/collision/websocket/broadcaster/WebSocketMessageBroadcaster.java b/qaup-collision/src/main/java/com/qaup/collision/websocket/broadcaster/WebSocketMessageBroadcaster.java index 9aace8f..80ec6ba 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/websocket/broadcaster/WebSocketMessageBroadcaster.java +++ b/qaup-collision/src/main/java/com/qaup/collision/websocket/broadcaster/WebSocketMessageBroadcaster.java @@ -42,6 +42,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @Component public class WebSocketMessageBroadcaster { + private static final int REACQUIRE_AFTER_REJECTS = 3; private static final double EARTH_RADIUS_METERS = 6371000.0; private static final double MIN_DT_SECONDS = 0.05; @@ -116,9 +117,6 @@ public class WebSocketMessageBroadcaster { @Value("${websocket.position.filter.state-ttl-ms:120000}") private long positionTrackStateTtlMs; - @Value("${websocket.position.filter.reacquire-gap-ms:8000}") - private long reacquireGapMs; - private record CoalescedPositionUpdate(PositionUpdatePayload payload, long timestamp) {} private static final class PositionTrackState { @@ -393,7 +391,7 @@ public class WebSocketMessageBroadcaster { } long payloadTimestamp = payload.getTimestamp() != null ? payload.getTimestamp() : System.currentTimeMillis(); - long filterTimestampMs = System.currentTimeMillis(); + long filterTimestampMs = payloadTimestamp; PositionTrackState state = positionTrackStates.computeIfAbsent(payload.getObjectId(), k -> new PositionTrackState()); double filteredLatitude = latitude; @@ -404,8 +402,11 @@ public class WebSocketMessageBroadcaster { initializeTrackState(state, latitude, longitude, filterTimestampMs); } else { if (filterTimestampMs <= state.lastPayloadTimestampMs) { - state.rejectedCount++; - return null; + // Use local monotonic time to avoid dropping legitimate points on equal/out-of-order source timestamps. + filterTimestampMs = System.currentTimeMillis(); + if (filterTimestampMs <= state.lastPayloadTimestampMs) { + filterTimestampMs = state.lastPayloadTimestampMs + 1; + } } long dtMs = filterTimestampMs - state.lastPayloadTimestampMs; @@ -418,42 +419,50 @@ public class WebSocketMessageBroadcaster { ); double rawSpeedMps = rawDistance / Math.max(dtSec, MIN_DT_SECONDS); AdaptiveFilterParams adaptive = resolveAdaptiveFilterParams(payload.getObjectType(), rawSpeedMps); + boolean aircraft = isAircraftType(payload.getObjectType()); double maxAllowedJump = resolveMaxAllowedJumpMeters(adaptive.maxSpeedMps(), adaptive.jumpMarginMeter(), dtSec); boolean isOutlierJump = rawDistance > maxAllowedJump; + if (isOutlierJump && !aircraft && state.rejectedCount < REACQUIRE_AFTER_REJECTS - 1) { + state.rejectedCount++; + log.debug("Drop outlier position update: objectId={}, jumpMeter={}, allowedMeter={}", + payload.getObjectId(), rawDistance, maxAllowedJump); + return null; + } if (isOutlierJump) { - boolean longGapReacquire = dtMs >= Math.max(reacquireGapMs, 0L); - if (longGapReacquire) { - state.rejectedCount = 0; - filteredLatitude = latitude; - filteredLongitude = longitude; - } else { - state.rejectedCount++; - double[] clipped = clipPointByMaxDistance( - state.lastFilteredLatitude, - state.lastFilteredLongitude, - latitude, - longitude, - maxAllowedJump - ); - filteredLatitude = clipped[0]; - filteredLongitude = clipped[1]; - log.debug("Clip outlier position update: objectId={}, jumpMeter={}, allowedMeter={}, dtMs={}", - payload.getObjectId(), rawDistance, maxAllowedJump, dtMs); - } + state.rejectedCount = 0; + filteredLatitude = latitude; + filteredLongitude = longitude; } else { state.rejectedCount = 0; - if (rawDistance <= adaptive.jitterMeter()) { - filteredLatitude = state.lastFilteredLatitude; - filteredLongitude = state.lastFilteredLongitude; + if (aircraft) { + if (rawDistance <= adaptive.jitterMeter()) { + filteredLatitude = state.lastFilteredLatitude; + filteredLongitude = state.lastFilteredLongitude; + } else { + double alpha = resolveAircraftDynamicAlpha(adaptive.emaAlpha(), rawDistance, maxAllowedJump); + filteredLatitude = state.lastFilteredLatitude + alpha * (latitude - state.lastFilteredLatitude); + filteredLongitude = state.lastFilteredLongitude + alpha * (longitude - state.lastFilteredLongitude); + } } else { - double alpha = clamp(adaptive.emaAlpha(), 0.05, 1.0); - filteredLatitude = state.lastFilteredLatitude + alpha * (latitude - state.lastFilteredLatitude); - filteredLongitude = state.lastFilteredLongitude + alpha * (longitude - state.lastFilteredLongitude); + if (rawDistance <= adaptive.jitterMeter()) { + filteredLatitude = state.lastFilteredLatitude; + filteredLongitude = state.lastFilteredLongitude; + } else { + double alpha = clamp(adaptive.emaAlpha(), 0.05, 1.0); + filteredLatitude = state.lastFilteredLatitude + alpha * (latitude - state.lastFilteredLatitude); + filteredLongitude = state.lastFilteredLongitude + alpha * (longitude - state.lastFilteredLongitude); + } } } + if (isOutlierJump && aircraft) { + // Aircraft points should not be dropped to avoid visual path gaps. + filteredLatitude = latitude; + filteredLongitude = longitude; + } + state.lastRawLatitude = latitude; state.lastRawLongitude = longitude; state.lastPayloadTimestampMs = filterTimestampMs; @@ -478,6 +487,18 @@ public class WebSocketMessageBroadcaster { .build(); } + private boolean isAircraftType(String objectType) { + return "AIRCRAFT".equals(objectType); + } + + private double resolveAircraftDynamicAlpha(double baseAlpha, double rawDistance, double maxAllowedJump) { + double normalized = maxAllowedJump <= 0.0 ? 1.0 : clamp(rawDistance / maxAllowedJump, 0.0, 1.0); + // Keep smoothness at low speed, but reduce lag quickly when displacement grows. + double minAlpha = Math.max(0.35, baseAlpha); + double maxAlpha = 0.92; + return clamp(minAlpha + (maxAlpha - minAlpha) * normalized, 0.05, 1.0); + } + private void initializeTrackState(PositionTrackState state, double latitude, double longitude, long payloadTimestamp) { state.initialized = true; state.lastPayloadTimestampMs = payloadTimestamp; @@ -569,21 +590,4 @@ public class WebSocketMessageBroadcaster { } return Math.min(value, max); } - - private double[] clipPointByMaxDistance( - double startLatitude, - double startLongitude, - double targetLatitude, - double targetLongitude, - double maxDistanceMeters) { - double distanceMeters = calculateDistanceMeters(startLatitude, startLongitude, targetLatitude, targetLongitude); - if (distanceMeters <= maxDistanceMeters || distanceMeters <= 0.0 || maxDistanceMeters <= 0.0) { - return new double[] {targetLatitude, targetLongitude}; - } - - double ratio = maxDistanceMeters / distanceMeters; - double clippedLatitude = startLatitude + (targetLatitude - startLatitude) * ratio; - double clippedLongitude = startLongitude + (targetLongitude - startLongitude) * ratio; - return new double[] {clippedLatitude, clippedLongitude}; - } } diff --git a/命令.md b/命令.md index 6c78414..4c777b6 100644 --- a/命令.md +++ b/命令.md @@ -134,8 +134,6 @@ docker exec -it qaup-app sh -lc 'sha256sum /app.jar' docker exec -it qaup-app rm /app.jar docker cp qaup-app.jar qaup-app:/app.jar docker restart qaup-app -curl -s http://10.64.58.228:8086/api/adxp/status -curl -s http://10.64.58.228:8086/api/adxp/health curl -s -X POST http://10.64.58.228:8086/api/adxp/reconnect docker logs -f qaup-app | grep "路由" ``` @@ -304,4 +302,51 @@ for F in MU9692; do BR=$(docker exec -i "qaup-redis" redis-cli HMGET "flightBiz:$BIZ" startSeat seat | tr '\n' ' ') fi echo "$F | biz=$BIZ | flight=[$FR] | biz=[$BR]" -done \ No newline at end of file +done + + +grep -oE 'TISFLIGHT' ./adxp-adapter/logs/adapter-logs.log | sort | uniq -c | sort -nr + + +``` +TOKEN=$(echo "$LOGIN_RESP" | sed -n 's/.*"data":"\([^"]*\)".*/\1/p') +# B. 连续观察 QDA9846(无 jq 版本) +for i in $(seq 1 20); do + TS=$(date '+%H:%M:%S') + RESP=$(curl -sS -H "Authorization: $TOKEN" "http://10.32.38.3:8090/openApi/getCurrentFlightPositions") + LINE=$(echo "$RESP" | tr '{' '\n' | grep '"flightNo":"FGCX253"' | head -n1) + if [ -z "$LINE" ]; then + echo "[$TS] NO_HIT" + else + echo "[$TS] $LINE" + fi + sleep 1 +done + +root@root:/home/project_20250804/qaup# for i in $(seq 1 20); do TS=$(date '+%H:%M:%S'); RESP=$(curl -sS -H "Authorization: $TOKEN" "http://10.32.38.3:8090/openApi/getCurrentFlightPositions"); LINE=$(echo "$RESP" | tr '{' '\n' | grep '"flightNo":"FGCX253"' | head -n1); if [ -z "$LINE" ]; then echo "[$TS] NO_HIT"; else echo "[$TS] $LINE"; fi; sleep 1; done +[05:59:44] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35046,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:45] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35046,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:46] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35046,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:47] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:48] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:49] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:51] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:52] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:53] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:54] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35044,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:55] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:56] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:57] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:58] "flightNo":"FGCX253","longitude":120.09361,"latitude":36.35045,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[05:59:59] "flightNo":"FGCX253","longitude":120.09372,"latitude":36.35048,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[06:00:00] "flightNo":"FGCX253","longitude":120.09372,"latitude":36.35048,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[06:00:01] "flightNo":"FGCX253","longitude":120.09372,"latitude":36.35048,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[06:00:02] "flightNo":"FGCX253","longitude":120.09396,"latitude":36.35043,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[06:00:03] "flightNo":"FGCX253","longitude":120.094,"latitude":36.35036,"time":1770760653475,"altitude":0.0,"trackNumber":0}, +[06:00:04] "flightNo":"FGCX253","longitude":120.09401,"latitude":36.3503,"time":1770760653475,"altitude":0.0,"trackNumber":0}, + + +``` + + +