using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using TrainInfomationProviderService.StationInfo; using TrainInfomationProviderService.StationInfo.Entities; namespace TrainInfomationProviderService.TrainInfo.Entities { public class TrainTransitOnceResultCollection : ICollection { List _list = new List(); TrainTransitSearchOptions _options; /// /// 最推荐的几个线路 /// public List TopMostRecommandLines { get; private set; } /// /// 日期 /// public DateTime Date { get; private set; } /// /// 出发城市编码 /// public string FromCode { get; private set; } /// /// 到达城市编码 /// public string ToCode { get; private set; } /// /// 出发城市信息 /// public StationDetailInfo From { get; private set; } /// /// 到达城市信息 /// public StationDetailInfo To { get; private set; } /// /// 创建 的新实例(TrainTransitOnceResultCollection) /// /// /// /// /// public TrainTransitOnceResultCollection(DateTime date, TrainTransitSearchOptions options, string fromCode, string toCode) { ToCode = toCode; FromCode = fromCode; Date = date; _options = options; From = StationManager.Instance.Storage.Stations.GetValue(fromCode); To = StationManager.Instance.Storage.Stations.GetValue(toCode); } #region Implementation of IEnumerable /// /// 返回一个循环访问集合的枚举器。 /// /// /// 可用于循环访问集合的 。 /// public IEnumerator GetEnumerator() { return _list.GetEnumerator(); } /// /// 返回一个循环访问集合的枚举数。 /// /// /// 一个可用于循环访问集合的 对象。 /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region Implementation of ICollection /// /// 将某项添加到 中。 /// /// 要添加到 的对象。 为只读。 public void Add(TrainTransitOnceResult item) { if (item == null || !CheckTransitLineAvailable(item)) return; _list.Add(item); } /// /// 从 中移除所有项。 /// /// 为只读。 public void Clear() { _list.Clear(); } /// /// 确定 是否包含特定值。 /// /// /// 如果在 中找到 ,则为 true;否则为 false。 /// /// 要在 中定位的对象。 public bool Contains(TrainTransitOnceResult item) { return _list.Contains(item); } /// /// 从特定的 索引开始,将 的元素复制到一个 中。 /// /// 作为从 复制的元素的目标的一维 必须具有从零开始的索引。 中从零开始的索引,从此索引处开始进行复制。 为 null。 小于 0。 中的元素数目大于从 到目标 末尾之间的可用空间。 public void CopyTo(TrainTransitOnceResult[] array, int arrayIndex) { _list.CopyTo(array, arrayIndex); } /// /// 从 中移除特定对象的第一个匹配项。 /// /// /// 如果已从 中成功移除 ,则为 true;否则为 false。 如果在原始 中没有找到 ,该方法也会返回 false。 /// /// 要从 中移除的对象。 为只读。 public bool Remove(TrainTransitOnceResult item) { return _list.Remove(item); } /// /// 获取 中包含的元素数。 /// /// /// 中包含的元素个数。 /// public int Count { get { return _list.Count; } } /// /// 获取一个值,该值指示 是否为只读。 /// /// /// 如果 为只读,则为 true;否则为 false。 /// public bool IsReadOnly { get { return false; } } public TrainTransitSearchOptions Options { get { return _options; } } #endregion #region 辅助函数 static int GetMinimalSpace(Train train, bool notRecommand) { var extraTime = notRecommand ? 60 : 0; switch (train.TrainClass) { case 'C': case 'G': return 30 + extraTime; case 'D': return 30 + extraTime; case 'Z': return 60 + extraTime; case 'T': return 60 + extraTime; default: return 60 + extraTime; } } static int GetMaxiumSpace(Train train, bool notRecommand) { var extraTime = notRecommand ? 60 : 0; switch (train.TrainClass) { case 'C': case 'G': return 150 + extraTime; case 'D': return 150 + extraTime; case 'Z': return 180 + extraTime; case 'T': return 180 + extraTime; default: return 240 + extraTime; } } /// /// 获得比较理想中位时间 /// /// /// static int GetHighQualityTimeSpace(Train train, bool notRecommand) { var extraTime = notRecommand ? 60 : 0; switch (train.TrainClass) { case 'C': case 'G': return 60 + extraTime; case 'D': return 60 + extraTime; case 'Z': return 120 + extraTime; case 'T': return 120 + extraTime; default: return 120 + extraTime; } } #endregion #region 验证车次换乘是否有效 bool CheckTransitLineAvailable(TrainTransitOnceResult item) { //总共时间 if (item.First.CalculatedMinutesBase + item.Second.CalculatedMinutesBase > _options.MaxExtraMinutes) //时间过久 return false; //时间衔接 var arriveFirst = Date.Add(item.First.To.Arrive.Value); var leftSecond = arriveFirst.Date.Add(item.Second.From.Left.Value); //早于发车 if (leftSecond <= arriveFirst) return false; var space = (int)(leftSecond - arriveFirst).TotalMinutes; if (space < GetMinimalSpace(item.First.Train, item.NotRecommand) || space > GetMaxiumSpace(item.Second.Train, item.NotRecommand)) return false; return true; } #endregion #region 对列表进行二次优化 /// /// 二次优化,排除小站等。 /// internal void SecondaryAnalyze() { //对于前车进行分组优化 RemoveSmallStation(); //排序 ProcessPriority(); } /// /// 移除小站 /// void RemoveSmallStation() { var groups = this.GroupBy(s => s.First.Train.Id).Select(s => new { s.Key, Array = s.ToArray() }).ToArray(); foreach (var g in groups) { if (g.Array.Length < 2) continue; Array.Sort(g.Array, (s1, s2) => { var st1 = s1.First.To.StopTime.TotalMinutes; var st2 = s2.First.To.StopTime.TotalMinutes; if (st1 < st2) return 1; if (st1 > st2) return -1; return 0; }); foreach (var line in g.Array.Skip(Options.SameLineMaxKeepStations)) { _list.Remove(line); } } groups = this.GroupBy(s => s.Second.Train.Id).Select(s => new { s.Key, Array = s.ToArray() }).ToArray(); foreach (var g in groups) { if (g.Array.Length < 2) continue; Array.Sort(g.Array, (s1, s2) => { var st1 = s1.Second.From.StopTime.TotalMinutes; var st2 = s2.Second.From.StopTime.TotalMinutes; if (st1 < st2) return 1; if (st1 > st2) return -1; return 0; }); foreach (var line in g.Array.Skip(Options.SameLineMaxKeepStations)) { _list.Remove(line); } } //如果线路数超过五条,则砍掉时间太多的 if (Options.StartCutLongRunningCount <= _list.Count) { //基础时间 var times = _list.Select(s => s.FirstElapsedTime.Add(s.SecondElapsedTime)).OrderBy(s => s).Min().TotalMinutes; var maxTime = times * Options.CutLongRunningRate; Array.ForEach(_list.Where(s => (s.SecondElapsedTime + s.FirstElapsedTime).TotalMinutes > maxTime).ToArray(), _ => _list.Remove(_)); } } #endregion #region 分组排序 public string[] CityOrder { get; private set; } public int[] TopLinesIndices { get; private set; } void ProcessPriority() { //对换乘站点计算优先级 var stData = _list.GroupBy(s => s.First.ToStation).Select(s => new KeyValuePair(s.Key, s.ToArray())).ToList(); stData.Sort((x, y) => { //前车高铁/动车等,优先推荐 var fhc1 = x.Value.Count(_ => _.First.Train.IsHighSpeedClass); var shc1 = y.Value.Count(_ => _.First.Train.IsHighSpeedClass); if (shc1 != fhc1) return shc1 > fhc1 ? 1 : -1; //否则推荐后车的高铁/动车 var fhc2 = x.Value.Count(_ => _.Second.Train.IsHighSpeedClass); var shc2 = y.Value.Count(_ => _.Second.Train.IsHighSpeedClass); if (shc2 != fhc2) return shc1 > fhc1 ? 1 : -1; //如果都米有,则按线路数推荐 if (x.Value.Length != y.Value.Length) return y.Value.Length - x.Value.Length; //线路数也相同。。。。那好像没啥再区别的了 return 0; }); CityOrder = stData.Select(s => s.Key.Code).ToArray(); //对应起排序 var stationWeight = new Dictionary(stData.Count); for (var i = 0; i < stData.Count; i++) { stationWeight.Add(stData[i].Key, i); } //分组排序 _list.Sort((x, y) => { //不推荐,按不推荐来排序 if (x.NotRecommand ^ y.NotRecommand) return x.NotRecommand ? 1 : -1; //按始发终到排序 var wx = (x.First.IsBegin ? 10 : 0) + (x.First.IsEnd ? 10 : 0) + (x.Second.IsBegin ? 10 : 0) + (x.Second.IsEnd ? 10 : 0); var wy = (y.First.IsBegin ? 10 : 0) + (y.First.IsEnd ? 10 : 0) + (y.Second.IsBegin ? 10 : 0) + (y.Second.IsEnd ? 10 : 0); if (wx != wy) return wx < wy ? 1 : -1; //按前车是否是高速车排序 if (x.First.Train.IsHighSpeedClass ^ y.First.Train.IsHighSpeedClass) return x.First.Train.IsHighSpeedClass ? -1 : 1; //按后车是否是高速车排序 if (x.Second.Train.IsHighSpeedClass ^ y.Second.Train.IsHighSpeedClass) return x.Second.Train.IsHighSpeedClass ? -1 : 1; //如果到的时间是不推荐的时间 var xIsTimeNotRecommand = x.FirstTrainArriveDate.Hour < 6 || x.FirstTrainArriveDate.Hour >= 23; var yIsTimeNotRecommand = y.FirstTrainArriveDate.Hour < 6 || y.FirstTrainArriveDate.Hour >= 23; if (xIsTimeNotRecommand ^ yIsTimeNotRecommand) return xIsTimeNotRecommand ? 1 : -1; //如果发车时间是不推荐的时间 var xIsBeginTimeNotRecommand = x.FirstTrainLeftDate.Hour < 6 || x.FirstTrainLeftDate.Hour >= 23; var yIsBeginTimeNotRecommand = y.FirstTrainLeftDate.Hour < 6 || y.FirstTrainLeftDate.Hour >= 23; if (xIsBeginTimeNotRecommand ^ yIsBeginTimeNotRecommand) return xIsBeginTimeNotRecommand ? 1 : -1; //如果到的时间是不推荐的时间 var xIsEndTimeNotRecommand = x.SecondTrainArriveDate.Hour < 6 || x.SecondTrainArriveDate.Hour >= 23; var yIsEndTimeNotRecommand = y.SecondTrainArriveDate.Hour < 6 || y.SecondTrainArriveDate.Hour >= 23; if (xIsEndTimeNotRecommand ^ yIsEndTimeNotRecommand) return xIsEndTimeNotRecommand ? 1 : -1; //按总耗时排序 var ttl1 = x.First.ElapsedTime + x.SecondElapsedTime; var ttl2 = y.First.ElapsedTime + y.SecondElapsedTime; if (ttl1 != ttl2) return ttl1 < ttl2 ? -1 : 1; return 0; }); TopMostRecommandLines = _list.Take(Options.TopMostRecommandCount).ToList(); _list.Sort((x, y) => { //不推荐,按不推荐来排序 if (x.NotRecommand ^ y.NotRecommand) return x.NotRecommand ? 1 : -1; //车站不同,按车站来排序 if (x.First.To != y.First.To) { return stationWeight[x.First.ToStation] - stationWeight[y.First.ToStation]; } return 0; }); TopLinesIndices = TopMostRecommandLines.Select(s => _list.IndexOf(s)).ToArray(); } #endregion } }