/// <summary> /// 计算路段小时状态 /// </summary> /// <param name="sectionFlow">路段流量</param> private void CaculateSectionHourStatus(SectionFlow sectionFlow) { if (!_sectionStatuses.ContainsKey(sectionFlow.SectionId)) { _sectionStatuses.Add(sectionFlow.SectionId, new SectionStatus { SectionId = sectionFlow.SectionId, DateTime = new DateTime(sectionFlow.DateTime.Year, sectionFlow.DateTime.Month, sectionFlow.DateTime.Day, sectionFlow.DateTime.Hour, 0, 0) }); } if (sectionFlow.TrafficStatus == TrafficStatus.通畅) { _sectionStatuses[sectionFlow.SectionId].Good += 1; } else if (sectionFlow.TrafficStatus == TrafficStatus.基本通畅) { _sectionStatuses[sectionFlow.SectionId].Normal += 1; } else if (sectionFlow.TrafficStatus == TrafficStatus.轻度拥堵) { _sectionStatuses[sectionFlow.SectionId].Warning += 1; } else if (sectionFlow.TrafficStatus == TrafficStatus.中度拥堵) { _sectionStatuses[sectionFlow.SectionId].Bad += 1; } else if (sectionFlow.TrafficStatus == TrafficStatus.严重拥堵) { _sectionStatuses[sectionFlow.SectionId].Dead += 1; } }
/// <summary> /// 查询通道分钟流量状态 /// </summary> /// <param name="channelId">通道编号</param> /// <returns>通道流量状态</returns> public ChannelMinuteFlow QueryChannelMuniteStatus(string channelId) { channelId = Uri.UnescapeDataString(channelId); ChannelMinuteFlow channelMinuteFlow = new ChannelMinuteFlow { LanesFlow = new List <LaneFlow>() }; FlowChannel channel = _memoryCache.GetChannel(channelId); if (channel != null) { if (channel.SectionId.HasValue) { SectionFlow sectionFlow = _memoryCache.GetSectionLastFlow(channel.SectionId.Value); if (sectionFlow == null) { channelMinuteFlow.SectionFlow = new SectionFlow { SectionId = channel.SectionId.Value, SectionName = channel.RoadSection.SectionName }; } else { _memoryCache.FillSectionFlowCache(sectionFlow); channelMinuteFlow.SectionFlow = sectionFlow; } } foreach (Lane lane in channel.Lanes) { LaneFlow f = _memoryCache.GetLaneLastFlow(lane.DataId); if (f == null) { channelMinuteFlow.LanesFlow.Add(new LaneFlow { DataId = lane.DataId, LaneName = lane.LaneName }); } else { _memoryCache.FillLaneFlow(f); channelMinuteFlow.LanesFlow.Add(f); } } } return(channelMinuteFlow); }
/// <summary> /// 设置路段小时流量 /// </summary> /// <param name="distributedCache">缓存</param> /// <param name="flow">路段小时流量</param> public static void SetSectionHourFlow( this IDistributedCache distributedCache, SectionFlow flow) { SectionFlow flowCache = GetSectionHourFlow(distributedCache, flow.SectionId, flow.DateTime); if (flowCache == null) { flowCache = flow; } else { flowCache.Vkt += flow.Vkt; flowCache.TravelTimeProportion += flow.TravelTimeProportion; flowCache.Count += flow.Count; } distributedCache.SetString( GetSectionHourFlowKey(flowCache.SectionId, flowCache.DateTime), JsonConvert.SerializeObject(flowCache), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) }); }
/// <summary> /// 设置路段最后流量缓存 /// </summary> /// <param name="memoryCache">缓存实例</param> /// <param name="flow">流量数据</param> public static void SetSectionLastFlow(this IMemoryCache memoryCache, SectionFlow flow) { memoryCache.Set(GetSectionLastFlowKey(flow.SectionId), flow, TimeSpan.FromMinutes(5)); }
/// <summary> /// 填充路段流量缓存 /// </summary> /// <param name="memoryCache">缓存</param> /// <param name="sectionFlow">车道流量</param> /// <returns>路段流量</returns> public static SectionFlow FillSectionFlowCache(this IMemoryCache memoryCache, SectionFlow sectionFlow) { if (sectionFlow != null) { RoadSection section = memoryCache.GetSection(sectionFlow.SectionId); if (section != null) { sectionFlow.SectionName = section.SectionName; } } return(sectionFlow); }
/// <summary> /// 设置路段当天流量 /// </summary> /// <param name="distributedCache">缓存</param> /// <param name="flow">路段当天流量</param> public static void SetSectionDayFlow( this IDistributedCache distributedCache, SectionFlow flow) { SectionFlow flowCache = GetSectionDayFlow(distributedCache, flow.SectionId, flow.DateTime) ?? new SectionFlow { SectionId = flow.SectionId, SectionType = flow.SectionType, DateTime = flow.DateTime, //平均速度 Vkt = flow.Vkt, Fls = flow.Fls, //拥堵状态 //这里需要设置为通常才能触发如果是拥堵则设置开始时间的条件 TrafficStatus = TrafficStatus.通畅, CurrentCongestionSpan = flow.CurrentCongestionSpan, CongestionSpan = flow.CongestionSpan, CongestionStartTime = flow.CongestionStartTime }; if (flow.TrafficStatus == TrafficStatus.通畅) { flowCache.CurrentCongestionSpan = 0; } else if (flow.TrafficStatus == TrafficStatus.基本通畅) { flowCache.CurrentCongestionSpan = 0; } else if (flow.TrafficStatus == TrafficStatus.轻度拥堵) { flowCache.CurrentCongestionSpan += 1; flowCache.CongestionSpan += 1; } else if (flow.TrafficStatus == TrafficStatus.中度拥堵) { flowCache.CurrentCongestionSpan += 1; flowCache.CongestionSpan += 1; } else { flowCache.CurrentCongestionSpan += 1; flowCache.CongestionSpan += 1; } if (flowCache.TrafficStatus < TrafficStatus.轻度拥堵 && flow.TrafficStatus >= TrafficStatus.轻度拥堵) { flowCache.CongestionStartTime = flow.DateTime; } flowCache.TrafficStatus = flow.TrafficStatus; flowCache.Total += flow.Total; flowCache.Vkt += flow.Vkt; flowCache.Fls += flow.Fls; flowCache.TravelTimeProportion += flow.TravelTimeProportion; flowCache.Count += flow.Count; distributedCache.SetString( GetSectionDayFlowKey(flowCache.SectionId, flowCache.DateTime), JsonConvert.SerializeObject(flowCache), new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) }); }
/// <summary> /// 获取城市小时拥堵指数 /// </summary> /// <param name="hour">时间</param> /// <returns>拥堵指数</returns> private double GetHourCongestionData(DateTime hour) { Dictionary <int, List <SectionFlow> > sectionTypeHourFlows = Enum.GetValues(typeof(SectionType)).Cast <SectionType>().ToDictionary(s => (int)s, s => new List <SectionFlow>()); foreach (var section in _memoryCache.GetSections()) { SectionFlow flow = _distributedCache.GetSectionHourFlow(section.SectionId, hour); if (flow != null) { sectionTypeHourFlows[flow.SectionType].Add(flow); } } double totalHourVkts = sectionTypeHourFlows.Sum(p => p.Value.Sum(f => f.Vkt)); //本小时拥堵指数 if (totalHourVkts > 0) { double totalTravelTimeProportion = sectionTypeHourFlows .Sum(p => p.Value.Sum(f => f.Count) == 0 ? 0 : p.Value.Sum(f => f.TravelTimeProportion) / p.Value.Sum(f => f.Count) * (p.Value.Sum(f => f.Vkt) / totalHourVkts)); if (totalTravelTimeProportion < 1) { totalTravelTimeProportion = 1; } double congestionData = (totalTravelTimeProportion - 1) / 0.3 * 2; if (totalTravelTimeProportion < 1.3) { congestionData += 0; } else if (totalTravelTimeProportion >= 1.3 && totalTravelTimeProportion < 1.6) { congestionData += 2; } else if (totalTravelTimeProportion >= 1.6 && totalTravelTimeProportion < 1.9) { congestionData += 4; } else if (totalTravelTimeProportion >= 1.9 && totalTravelTimeProportion < 2.2) { congestionData += 6; } else if (totalTravelTimeProportion >= 2.2 && totalTravelTimeProportion < 2.5) { congestionData += 8; } else { congestionData = 10; } return(congestionData); } else { return(0); } }
/// <summary> /// 计算路段当天流量 /// </summary> private void CalculateSectionDayFlow() { DateTime now = DateTime.Now; CityStatus cityStatus = new CityStatus { CongestionDatas = new Dictionary <int, double>(), SectionStatuses = Enum.GetValues(typeof(TrafficStatus)) .Cast <TrafficStatus>() .ToDictionary(trafficStatus => (int)trafficStatus, trafficStatus => new SectionsSpeed()) }; List <RoadSection> sections = _memoryCache.GetSections(); //计算当前分钟路段交通状态 Dictionary <TrafficStatus, List <SectionFlow> > trafficStatusFlows = Enum.GetValues(typeof(TrafficStatus)).Cast <TrafficStatus>().ToDictionary(trafficStatus => trafficStatus, trafficStatus => new List <SectionFlow>()); foreach (var section in sections) { SectionFlow flow = _memoryCache.GetSectionLastFlow(section.SectionId); if (flow != null) { trafficStatusFlows[flow.TrafficStatus].Add(flow); } } foreach (var pair in trafficStatusFlows) { //状态下路网总vkt double totalStatusVkts = pair.Value.Sum(f => f.Vkt); //状态下路网平均速度 double totalStatusAverageSpeed = 0.0; if (totalStatusVkts > 0) { //状态下路网平均速度 totalStatusAverageSpeed = (pair.Value.Sum(f => f.Fls) > 0 ? pair.Value.Sum(f => f.Vkt) / pair.Value.Sum(f => f.Fls) * (pair.Value.Sum(f => f.Vkt) / totalStatusVkts) : 0) * 3600 / 1000; } //当前交通状态 cityStatus.SectionStatuses[(int)pair.Key].AverageSpeed = Convert.ToInt32(totalStatusAverageSpeed); cityStatus.SectionStatuses[(int)pair.Key].SectionCount = pair.Value.Count; } //城市小时拥堵指数 DateTime today = now.Date; for (int h = 0; h < now.Hour; ++h) { double?congestionData = _memoryCache.GetCityHourCongestionData(today.AddHours(h)); if (!congestionData.HasValue) { congestionData = GetHourCongestionData(today.AddHours(h)); _memoryCache.SetCityHourCongestionData(today.AddHours(h), congestionData.Value); } cityStatus.CongestionDatas.Add(h, Math.Round(congestionData.Value, 1)); } cityStatus.CongestionDatas.Add(now.Hour, Math.Round(GetHourCongestionData(today.AddHours(now.Hour)), 1)); //今日总流量 foreach (var lane in _memoryCache.GetLanes()) { LaneFlow flow = _distributedCache.GetLaneDayFlow(lane.DataId, today); if (flow != null) { cityStatus.TotalFlow += flow.Total; } } //今日平均速度 Dictionary <int, List <SectionFlow> > sectionTypeDayFlows = Enum.GetValues(typeof(SectionType)).Cast <SectionType>().ToDictionary(s => (int)s, s => new List <SectionFlow>()); foreach (var section in sections) { SectionFlow flow = _distributedCache.GetSectionDayFlow(section.SectionId, today); if (flow != null) { _memoryCache.FillSectionFlowCache(flow); sectionTypeDayFlows[flow.SectionType].Add(flow); } } //路网总vkt double totalVkts = sectionTypeDayFlows.Sum(p => p.Value.Sum(f => f.Vkt)); if (totalVkts > 0) { cityStatus.AverageSpeed = Convert.ToInt64( sectionTypeDayFlows .Sum(p => p.Value.Sum(f => f.Fls) > 0 ? p.Value.Sum(f => f.Vkt) / p.Value.Sum(f => f.Fls) * (p.Value.Sum(f => f.Vkt) / totalVkts) : 0) * 3600 / 1000); } else { cityStatus.AverageSpeed = 0; } //拥堵排名 cityStatus.SectionCongestionRank = sectionTypeDayFlows .SelectMany(p => p.Value) .Where(f => f.CongestionSpan > 0) .OrderByDescending(f => f.CongestionSpan) .Take(10) .ToList(); //当前拥堵路段列表 cityStatus.SectionCongestions = sectionTypeDayFlows .SelectMany(p => p.Value) .Where(f => f.TrafficStatus >= TrafficStatus.轻度拥堵) .OrderByDescending(f => f.CongestionStartTime) .ToList(); Status = cityStatus; }
/// <summary> /// 保存路算分钟流量 /// </summary> /// <param name="sectionFlow">路段流量</param> private void SaveSectionMinuteFlow(SectionFlow sectionFlow) { _memoryCache.SetSectionLastFlow(sectionFlow); _distributedCache.SetSectionHourFlow(sectionFlow); _distributedCache.SetSectionDayFlow(sectionFlow); }
/// <summary> /// 计算所有路段分钟流量 /// </summary> /// <param name="time"></param> private void CalculateSectionMinuteFlow(DateTime time) { var totalLanes = _memoryCache.GetLanes(); time = time.Add(-_span); foreach (RoadSection section in _memoryCache.GetSections()) { List <Lane> sectionLanes = totalLanes .Where(l => l.Channel.SectionId.HasValue && l.Channel.SectionId.Value == section.SectionId) .ToList(); SectionFlow sectionFlow = new SectionFlow { SectionId = section.SectionId, DateTime = time, SectionType = section.SectionType, FreeSpeed = section.FreeSpeed, Length = section.Length }; int availableLanesCount = 0; foreach (var lane in sectionLanes) { LaneFlow laneFlow = _distributedCache.GetLaneMinuteFlow(lane.DataId, time); if (laneFlow != null) { sectionFlow.Total += laneFlow.Total; sectionFlow.Vehicle += laneFlow.Vehicle; sectionFlow.HeadDistance += laneFlow.HeadDistance; sectionFlow.TimeOccupancy += laneFlow.TimeOccupancy; sectionFlow.Occupancy += laneFlow.Occupancy; sectionFlow.Distance += laneFlow.Distance; sectionFlow.TravelTime += laneFlow.TravelTime; availableLanesCount += 1; } } if (availableLanesCount > 0) { //路段平均速度(千米/小时) double sectionAverageSpeed = sectionFlow.TravelTime > 0 ? (sectionFlow.Distance / sectionFlow.TravelTime * 3600 / 1000) : 0; //路段行程时间(秒) double sectionTravelTime = sectionAverageSpeed > 0 ? sectionFlow.Length / (sectionAverageSpeed * 1000 / 3600) : 0; //路段自由流行程时间(秒) double sectionFreeTravelTime = sectionFlow.FreeSpeed > 0 ? sectionFlow.Length / (sectionFlow.FreeSpeed * 1000 / 3600) : 0; //路段行程时间比 double sectionTravelTimeProportion = sectionTravelTime < sectionFreeTravelTime ? 1 : sectionTravelTime / sectionFreeTravelTime; //路段交通状态 TrafficStatus trafficStatus; if (sectionAverageSpeed > sectionFlow.FreeSpeed * 0.7) { trafficStatus = TrafficStatus.通畅; } else if (sectionAverageSpeed <= sectionFlow.FreeSpeed * 0.7 && sectionAverageSpeed > sectionFlow.FreeSpeed * 0.5) { trafficStatus = TrafficStatus.基本通畅; } else if (sectionAverageSpeed <= sectionFlow.FreeSpeed * 0.5 && sectionAverageSpeed > sectionFlow.FreeSpeed * 0.4) { trafficStatus = TrafficStatus.轻度拥堵; } else if (sectionAverageSpeed <= sectionFlow.FreeSpeed * 0.4 && sectionAverageSpeed > sectionFlow.FreeSpeed * 0.3) { trafficStatus = TrafficStatus.中度拥堵; } else { trafficStatus = TrafficStatus.严重拥堵; } sectionFlow.Count = 1; sectionFlow.AverageSpeed = sectionAverageSpeed; sectionFlow.HeadDistance = sectionFlow.HeadDistance / availableLanesCount; sectionFlow.Occupancy = sectionFlow.Occupancy / availableLanesCount; sectionFlow.TimeOccupancy = sectionFlow.TimeOccupancy / availableLanesCount; sectionFlow.TrafficStatus = trafficStatus; sectionFlow.Total = sectionFlow.Total; sectionFlow.Vehicle = sectionFlow.Vehicle; sectionFlow.Vkt = sectionFlow.Vehicle * sectionFlow.Length; sectionFlow.Fls = sectionFlow.Vehicle * sectionTravelTime; sectionFlow.TravelTimeProportion = sectionTravelTimeProportion; CaculateSectionHourStatus(sectionFlow); SaveSectionMinuteFlow(sectionFlow); _logger.LogDebug((int)LogEvent.路段流量, $"路段流量 id:{sectionFlow.SectionId} time:{sectionFlow.DateTime} total lanes:{sectionLanes.Count} available lanes:{availableLanesCount} type:{sectionFlow.SectionType} length:{sectionFlow.Length} free speed:{sectionFlow.FreeSpeed} average speed:{sectionFlow.AverageSpeed} head distance:{sectionFlow.HeadDistance} occ:{sectionFlow.Occupancy} tocc {sectionFlow.TimeOccupancy} status:{sectionFlow.TrafficStatus} total:{sectionFlow.Total} vehicle:{sectionFlow.Vehicle} vkt:{sectionFlow.Vkt} fls:{sectionFlow.Fls} ttp:{sectionFlow.TravelTimeProportion}"); } else { _logger.LogDebug((int)LogEvent.路段流量, $"路段流量计算失败 id:{sectionFlow.SectionId} time:{sectionFlow.DateTime} total lanes:{sectionLanes.Count} available lanes:{availableLanesCount}"); } } }