• R/O
  • SSH
  • HTTPS

protra: Commit


Commit MetaInfo

Revision516 (tree)
Time2014-04-14 20:51:39
Authorpanacoran

Log Message

株価データ更新の進捗の計算をProgressクラスに分離する

* Protra.Lib/Update/PriceDataUpdator.cs (Progress): 新規。進捗を計算するクラス。
(PriceDataUpdator.NumRecords): 削除。
(PriceDataUpdator.NumDays): 削除。
(PriceDataUpdator.DoneRecords): 削除。
(PriceDataUpdator.TotalRecords): 削除。
(PriceDataUpdator.TotalDays): 削除。
(PriceDataUpdator.AdjustedDate): Startから改名。インスタンス変数に触るのをやめる。
(PriceDataUpdator.UpdatePrice): 進捗の計算をProgressクラスに移譲する。
(PriceDataUpdator.UpdateProgress): Progressクラスに移動。
(PriceDataUpdator.LeftTime): 同上。
(PriceDataUpdator.CalcLeftTime): 同上。
* Protra.Lib/Update/YahooFinanceUpdator.cs (YahooFinanceUpdator.UpdatePrice): 進捗の計算をProgressクラスに移譲する。
(YahooFinanceUpdator.GetPage): エラー時に1秒待ってから再接続する。

Change Summary

Incremental Difference

