Skip to content

Commit

Permalink
Merge branch 'release'
Browse files Browse the repository at this point in the history
  • Loading branch information
terry-u16 committed Sep 27, 2015
2 parents 9f230ee + b30dfa1 commit 871e10d
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 1,039 deletions.
4 changes: 2 additions & 2 deletions MaterialChartPlugin/MaterialChartPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ namespace MaterialChartPlugin
[Export(typeof(IPlugin))]
[ExportMetadata("Guid", "56B66906-608A-4BCC-9FE2-6B3B0093F377")]
[ExportMetadata("Title", "MaterialChart")]
[ExportMetadata("Description", "資材の推移を折れ線グラフで表示します。")]
[ExportMetadata("Version", "1.0.0")]
[ExportMetadata("Description", "資材の推移をグラフで表示します。")]
[ExportMetadata("Version", "1.1.0")]
[ExportMetadata("Author", "@terry_u16")]
[Export(typeof(ITool))]
[Export(typeof(IRequestNotify))]
Expand Down
105 changes: 87 additions & 18 deletions MaterialChartPlugin/Models/MaterialLog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@
using System.Xml;
using Livet;
using ProtoBuf;
using Grabacr07.KanColleWrapper;

namespace MaterialChartPlugin.Models
{
public class MaterialLog : NotificationObject
{
static readonly string roamingDirectoryPath = Path.Combine(
static readonly string localDirectoryPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"terry_u16", "MaterialChartPlugin");

static readonly string exportDirectoryPath = "MaterialChartPlugin";
public static readonly string ExportDirectoryPath = "MaterialChartPlugin";

static readonly string saveFileName = "materiallog.dat";

static string FilePath => Path.Combine(roamingDirectoryPath, saveFileName);
static string SaveFilePath => Path.Combine(localDirectoryPath, saveFileName);

private MaterialChartPlugin plugin;

Expand All @@ -43,7 +44,6 @@ public bool HasLoaded
}
#endregion


public ObservableCollection<TimeMaterialsPair> History { get; private set; }

public MaterialLog(MaterialChartPlugin plugin)
Expand All @@ -53,60 +53,88 @@ public MaterialLog(MaterialChartPlugin plugin)

public async Task LoadAsync()
{
if (File.Exists(FilePath))
await LoadAsync(SaveFilePath, null);
}

private async Task LoadAsync(string filePath, Action onSuccess)
{
this.HasLoaded = false;

if (File.Exists(filePath))
{
try
{
using (var stream = File.OpenRead(FilePath))
using (var stream = File.OpenRead(filePath))
{
this.History = await Task.Run(() => Serializer.Deserialize<ObservableCollection<TimeMaterialsPair>>(stream));
}
onSuccess?.Invoke();
}
catch (ProtoException ex)
{
plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.LoadFailed", "読み込み失敗",
"資材データの読み込みに失敗しました。データが破損している可能性があります。"));
System.Diagnostics.Debug.WriteLine(ex);
this.History = new ObservableCollection<TimeMaterialsPair>();
if (this.History == null)
this.History = new ObservableCollection<TimeMaterialsPair>();
}
catch (IOException ex)
{
plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.LoadFailed", "読み込み失敗",
"資材データの読み込みに失敗しました。必要なアクセス権限がない可能性があります。"));
System.Diagnostics.Debug.WriteLine(ex);
this.History = new ObservableCollection<TimeMaterialsPair>();
if (this.History == null)
this.History = new ObservableCollection<TimeMaterialsPair>();
}
}
else
{
this.History = new ObservableCollection<TimeMaterialsPair>();
if (this.History == null)
this.History = new ObservableCollection<TimeMaterialsPair>();
}

this.HasLoaded = true;
}

public async Task SaveAsync()
{
try
{
if (!Directory.Exists(roamingDirectoryPath))
await SaveAsync(localDirectoryPath, SaveFilePath, null);
}
catch (IOException ex)
{
plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.SaveFailed", "読み込み失敗",
"資材データの保存に失敗しました。必要なアクセス権限がない可能性があります。"));
System.Diagnostics.Debug.WriteLine(ex);
}
}

