< Summary

Information
Class: CounterDrone.Core.Algorithms.DefaultDefenseAdvisor
Assembly: CounterDrone.Core
File(s): C:\Users\Tellme\apps\CounterDroneBackend\src\CounterDrone.Core\Algorithms\DefaultDefenseAdvisor.cs
Line coverage
94%
Covered lines: 132
Uncovered lines: 7
Coverable lines: 139
Total lines: 187
Line coverage: 94.9%
Branch coverage
83%
Covered branches: 20
Total branches: 24
Branch coverage: 83.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%22100%
.cctor()100%11100%
Recommend(...)81.81%222294.53%
ToV3(...)100%11100%

File(s)

C:\Users\Tellme\apps\CounterDroneBackend\src\CounterDrone.Core\Algorithms\DefaultDefenseAdvisor.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using CounterDrone.Core.Models;
 5
 6namespace CounterDrone.Core.Algorithms
 7{
 8    public class DefaultDefenseAdvisor : IDefenseAdvisor
 9    {
 10        private readonly List<AmmunitionSpec> _ammoCatalog;
 11
 1612        public DefaultDefenseAdvisor(List<AmmunitionSpec> ammoCatalog)
 1613        {
 1614            _ammoCatalog = ammoCatalog ?? new List<AmmunitionSpec>();
 1615        }
 16
 117        private static readonly Dictionary<PowerType, AerosolType> MatchTable = new()
 118        {
 119            { PowerType.Electric, AerosolType.InertGas },
 120            { PowerType.Piston,   AerosolType.InertGas },
 121            { PowerType.Jet,      AerosolType.ActiveMaterial },
 122        };
 23
 24        public DefenseRecommendation Recommend(ThreatProfile threat)
 1525        {
 1526            var result = new DefenseRecommendation
 1527            {
 1528                Best = new DefenseSolution(),
 1529                Critical = new DefenseSolution(),
 1530            };
 31
 32            // ===== 输入校验 =====
 1533            if (threat.Targets.Count == 0)
 134            {
 135                result.Best.SummaryRationale = "失败:未配置威胁目标";
 136                return result;
 37            }
 1438            if (threat.Waypoints.Count < 2)
 139            {
 140                result.Best.SummaryRationale = "失败:需要至少两个航路点";
 141                return result;
 42            }
 43
 1344            var target = threat.Targets[0];
 1345            if (target.TypicalSpeed <= 0)
 046            {
 047                result.Best.SummaryRationale = "失败:目标速度为 0";
 048                return result;
 49            }
 50
 51            // ===== Step A:气溶胶选型 =====
 1352            var powerType = (PowerType)target.PowerType;
 1353            var aerosolType = MatchTable.TryGetValue(powerType, out var match)
 1354                ? match : AerosolType.InertGas;
 55
 2756            var ammo = _ammoCatalog.FirstOrDefault(a => a.AerosolType == (int)aerosolType);
 1357            if (ammo == null)
 158            {
 159                result.Best.SummaryRationale = $"失败:弹药库中未找到 {aerosolType} 类型的弹药规格";
 160                return result;
 61            }
 62
 1263            var rationale = powerType switch
 1264            {
 1265                PowerType.Electric or PowerType.Piston =>
 1066                    $"{powerType}发动机依赖氧气,推荐惰性气体窒息方案",
 1267                PowerType.Jet =>
 268                    $"{powerType}发动机高温表面可触发活性材料爆燃反应",
 069                _ => "默认推荐惰性气体方案",
 1270            };
 71
 72            // ===== Step B:时空交汇优化 =====
 1273            var start = ToV3(threat.Waypoints[0]);
 1274            var end = ToV3(threat.Waypoints[^1]);
 1275            var mid = new Vector3((start.X + end.X) / 2f, (start.Y + end.Y) / 2f, (start.Z + end.Z) / 2f);
 1276            var routeLength = start.DistanceTo(end);
 1277            var avgSpeed = (float)target.TypicalSpeed / 3.6f;
 1278            var totalFlightTime = routeLength / avgSpeed;
 79
 1280            if (routeLength < 100)
 081            {
 082                result.Best.SummaryRationale = "失败:航路太短(<100m)";
 083                return result;
 84            }
 85
 86            // 弹药参数
 1287            var initialR = (float)ammo.InitialRadius;
 1288            var maxDur = (float)ammo.MaxDuration;
 89
 90            // 无人机穿过单个云团的时间
 1291            var crossTime = (2f * initialR) / avgSpeed;
 1292            var neededExposure = aerosolType == AerosolType.ActiveMaterial ? 2f : 6f;
 1293            var spacing = initialR * 1.5f; // 云团间隔
 94
 95            // 有效覆盖距离 = 间距×(N-1) + 直径,需 ≥ 所需暴露时间 × 速度
 1296            var requiredCoverage = neededExposure * avgSpeed;
 1297            var roundsNeeded = Math.Max(1,
 1298                (int)Math.Ceiling((requiredCoverage - 2f * initialR) / spacing) + 1);
 99
 100            // 实际有效暴露时间
 12101            var actualCoverage = spacing * (roundsNeeded - 1) + 2f * initialR;
 12102            var actualExposure = actualCoverage / avgSpeed;
 12103            var prob = Math.Min(0.95f, actualExposure / neededExposure);
 104
 105            // 多轮次云团:沿航路等距分布
 12106            var cloudSalvo = new List<CloudDispersal>();
 112107            for (int i = 0; i < roundsNeeded; i++)
 44108            {
 44109                var offset = (i - (roundsNeeded - 1) / 2f) * spacing;
 44110                cloudSalvo.Add(new CloudDispersal
 44111                {
 44112                    AerosolType = (int)aerosolType,
 44113                    PositionX = mid.X + offset,
 44114                    PositionY = mid.Y,
 44115                    PositionZ = mid.Z,
 44116                    DisperseHeight = (float)target.TypicalAltitude,
 44117                    TriggerMode = (int)TriggerMode.Time,
 44118                    Duration = (int)maxDur,
 44119                    InitialScale = ammo.InitialVolume,
 44120                    ReleaseMode = (int)ReleaseMode.Single,
 44121                    Source = "Algorithm",
 44122                    PositionMode = (int)PositionMode.AlgorithmRecommended,
 44123                    RecommendedTiming = totalFlightTime / 2f,
 44124                });
 44125            }
 126
 127            // 平台部署:假设齐射(同时发射),每门炮打一发后冷却 5s
 128            // 需要 roundsNeeded 门炮同时发射
 12129            var gunCount = roundsNeeded;
 12130            var platforms = new List<RecommendedPlatform>();
 112131            for (int i = 0; i < gunCount; i++)
 44132                platforms.Add(new RecommendedPlatform
 44133                {
 44134                    Type = PlatformType.GroundBased,
 44135                    Position = new Vector3(mid.X + i * 50, 0, 50),
 44136                    Quantity = 1,
 44137                    MunitionCount = 1,
 44138                    CoverageVolume = (float)ammo.InitialVolume,
 44139                    Cooldown = 5f,
 44140                    MuzzleVelocity = 800f,
 44141                });
 142
 12143            result.Best = new DefenseSolution
 12144            {
 12145                RecommendedAerosolType = aerosolType,
 12146                AerosolRationale = rationale,
 12147                RecommendedCloud = cloudSalvo[0],
 12148                CloudSalvo = cloudSalvo,
 12149                Platforms = platforms,
 12150                Detections = new List<RecommendedDetection>
 12151                {
 12152                    new RecommendedDetection
 12153                    {
 12154                        Position = new Vector3(mid.X, mid.Y, 0),
 12155                        DetectionRadius = Math.Max(3000f, routeLength * 0.4f),
 12156                        Quantity = 1,
 12157                    }
 12158                },
 12159                InterceptProbability = prob,
 12160                SummaryRationale = $"{aerosolType}方案,{roundsNeeded}发,预计拦截概率 {prob:P0}",
 12161            };
 162
 12163            result.Critical = new DefenseSolution
 12164            {
 12165                RecommendedAerosolType = aerosolType,
 12166                RecommendedCloud = new CloudDispersal
 12167                {
 12168                    AerosolType = (int)aerosolType,
 12169                    PositionX = start.X + (end.X - start.X) * 0.25f,
 12170                    PositionY = start.Y, PositionZ = start.Z,
 12171                    DisperseHeight = (float)target.TypicalAltitude,
 12172                    Duration = (int)maxDur,
 12173                    Source = "Algorithm",
 12174                    PositionMode = (int)PositionMode.AlgorithmRecommended,
 12175                    RecommendedTiming = totalFlightTime * 0.25f,
 12176                },
 12177                Platforms = platforms,
 12178                InterceptProbability = prob * 0.3f,
 12179                SummaryRationale = $"临界方案:拦截概率仅 {prob * 0.3f:P0}",
 12180            };
 181
 12182            return result;
 15183        }
 184
 24185        private static Vector3 ToV3(Waypoint wp) => new((float)wp.PosX, (float)wp.PosY, (float)wp.PosZ);
 186    }
 187}