--- protra/trunk/ChangeLog.txt (revision 515)
+++ protra/trunk/ChangeLog.txt (revision 516)
@@ -1,3 +1,21 @@
1+2014-04-10 panacoran <panacoran@users.sourceforge.jp>
2+
3+ 株価データ更新の進捗の計算をProgressクラスに分離する
4+
5+ * Protra.Lib/Update/PriceDataUpdator.cs (Progress): 新規。進捗を計算するクラス。
6+ (PriceDataUpdator.NumRecords): 削除。
7+ (PriceDataUpdator.NumDays): 削除。
8+ (PriceDataUpdator.DoneRecords): 削除。
9+ (PriceDataUpdator.TotalRecords): 削除。
10+ (PriceDataUpdator.TotalDays): 削除。
11+ (PriceDataUpdator.AdjustedDate): Startから改名。インスタンス変数に触るのをやめる。
12+ (PriceDataUpdator.UpdatePrice): 進捗の計算をProgressクラスに移譲する。
13+ (PriceDataUpdator.UpdateProgress): Progressクラスに移動。
14+ (PriceDataUpdator.LeftTime): 同上。
15+ (PriceDataUpdator.CalcLeftTime): 同上。
16+ * Protra.Lib/Update/YahooFinanceUpdator.cs (YahooFinanceUpdator.UpdatePrice): 進捗の計算をProgressクラスに移譲する。
17+ (YahooFinanceUpdator.GetPage): エラー時に1秒待ってから再接続する。
18+
119 2014-03-15 panacoran <panacoran@users.sourceforge.jp>
220
321 .NET Framework 2.0が含まれているWindowsのバージョンの記述をVista以降に直す
--- protra/trunk/Protra.Lib/Update/PriceDataUpdator.cs (revision 515)
+++ protra/trunk/Protra.Lib/Update/PriceDataUpdator.cs (revision 516)
@@ -148,36 +148,147 @@
148148 }
149149
150150 /// <summary>
151- /// 株価情報の更新を行う抽象クラスです。
151+ /// 進捗を計算するクラス
152152 /// </summary>
153- public abstract class PriceDataUpdator
153+ public class Progress
154154 {
155+ private int _daily; // 一日の処理済みのレコード数
156+ private int _total; // 処理済みのレコード数
157+ private int _days; // 処理済みの日数
158+
159+ private long _startTicks; // 処理を開始したティック
160+ private long _startDownload; // ダウンロードを開始したティック
161+ private long _ticksForDownload; // ダウンロードに要したティック
162+
155163 /// <summary>
156- /// 処理した一日分のレコード数を取得または設定する。
164+ /// 一日当たりのレコード数を取得または設定する。
157165 /// </summary>
158- protected int NumRecords { get; set; }
166+ public int RecordsPerDay { get; set; }
159167
160168 /// <summary>
161- /// 処理した日数を取得または設定する。
169+ /// 処理すべき日数を取得または設定する。
162170 /// </summary>
163- protected int NumDays { get; set; }
171+ public int NumDays { get; set; }
164172
165173 /// <summary>
166- /// 処理したレコード数を取得または設定する。
174+ /// 開始時刻を記録する。
167175 /// </summary>
168- protected int DoneRecords { get; set; }
176+ public void Start()
177+ {
178+ _startTicks = DateTime.Now.Ticks;
179+ }
169180
170181 /// <summary>
171- /// 処理すべき一日分のレコード数を取得または設定する。
182+ /// ダウンロードの開始時刻を記録する。
172183 /// </summary>
173- protected int TotalRecords { get; set; }
184+ public void StartDownload()
185+ {
186+ _startDownload = DateTime.Now.Ticks;
187+ }
174188
175189 /// <summary>
176- /// 処理すべき日数を取得または設定する。
190+ /// ダウンロードの終了時刻を記録する。
177191 /// </summary>
178- protected int TotalDays { get; set; }
192+ public void FinishDownload()
193+ {
194+ _ticksForDownload += DateTime.Now.Ticks - _startDownload;
195+ }
179196
180197 /// <summary>
198+ /// 処理済みのレコード数を増分する。
199+ /// </summary>
200+ public void IncrementRecords()
201+ {
202+ _daily++;
203+ _total++;
204+ }
205+
206+ /// <summary>
207+ /// 処理済みの日数を増分する。
208+ /// </summary>
209+ public void IncrementDays()
210+ {
211+ _daily = 0;
212+ _days++;
213+ }
214+
215+ private int _prevProgress = -1;
216+ private int _prevDays;
217+ private long _prevTicks;
218+
219+ /// <summary>
220+ /// 残りの所要時間と進捗を計算して表示する。
221+ /// </summary>
222+ /// <param name="worker">BackgroundWorker</param>
223+ /// <param name="date">日付</param>
224+ public void Show(BackgroundWorker worker, DateTime date)
225+ {
226+ var progress = _total == 0
227+ ? 0
228+ : (_total * 100 /
229+ (_total + RecordsPerDay - _daily + (NumDays - _days - 1) * RecordsPerDay));
230+ if (progress > 100)
231+ progress = 100;
232+ if (progress == _prevProgress && _days == _prevDays &&
233+ DateTime.Now.Ticks - _prevTicks < (long)20 * 10000000) // 20秒以上経ったら残り時間を更新する。
234+ return;
235+ _prevDays = _days;
236+ _prevProgress = progress;
237+ _prevTicks = DateTime.Now.Ticks;
238+ var message = String.Format("{0:d} ({1}/{2}) ", date, _days + 1, NumDays) + LeftTime();
239+ worker.ReportProgress(progress, message);
240+ }
241+
242+ /// <summary>
243+ /// 残り時間を文字列表現で返す。
244+ /// </summary>
245+ /// <returns>文字列で表した残り時間</returns>
246+ private string LeftTime()
247+ {
248+ var leftTime = CalcLeftTime();
249+ if (leftTime == 0) // 処理が始まる前も0になる。
250+ return "";
251+ var s = "残り";
252+ if (leftTime <= 50)
253+ s += " " + leftTime + " 秒";
254+ else
255+ {
256+ if (leftTime >= 3600)
257+ {
258+ s += " " + (leftTime / 3600) + " 時間";
259+ leftTime %= 3600;
260+ }
261+ if (leftTime > 0)
262+ s += " " + ((leftTime + 59) / 60) + " 分"; // 切り上げ
263+ }
264+ return s;
265+ }
266+
267+ /// <summary>
268+ /// 残り時間を計算する。
269+ /// </summary>
270+ /// <returns>秒数であらわした残り時間</returns>
271+ private int CalcLeftTime()
272+ {
273+ var ticksForRecords = (DateTime.Now.Ticks - _startTicks) - _ticksForDownload;
274+ var leftTicks = _ticksForDownload / (_days + 1) * (NumDays - _days - 1);
275+ if (_total > 0)
276+ {
277+ var leftRecords = RecordsPerDay - _daily + (NumDays - _days - 1) * RecordsPerDay;
278+ leftTicks += ticksForRecords / _total * leftRecords;
279+ }
280+ return (int)(leftTicks / 10000000);
281+ }
282+ }
283+
284+ /// <summary>
285+ /// 株価情報の更新を行う抽象クラス
286+ /// </summary>
287+ public abstract class PriceDataUpdator
288+ {
289+ private readonly Progress _progress = new Progress();
290+
291+ /// <summary>
181292 /// 処理中の日付を取得または設定する。
182293 /// </summary>
183294 protected DateTime Date { get; set; }
@@ -193,32 +304,34 @@
193304 /// <returns>URL</returns>
194305 protected abstract string DownloadUrl { get; }
195306
196- private long _startTicks; // 処理を開始したティック
197- private long _startDownload; // ダウンロードを開始したティック
198- private long _ticksToDownload; // ダウンロードに要したティック
199-
200307 /// <summary>
201- /// 処理の開始に必要な設定をする。
308+ /// 処理を開始終了する日付を営業日に調節し、その間の営業日数を数える。
202309 /// <param name="begin">処理を開始する日付</param>
203310 /// <param name="end">処理を終了する日付</param>
204311 /// </summary>
205- protected void Start(DateTime begin, DateTime end)
312+ protected int AdjustedDate(ref DateTime begin, ref DateTime end)
206313 {
314+ var days = 0;
315+ var set = false;
207316 // 市場が開いている日数を数える。
208317 for (var d = begin; d <= end; d = d.AddDays(1))
209- if (Calendar.IsMarketOpen(d))
318+ {
319+ if (!Calendar.IsMarketOpen(d))
320+ continue;
321+ if (!set)
210322 {
211- if (Date == DateTime.MinValue)
212- Date = d; // 最初の市場が開いている日に設定する。
213- TotalDays++;
323+ begin = d; // 最初の市場が開いている日に設定する。
324+ set = true;
214325 }
215- if (Date == DateTime.MinValue) // begin > endの場合。
216- Date = begin;
326+ days++;
327+ }
217328 // 最も最近の市場が開いている日に設定する。
218- for (EndDate = end; EndDate >= begin; EndDate = EndDate.AddDays(-1))
219- if (Calendar.IsMarketOpen(EndDate))
329+ for (; end >= begin; end = end.AddDays(-1))
330+ {
331+ if (Calendar.IsMarketOpen(end))
220332 break;
221- _startTicks = DateTime.Now.Ticks;
333+ }
334+ return days;
222335 }
223336
224337 /// <summary>
@@ -267,12 +380,12 @@
267380 get
268381 {
269382 return new[]
270- {
271- "株価情報",
272- "無尽蔵",
273- "株価データダウンロードサイト",
274- "Yahoo!ファイナンス"
275- };
383+ {
384+ "株価情報",
385+ "無尽蔵",
386+ "株価データダウンロードサイト",
387+ "Yahoo!ファイナンス"
388+ };
276389 }
277390 }
278391
@@ -330,19 +443,21 @@
330443 var begin = (DateTime)e.Argument;
331444 if (begin < DataSince)
332445 begin = DataSince;
333- var today = DateTime.Now;
446+ var end = DateTime.Now;
334447 // 今日のデータがまだ置かれていない。
335- if (!IsDataAvailable(today))
336- today = today.AddDays(-1);
337- for (Start(begin, today); Date <= EndDate; NextDate())
448+ if (!IsDataAvailable(end))
449+ end = end.AddDays(-1);
450+ _progress.NumDays = AdjustedDate(ref begin, ref end);
451+ _progress.Start();
452+ for (Date = begin, EndDate = end; Date <= EndDate; NextDate())
338453 {
339- NumRecords = 0; // 進捗の計算に使うのでここでリセットする。
340- UpdateProgress(worker);
454+ _progress.Show(worker, Date);
341455 var dl = new DownloadUtil(DownloadUrl);
342- _startDownload = DateTime.Now.Ticks;
456+ _progress.StartDownload();
343457 var prices = new List<Price>();
344458 using (var stream = dl.DownloadAndExtract())
345459 {
460+ _progress.FinishDownload();
346461 if (worker.CancellationPending)
347462 {
348463 e.Cancel = true;
@@ -351,10 +466,9 @@
351466 }
352467 if (stream == null)
353468 {
354- TotalDays--;
469+ _progress.NumDays--;
355470 continue;
356471 }
357- _ticksToDownload += DateTime.Now.Ticks - _startDownload;
358472 using (var reader = new StreamReader(stream, Encoding.GetEncoding("shift_jis")))
359473 {
360474 string line;
@@ -383,8 +497,8 @@
383497 prices.Add(prev);
384498 }
385499 }
386- TotalRecords = prices.Count;
387- for (; NumRecords < TotalRecords; NumRecords++, DoneRecords++, UpdateProgress(worker))
500+ _progress.RecordsPerDay = prices.Count;
501+ foreach (var price in prices)
388502 {
389503 if (worker.CancellationPending)
390504 {
@@ -392,10 +506,12 @@
392506 PriceData.CloseAll();
393507 return;
394508 }
395- PriceData.Add(prices[NumRecords], Date == EndDate);
509+ PriceData.Add(price, Date == EndDate);
510+ _progress.IncrementRecords();
511+ _progress.Show(worker, Date);
396512 }
397513 PriceData.MaxDate = Date;
398- NumDays++;
514+ _progress.IncrementDays();
399515 }
400516 PriceData.CloseAll();
401517 }
@@ -407,7 +523,6 @@
407523 /// <returns></returns>
408524 protected abstract bool IsDataAvailable(DateTime time);
409525
410-
411526 /// <summary>
412527 /// 文字列を解析して価格データを返す。
413528 /// </summary>
@@ -414,73 +529,5 @@
414529 /// <param name="line">文字列</param>
415530 /// <returns>価格データ</returns>
416531 protected abstract Price ParseLine(string line);
417-
418- private int _prevProgress = -1;
419- private int _prevNumDyas;
420- private long _prevTicks;
421-
422- /// <summary>
423- /// 残りの所要時間と進捗を計算して表示する。
424- /// </summary>
425- /// <param name="worker">BackgroundWorker</param>
426- protected void UpdateProgress(BackgroundWorker worker)
427- {
428- var progress = DoneRecords == 0
429- ? 0
430- : (int)
431- ((float)DoneRecords * 100 /
432- (DoneRecords + TotalRecords - NumRecords + (TotalDays - NumDays - 1) * TotalRecords));
433- if (progress > 100)
434- progress = 100;
435- if (progress == _prevProgress && NumDays == _prevNumDyas &&
436- DateTime.Now.Ticks - _prevTicks < (long)20 * 10000000) // 20秒以上経ったら残り時間を更新する。
437- return;
438- _prevNumDyas = NumDays;
439- _prevProgress = progress;
440- _prevTicks = DateTime.Now.Ticks;
441- var message = String.Format("{0:d} ({1}/{2}) ", Date, NumDays + 1, TotalDays) + LeftTime();
442- worker.ReportProgress(progress, message);
443- }
444-
445- /// <summary>
446- /// 残り時間を文字列表現で返す。
447- /// </summary>
448- /// <returns>文字列で表した残り時間</returns>
449- private string LeftTime()
450- {
451- var leftTime = CalcLeftTime();
452- if (leftTime == 0) // 処理が始まる前も0になる。
453- return "";
454- var s = "残り";
455- if (leftTime <= 50)
456- s += " " + leftTime + " 秒";
457- else
458- {
459- if (leftTime >= 3600)
460- {
461- s += " " + (leftTime / 3600) + " 時間";
462- leftTime %= 3600;
463- }
464- if (leftTime > 0)
465- s += " " + ((leftTime + 59) / 60) + " 分"; // 切り上げ
466- }
467- return s;
468- }
469-
470- /// <summary>
471- /// 残り時間を計算する。
472- /// </summary>
473- /// <returns>秒数であらわした残り時間</returns>
474- private int CalcLeftTime()
475- {
476- var ticksToRecords = (DateTime.Now.Ticks - _startTicks) - _ticksToDownload;
477- var leftTicks = _ticksToDownload / (NumDays + 1) * (TotalDays - NumDays - 1);
478- if (DoneRecords > 0)
479- {
480- var leftRecords = TotalRecords - NumRecords + (TotalDays - NumDays - 1) * TotalRecords;
481- leftTicks += ticksToRecords / DoneRecords * leftRecords;
482- }
483- return (int)(leftTicks / 10000000);
484- }
485532 }
486533 }
\ No newline at end of file
--- protra/trunk/Protra.Lib/Update/YahooFinanceUpdator.cs (revision 515)
+++ protra/trunk/Protra.Lib/Update/YahooFinanceUpdator.cs (revision 516)
@@ -39,6 +39,7 @@
3939 private readonly Queue<Price> _priceQueue = new Queue<Price>();
4040 private bool _terminate;
4141 private Exception _exception;
42+ private readonly Progress _progress = new Progress();
4243
4344 /// <summary>
4445 /// データが存在する最初の日付を取得する。
@@ -58,25 +59,28 @@
5859 var begin = (DateTime)e.Argument;
5960 if (begin < DataSince)
6061 begin = DataSince;
61- var today = DateTime.Now;
62+ var end = DateTime.Now;
6263 // 新しいデータが置かれるのは早くても午後7時以降
63- if (today.Hour < 19)
64- today = today.AddDays(-1);
64+ if (end.Hour < 19)
65+ end = end.AddDays(-1);
6566 var threads = new Thread[1];
6667 for (var i = 0; i < threads.Length; i++)
6768 (threads[i] = new Thread(DoFetchPrice) {Name = "Fetch Thread " + i}).Start();
6869 try
6970 {
70- for (Start(begin, today); Date <= EndDate; NextDate())
71+ _progress.NumDays = AdjustedDate(ref begin, ref end);
72+ _progress.RecordsPerDay = GlobalEnv.BrandData.Count;
73+ _progress.Start();
74+ for (Date = begin, EndDate = end; Date <= EndDate; NextDate())
7175 {
72- TotalRecords = GlobalEnv.BrandData.Count;
73- UpdateProgress(worker);
7476 lock (_syncObject)
7577 {
7678 _brandQueue = new Queue<Brand>(GlobalEnv.BrandData);
7779 Monitor.PulseAll(_syncObject);
7880 }
79- for (NumRecords = 0; NumRecords < TotalRecords; NumRecords++, DoneRecords++, UpdateProgress(worker))
81+ _progress.Show(worker, Date);
82+ var i = 0;
83+ for (; i < _progress.RecordsPerDay; i++)
8084 {
8185 if (worker.CancellationPending)
8286 {
@@ -97,14 +101,11 @@
97101 if (price.Code == "1001" && price.Open == 0) // まだ株価が用意されていない。
98102 return;
99103 PriceData.Add(price, Date == EndDate);
104+ _progress.IncrementRecords();
105+ _progress.Show(worker, Date);
100106 }
101- if (NumRecords == TotalRecords)
102- {
103- PriceData.MaxDate = Date;
104- NumDays++;
105- }
106- else
107- TotalDays--;
107+ PriceData.MaxDate = Date;
108+ _progress.IncrementDays();
108109 }
109110 }
110111 finally
@@ -182,6 +183,7 @@
182183 case WebExceptionStatus.ConnectionClosed:
183184 case WebExceptionStatus.ReceiveFailure:
184185 case WebExceptionStatus.ConnectFailure:
186+ Thread.Sleep(1000);
185187 break;
186188 default:
187189 throw;
Show on old repository browser