private async Task SaveAsync(string directoryPath, string filePath, Action onSuccess)
{
try
{
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(roamingDirectoryPath);
Directory.CreateDirectory(directoryPath);
}

// オレオレ形式でバイナリ保存とかも考えたけど
// 今後ネジみたいに新しい資材が入ってくると対応が面倒なのでやめた
using (var stream = File.OpenWrite(FilePath))
using (var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
await Task.Run(() => Serializer.Serialize(stream, History));
}

onSuccess?.Invoke();
}
catch (IOException ex)
{
plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.SaveFailed", "読み込み失敗",
"MaterialChartPlugin.SaveFailed", "保存失敗",
"資材データの保存に失敗しました。必要なアクセス権限がない可能性があります。"));
System.Diagnostics.Debug.WriteLine(ex);
}
Expand All @@ -115,13 +143,13 @@ public async Task SaveAsync()
public async Task ExportAsCsvAsync()
{
var csvFileName = CreateCsvFileName(DateTime.Now);
var csvFilePath = Path.Combine(exportDirectoryPath, csvFileName);
var csvFilePath = Path.Combine(ExportDirectoryPath, csvFileName);

try
{
if (!Directory.Exists(exportDirectoryPath))
if (!Directory.Exists(ExportDirectoryPath))
{
Directory.CreateDirectory(exportDirectoryPath);
Directory.CreateDirectory(ExportDirectoryPath);
}

using (var writer = new StreamWriter(csvFilePath, false, Encoding.UTF8))
Expand All @@ -135,7 +163,7 @@ public async Task ExportAsCsvAsync()
}

plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.ExportCompleted", "エクスポート完了",
"MaterialChartPlugin.CsvExportCompleted", "エクスポート完了",
$"資材データがエクスポートされました: {csvFilePath}")
{
Activated = () =>
Expand All @@ -153,9 +181,50 @@ public async Task ExportAsCsvAsync()
}
}

public async Task ImportAsync(string filePath)
{
await LoadAsync(filePath, async () =>
{
plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.ImportComplete", "インポート完了",
"資材データのインポートに成功しました。"));

var materials = KanColleClient.Current.Homeport.Materials;
History.Add(new TimeMaterialsPair(DateTime.Now, materials.Fuel, materials.Ammunition, materials.Steel,
materials.Bauxite, materials.InstantRepairMaterials, materials.DevelopmentMaterials,
materials.InstantBuildMaterials, materials.ImprovementMaterials));

await SaveAsync();
}
);

}

public async Task ExportAsync()
{
var fileName = CreateExportedFileName(DateTime.Now);
var filePath = Path.Combine(ExportDirectoryPath, fileName);
await SaveAsync(ExportDirectoryPath, filePath, () =>
plugin.InvokeNotifyRequested(new Grabacr07.KanColleViewer.Composition.NotifyEventArgs(
"MaterialChartPlugin.ExportComplete", "エクスポート完了",
$"資材データがエクスポートされました: {filePath}")
{
Activated = () =>
{
System.Diagnostics.Process.Start("EXPLORER.EXE", $"/select,\"\"{filePath}\"\"");
}
})
);
}

private string CreateCsvFileName(DateTime dateTime)
{
return $"MaterialChartPlugin-{dateTime.ToString("yyMMdd-HHmmssff")}.csv";
}

