337 lines
9.8 KiB
C#
337 lines
9.8 KiB
C#
|
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<TrainTransitOnceResult>
|
|||
|
{
|
|||
|
List<TrainTransitOnceResult> _list = new List<TrainTransitOnceResult>();
|
|||
|
TrainTransitSearchOptions _options;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 日期
|
|||
|
/// </summary>
|
|||
|
public DateTime Date { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 出发城市编码
|
|||
|
/// </summary>
|
|||
|
public string FromCode { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 到达城市编码
|
|||
|
/// </summary>
|
|||
|
public string ToCode { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 出发城市信息
|
|||
|
/// </summary>
|
|||
|
public StationDetailInfo From { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 到达城市信息
|
|||
|
/// </summary>
|
|||
|
public StationDetailInfo To { get; private set; }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 创建 <see cref="TrainTransitOnceResultCollection"/> 的新实例(TrainTransitOnceResultCollection)
|
|||
|
/// </summary>
|
|||
|
/// <param name="options"></param>
|
|||
|
/// <param name="date"></param>
|
|||
|
/// <param name="fromCode"></param>
|
|||
|
/// <param name="toCode"></param>
|
|||
|
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
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回一个循环访问集合的枚举器。
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// 可用于循环访问集合的 <see cref="T:System.Collections.Generic.IEnumerator`1"/>。
|
|||
|
/// </returns>
|
|||
|
public IEnumerator<TrainTransitOnceResult> GetEnumerator()
|
|||
|
{
|
|||
|
return _list.GetEnumerator();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 返回一个循环访问集合的枚举数。
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// 一个可用于循环访问集合的 <see cref="T:System.Collections.IEnumerator"/> 对象。
|
|||
|
/// </returns>
|
|||
|
IEnumerator IEnumerable.GetEnumerator()
|
|||
|
{
|
|||
|
return GetEnumerator();
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region Implementation of ICollection<TrainTransitOnceResult>
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 将某项添加到 <see cref="T:System.Collections.Generic.ICollection`1"/> 中。
|
|||
|
/// </summary>
|
|||
|
/// <param name="item">要添加到 <see cref="T:System.Collections.Generic.ICollection`1"/> 的对象。</param><exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> 为只读。</exception>
|
|||
|
public void Add(TrainTransitOnceResult item)
|
|||
|
{
|
|||
|
if (item == null || !CheckTransitLineAvailable(item))
|
|||
|
return;
|
|||
|
|
|||
|
_list.Add(item);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中移除所有项。
|
|||
|
/// </summary>
|
|||
|
/// <exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> 为只读。</exception>
|
|||
|
public void Clear()
|
|||
|
{
|
|||
|
_list.Clear();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 确定 <see cref="T:System.Collections.Generic.ICollection`1"/> 是否包含特定值。
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// 如果在 <see cref="T:System.Collections.Generic.ICollection`1"/> 中找到 <paramref name="item"/>,则为 true;否则为 false。
|
|||
|
/// </returns>
|
|||
|
/// <param name="item">要在 <see cref="T:System.Collections.Generic.ICollection`1"/> 中定位的对象。</param>
|
|||
|
public bool Contains(TrainTransitOnceResult item)
|
|||
|
{
|
|||
|
return _list.Contains(item);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 从特定的 <see cref="T:System.Array"/> 索引开始,将 <see cref="T:System.Collections.Generic.ICollection`1"/> 的元素复制到一个 <see cref="T:System.Array"/> 中。
|
|||
|
/// </summary>
|
|||
|
/// <param name="array">作为从 <see cref="T:System.Collections.Generic.ICollection`1"/> 复制的元素的目标的一维 <see cref="T:System.Array"/>。 <see cref="T:System.Array"/> 必须具有从零开始的索引。</param><param name="arrayIndex"><paramref name="array"/> 中从零开始的索引,从此索引处开始进行复制。</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> 为 null。</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> 小于 0。</exception><exception cref="T:System.ArgumentException">源 <see cref="T:System.Collections.Generic.ICollection`1"/> 中的元素数目大于从 <paramref name="arrayIndex"/> 到目标 <paramref name="array"/> 末尾之间的可用空间。</exception>
|
|||
|
public void CopyTo(TrainTransitOnceResult[] array, int arrayIndex)
|
|||
|
{
|
|||
|
_list.CopyTo(array, arrayIndex);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中移除特定对象的第一个匹配项。
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// 如果已从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中成功移除 <paramref name="item"/>,则为 true;否则为 false。 如果在原始 <see cref="T:System.Collections.Generic.ICollection`1"/> 中没有找到 <paramref name="item"/>,该方法也会返回 false。
|
|||
|
/// </returns>
|
|||
|
/// <param name="item">要从 <see cref="T:System.Collections.Generic.ICollection`1"/> 中移除的对象。</param><exception cref="T:System.NotSupportedException"><see cref="T:System.Collections.Generic.ICollection`1"/> 为只读。</exception>
|
|||
|
public bool Remove(TrainTransitOnceResult item)
|
|||
|
{
|
|||
|
return _list.Remove(item);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取 <see cref="T:System.Collections.Generic.ICollection`1"/> 中包含的元素数。
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// <see cref="T:System.Collections.Generic.ICollection`1"/> 中包含的元素个数。
|
|||
|
/// </returns>
|
|||
|
public int Count { get { return _list.Count; } }
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获取一个值,该值指示 <see cref="T:System.Collections.Generic.ICollection`1"/> 是否为只读。
|
|||
|
/// </summary>
|
|||
|
/// <returns>
|
|||
|
/// 如果 <see cref="T:System.Collections.Generic.ICollection`1"/> 为只读,则为 true;否则为 false。
|
|||
|
/// </returns>
|
|||
|
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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 获得比较理想中位时间
|
|||
|
/// </summary>
|
|||
|
/// <param name="train"></param>
|
|||
|
/// <returns></returns>
|
|||
|
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 对列表进行二次优化
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 二次优化,排除小站等。
|
|||
|
/// </summary>
|
|||
|
internal void SecondaryAnalyze()
|
|||
|
{
|
|||
|
//对于前车进行分组优化
|
|||
|
RemoveSmallStation();
|
|||
|
//排序
|
|||
|
ProcessPriority();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 移除小站
|
|||
|
/// </summary>
|
|||
|
void RemoveSmallStation()
|
|||
|
{
|
|||
|
var groups = this.GroupBy(s => s.First.Train.Code).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.Code).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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 分组排序
|
|||
|
|
|||
|
void ProcessPriority()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|