private string CreateExportedFileName(DateTime dateTime)
{
return $"MaterialChartPlugin-BackUp-{dateTime.ToString("yyMMdd-HHmmssff")}.dat";
}
}
}
86 changes: 58 additions & 28 deletions MaterialChartPlugin/Models/Utilities/ChartExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,51 +19,81 @@ public static IEnumerable<TimeMaterialsPair> ThinOut(this IEnumerable<TimeMateri
if (log.Count() == 0)
yield break;

DateTime lastUpdated = DateTime.MinValue;
TimeSpan minimumTimeStep = GetMinimumTimeStep(period);
double minimumMaterialRatio = GetMinimumMaterialRatio(period);

// 最初のデータは必ず返す
yield return log.First();
TimeMaterialsPair lastData = log.First();
TimeSpan minimumStep;

// データ数を最大でもだいたい150点くらいに抑える(わりと適当)
foreach (var data in log.Skip(1))
{
// 前のデータから時刻あるいは資材量が十分離れているデータであれば値を返す
if (HasSeriouslyChanged(lastData, data, minimumTimeStep, minimumMaterialRatio))
{
yield return data;
lastData = data;
}
}

// 直近のデータは必ず返す
if (!lastData.Equals(log.Last()))
{
yield return log.Last();
}
}

private static TimeSpan GetMinimumTimeStep(DisplayedPeriod period)
{
// x方向の解像度は150分割くらいにしておく(わりと適当)
switch (period)
{
case DisplayedPeriod.OneDay:
minimumStep = TimeSpan.FromMinutes(10);
break;
return TimeSpan.FromMinutes(10);
case DisplayedPeriod.OneWeek:
minimumStep = TimeSpan.FromHours(1);
break;
return TimeSpan.FromHours(1);
case DisplayedPeriod.OneMonth:
minimumStep = TimeSpan.FromHours(4);
break;
return TimeSpan.FromHours(4);
case DisplayedPeriod.ThreeMonths:
minimumStep = TimeSpan.FromHours(12);
break;
return TimeSpan.FromHours(12);
case DisplayedPeriod.OneYear:
minimumStep = TimeSpan.FromDays(2);
break;
return TimeSpan.FromDays(2);
case DisplayedPeriod.ThreeYears:
minimumStep = TimeSpan.FromDays(6);
break;
return TimeSpan.FromDays(6);
default:
throw new ArgumentException("periodの値が不正です");
}
}

foreach (var data in log)
private static double GetMinimumMaterialRatio(DisplayedPeriod period)
{
switch (period)
{
// 前のデータから時刻が十分離れているデータであれば値を返す
if (data.DateTime - lastUpdated >= minimumStep)
{
yield return data;
lastUpdated = data.DateTime;
lastData = data;
}
case DisplayedPeriod.OneDay:
case DisplayedPeriod.OneWeek:
return 0.02;
case DisplayedPeriod.OneMonth:
return 0.05;
case DisplayedPeriod.ThreeMonths:
return 0.08;
case DisplayedPeriod.OneYear:
return 0.12;
case DisplayedPeriod.ThreeYears:
return 0.25;
default:
throw new ArgumentException("periodの値が不正です");
}
}

// 直近のデータは必ず返す
if (!lastData.Equals(log.Last()))
{
yield return log.Last();
}
private static bool HasSeriouslyChanged(TimeMaterialsPair oldData, TimeMaterialsPair newData, TimeSpan minimumTimeStep, double minimumMaterialRatio)
{
// 5%くらい変化したら大きく変わったとみてよい
return newData.DateTime - oldData.DateTime >= minimumTimeStep
|| Math.Abs(newData.Fuel - oldData.Fuel) >= oldData.Fuel * minimumMaterialRatio
|| Math.Abs(newData.Ammunition - oldData.Ammunition) >= oldData.Fuel * minimumMaterialRatio
|| Math.Abs(newData.Steel - oldData.Steel) >= oldData.Steel * minimumMaterialRatio
|| Math.Abs(newData.Bauxite - oldData.Bauxite) >= oldData.Bauxite * minimumMaterialRatio
|| Math.Abs(newData.RepairTool - oldData.RepairTool) >= oldData.RepairTool * minimumMaterialRatio;
}

/// <summary>
Expand Down
Loading

0 comments on commit 871e10d

Please sign in to comment.