OSDN Git Service

払暁戦に対応する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Sniffer.cs
1 // Copyright (C) 2013, 2014, 2015 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 // \r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.Collections.Generic;\r
17 using System.Linq;\r
18 \r
19 namespace KancolleSniffer\r
20 {\r
21     public class Sniffer\r
22     {\r
23         private bool _start;\r
24         private readonly ItemInfo _itemInfo = new ItemInfo();\r
25         private readonly MaterialInfo _materialInfo = new MaterialInfo();\r
26         private readonly QuestInfo _questInfo = new QuestInfo();\r
27         private readonly MissionInfo _missionInfo = new MissionInfo();\r
28         private readonly ShipInfo _shipInfo;\r
29         private readonly ConditionTimer _conditionTimer;\r
30         private readonly DockInfo _dockInfo;\r
31         private readonly AkashiTimer _akashiTimer;\r
32         private readonly Achievement _achievement = new Achievement();\r
33         private readonly BattleInfo _battleInfo;\r
34         private readonly Logger _logger;\r
35         private readonly ExMapInfo _exMapInfo = new ExMapInfo();\r
36         private readonly MiscTextInfo _miscTextInfo = new MiscTextInfo();\r
37         private readonly BaseAirCoprs _baseAirCoprs;\r
38         private readonly PresetDeck _presetDeck = new PresetDeck();\r
39         private readonly Status _status = new Status();\r
40         private bool _saveState;\r
41         private readonly List<IHaveState> _haveState;\r
42 \r
43         [Flags]\r
44         public enum Update\r
45         {\r
46             None = 0,\r
47             Error = 1 << 0,\r
48             Start = 1 << 1,\r
49             Item = 1 << 2,\r
50             Ship = 1 << 3,\r
51             Timer = 1 << 4,\r
52             NDock = 1 << 5,\r
53             Mission = 1 << 6,\r
54             QuestList = 1 << 7,\r
55             Battle = 1 << 8,\r
56             All = (1 << 9) - 1\r
57         }\r
58 \r
59         public Sniffer()\r
60         {\r
61             _shipInfo = new ShipInfo(_itemInfo);\r
62             _conditionTimer = new ConditionTimer(_shipInfo);\r
63             _dockInfo = new DockInfo(_shipInfo, _materialInfo);\r
64             _akashiTimer = new AkashiTimer(_shipInfo, _dockInfo, _presetDeck);\r
65             _battleInfo = new BattleInfo(_shipInfo, _itemInfo);\r
66             _logger = new Logger(_shipInfo, _itemInfo, _battleInfo);\r
67             _baseAirCoprs = new BaseAirCoprs(_itemInfo);\r
68             _haveState = new List<IHaveState> {_achievement, _materialInfo, _conditionTimer, _exMapInfo};\r
69         }\r
70 \r
71         private void SaveState()\r
72         {\r
73             if (!_saveState)\r
74                 return;\r
75             if (!_haveState.Any(x => x.NeedSave))\r
76                 return;\r
77             foreach (var x in _haveState)\r
78                 x.SaveState(_status);\r
79             _status.Save();\r
80         }\r
81 \r
82         public void LoadState()\r
83         {\r
84             _status.Load();\r
85             foreach (var x in _haveState)\r
86                 x.LoadState(_status);\r
87             _saveState = true;\r
88         }\r
89 \r
90         public Update Sniff(string url, string request, dynamic json)\r
91         {\r
92             if (!json.api_result())\r
93                 return Update.Error;\r
94             if ((int)json.api_result != 1)\r
95                 return Update.None;\r
96             var data = json.api_data() ? json.api_data : new object();\r
97 \r
98             if (url.EndsWith("api_start2"))\r
99             {\r
100                 return ApiStart(data);\r
101             }\r
102             if (!_start)\r
103                 return Update.None;\r
104             if (url.EndsWith("api_port/port"))\r
105                 return ApiPort(data);\r
106             if (url.Contains("member"))\r
107                 return ApiMember(url, json);\r
108             if (url.Contains("kousyou"))\r
109                 return ApiKousyou(url, request, data);\r
110             if (url.Contains("battle"))\r
111                 return ApiBattle(url, request, data);\r
112             return ApiOthers(url, request, data);\r
113         }\r
114 \r
115         private Update ApiStart(dynamic data)\r
116         {\r
117             _shipInfo.InspectMaster(data);\r
118             _missionInfo.InspectMaster(data.api_mst_mission);\r
119             _itemInfo.InspectMaster(data);\r
120             _exMapInfo.ResetIfNeeded();\r
121             _start = true;\r
122             return Update.Start;\r
123         }\r
124 \r
125         private Update ApiPort(dynamic data)\r
126         {\r
127             _itemInfo.InspectBasic(data.api_basic);\r
128             _materialInfo.InspectMaterialPort(data.api_material);\r
129             _logger.InspectBasic(data.api_basic);\r
130             _logger.InspectMaterial(data.api_material);\r
131             _shipInfo.InspectShip(data);\r
132             _shipInfo.ClearBadlyDamagedShips();\r
133             _conditionTimer.CalcRegenTime();\r
134             _missionInfo.InspectDeck(data.api_deck_port);\r
135             _dockInfo.InspectNDock(data.api_ndock);\r
136             _akashiTimer.Port();\r
137             _achievement.InspectBasic(data.api_basic);\r
138             if (data.api_parallel_quest_count()) // 昔のログにはないので\r
139                 _questInfo.QuestCount = (int)data.api_parallel_quest_count;\r
140             if (data.api_event_object())\r
141                 _baseAirCoprs.InspectEventObject(data.api_event_object);\r
142             if (data.api_plane_info())\r
143                 _baseAirCoprs.InspectPlaneInfo(data.api_plane_info);\r
144             _battleInfo.CleanupResult();\r
145             _battleInfo.BattleState = BattleState.None;\r
146             _shipInfo.ClearEscapedShips();\r
147             _miscTextInfo.ClearIfNeeded();\r
148             SaveState();\r
149             return Update.All;\r
150         }\r
151 \r
152         private Update ApiMember(string url, dynamic json)\r
153         {\r
154             var data = json.api_data() ? json.api_data : new object();\r
155 \r
156             if (url.EndsWith("api_get_member/require_info"))\r
157             {\r
158                 _itemInfo.InspectSlotItem(data.api_slot_item, true);\r
159                 _dockInfo.InspectKDock(data.api_kdock);\r
160                 return Update.Timer;\r
161             }\r
162             if (url.EndsWith("api_get_member/basic"))\r
163             {\r
164                 _itemInfo.InspectBasic(data);\r
165                 _logger.InspectBasic(data);\r
166                 return Update.None;\r
167             }\r
168             if (url.EndsWith("api_get_member/slot_item"))\r
169             {\r
170                 _itemInfo.InspectSlotItem(data, true);\r
171                 return Update.Item;\r
172             }\r
173             if (url.EndsWith("api_get_member/kdock"))\r
174             {\r
175                 _dockInfo.InspectKDock(data);\r
176                 _logger.InspectKDock(data);\r
177                 return Update.Timer;\r
178             }\r
179             if (url.EndsWith("api_get_member/ndock"))\r
180             {\r
181                 _dockInfo.InspectNDock(data);\r
182                 _conditionTimer.CheckCond();\r
183                 _akashiTimer.CheckFleet();\r
184                 return Update.NDock | Update.Timer | Update.Ship;\r
185             }\r
186             if (url.EndsWith("api_get_member/questlist"))\r
187             {\r
188                 _questInfo.InspectQuestList(data);\r
189                 return Update.QuestList;\r
190             }\r
191             if (url.EndsWith("api_get_member/deck"))\r
192             {\r
193                 _shipInfo.InspectDeck(data);\r
194                 _missionInfo.InspectDeck(data);\r
195                 _akashiTimer.CheckFleet();\r
196                 return Update.Mission | Update.Timer;\r
197             }\r
198             if (url.EndsWith("api_get_member/ship2"))\r
199             {\r
200                 // ここだけjsonなので注意\r
201                 _shipInfo.InspectShip(json);\r
202                 _akashiTimer.CheckFleet();\r
203                 _battleInfo.BattleState = BattleState.None;\r
204                 return Update.Item | Update.Ship | Update.Battle;\r
205             }\r
206             if (url.EndsWith("api_get_member/ship_deck"))\r
207             {\r
208                 _shipInfo.InspectShip(data);\r
209                 _akashiTimer.CheckFleet();\r
210                 _battleInfo.BattleState = BattleState.None;\r
211                 return Update.Ship | Update.Battle;\r
212             }\r
213             if (url.EndsWith("api_get_member/ship3"))\r
214             {\r
215                 _shipInfo.InspectShip(data);\r
216                 _akashiTimer.CheckFleet();\r
217                 _conditionTimer.CheckCond();\r
218                 return Update.Ship;\r
219             }\r
220             if (url.EndsWith("api_get_member/material"))\r
221             {\r
222                 _materialInfo.InspectMaterial(data);\r
223                 return Update.Item;\r
224             }\r
225             if (url.EndsWith("api_get_member/mapinfo"))\r
226             {\r
227                 _exMapInfo.InspectMapInfo(data);\r
228                 _miscTextInfo.InspectMapInfo(data);\r
229                 if (data.api_air_base())\r
230                     _baseAirCoprs.Inspect(data.api_air_base);\r
231                 return Update.Item;\r
232             }\r
233             if (url.EndsWith("api_req_member/get_practice_enemyinfo"))\r
234             {\r
235                 _miscTextInfo.InspectPracticeEnemyInfo(data);\r
236                 return Update.Item;\r
237             }\r
238             if (url.EndsWith("api_get_member/preset_deck"))\r
239             {\r
240                 _presetDeck.Inspect(data);\r
241                 return Update.None;\r
242             }\r
243             if (url.EndsWith("api_get_member/base_air_corps"))\r
244             {\r
245                 _baseAirCoprs.Inspect(data);\r
246                 return Update.Ship;\r
247             }\r
248             return Update.None;\r
249         }\r
250 \r
251         private Update ApiKousyou(string url, string request, dynamic data)\r
252         {\r
253             if (url.EndsWith("api_req_kousyou/createitem"))\r
254             {\r
255                 _itemInfo.InspectCreateItem(data);\r
256                 _materialInfo.InspectCreateIem(data);\r
257                 _logger.InspectCreateItem(request, data);\r
258                 return Update.Item;\r
259             }\r
260             if (url.EndsWith("api_req_kousyou/getship"))\r
261             {\r
262                 _itemInfo.InspectGetShip(data);\r
263                 _shipInfo.InspectShip(data);\r
264                 _dockInfo.InspectKDock(data.api_kdock);\r
265                 _conditionTimer.CheckCond();\r
266                 return Update.Item | Update.Timer;\r
267             }\r
268             if (url.EndsWith("api_req_kousyou/destroyship"))\r
269             {\r
270                 _shipInfo.InspectDestroyShip(request, data);\r
271                 _materialInfo.InspectDestroyShip(data);\r
272                 _conditionTimer.CheckCond();\r
273                 _akashiTimer.CheckFleet();\r
274                 return Update.Item | Update.Ship;\r
275             }\r
276             if (url.EndsWith("api_req_kousyou/destroyitem2"))\r
277             {\r
278                 _itemInfo.InspectDestroyItem(request, data);\r
279                 _materialInfo.InspectDestroyItem(data);\r
280                 return Update.Item;\r
281             }\r
282             if (url.EndsWith("api_req_kousyou/remodel_slot"))\r
283             {\r
284                 _logger.SetCurrentMaterial(_materialInfo.Current);\r
285                 _logger.InspectRemodelSlot(request, data); // 資材の差が必要なので_materialInfoより前\r
286                 _itemInfo.InspectRemodelSlot(data);\r
287                 _materialInfo.InspectRemodelSlot(data);\r
288                 return Update.Item;\r
289             }\r
290             if (url.EndsWith("api_req_kousyou/createship"))\r
291             {\r
292                 _logger.InspectCreateShip(request);\r
293                 return Update.None;\r
294             }\r
295             if (url.EndsWith("api_req_kousyou/createship_speedchange"))\r
296             {\r
297                 _dockInfo.InspectCreateShipSpeedChange(request);\r
298                 return Update.Timer;\r
299             }\r
300             return Update.None;\r
301         }\r
302 \r
303         private Update ApiBattle(string url, string request, dynamic data)\r
304         {\r
305             if (IsNormalBattleAPI(url) || IsCombinedBattleAPI(url))\r
306             {\r
307                 _battleInfo.InspectBattle(data, url);\r
308                 _logger.InspectBattle(data);\r
309                 return Update.Ship | Update.Battle;\r
310             }\r
311             if (url.EndsWith("api_req_practice/battle") || url.EndsWith("api_req_practice/midnight_battle"))\r
312             {\r
313                 if (url.EndsWith("/battle"))\r
314                 {\r
315                     _shipInfo.InspectMapStart(request); // 演習を出撃中とみなす\r
316                     _conditionTimer.InvalidateCond();\r
317                     _miscTextInfo.ClearFlag = true;\r
318                 }\r
319                 _battleInfo.InspectBattle(data, url);\r
320                 return Update.Ship | Update.Battle | Update.Timer;\r
321             }\r
322             if (url.EndsWith("api_req_sortie/battleresult") || url.EndsWith("api_req_combined_battle/battleresult"))\r
323             {\r
324                 _shipInfo.SaveBattleResult();\r
325                 _battleInfo.InspectBattleResult(data);\r
326                 _exMapInfo.InspectBattleResult(data);\r
327                 _logger.InspectBattleResult(data);\r
328                 return Update.Ship;\r
329             }\r
330             if (url.EndsWith("api_req_practice/battle_result"))\r
331             {\r
332                 _battleInfo.InspectPracticeResult(data);\r
333                 return Update.Ship;\r
334             }\r
335             if (url.EndsWith("api_req_combined_battle/goback_port"))\r
336             {\r
337                 _battleInfo.CauseCombinedBattleEscape();\r
338                 return Update.Ship;\r
339             }\r
340             _battleInfo.BattleState = BattleState.Unknown;\r
341             return Update.None;\r
342         }\r
343 \r
344         private bool IsNormalBattleAPI(string url)\r
345         {\r
346             return url.EndsWith("api_req_sortie/battle") ||\r
347                    url.EndsWith("api_req_sortie/airbattle") ||\r
348                    url.EndsWith("api_req_sortie/ld_airbattle") ||\r
349                    url.EndsWith("api_req_battle_midnight/battle") ||\r
350                    url.EndsWith("api_req_battle_midnight/sp_midnight");\r
351         }\r
352 \r
353         private bool IsCombinedBattleAPI(string url)\r
354         {\r
355             return url.EndsWith("api_req_combined_battle/battle") ||\r
356                    url.EndsWith("api_req_combined_battle/airbattle") ||\r
357                    url.EndsWith("api_req_combined_battle/ld_airbattle") ||\r
358                    url.EndsWith("api_req_combined_battle/battle_water") ||\r
359                    url.EndsWith("api_req_combined_battle/midnight_battle") ||\r
360                    url.EndsWith("api_req_combined_battle/sp_midnight") ||\r
361                    url.EndsWith("api_req_combined_battle/ec_battle") ||\r
362                    url.EndsWith("api_req_combined_battle/ec_midnight_battle") ||\r
363                    url.EndsWith("api_req_combined_battle/ec_night_to_day") ||\r
364                    url.EndsWith("api_req_combined_battle/each_battle") ||\r
365                    url.EndsWith("api_req_combined_battle/each_battle_water");\r
366         }\r
367 \r
368         private Update ApiOthers(string url, string request, dynamic data)\r
369         {\r
370             if (url.EndsWith("api_req_hensei/change"))\r
371             {\r
372                 _shipInfo.InspectChange(request);\r
373                 _akashiTimer.InspectChange(request);\r
374                 return Update.Ship;\r
375             }\r
376             if (url.EndsWith("api_req_hensei/preset_select"))\r
377             {\r
378                 _shipInfo.InspectDeck(new[] {data});\r
379                 _akashiTimer.CheckFleet();\r
380                 return Update.Ship;\r
381             }\r
382             if (url.EndsWith("api_req_hensei/preset_register"))\r
383             {\r
384                 _presetDeck.InspectRegister(data);\r
385                 return Update.None;\r
386             }\r
387             if (url.EndsWith("api_req_hensei/preset_delete"))\r
388             {\r
389                 _presetDeck.InspectDelete(request);\r
390                 return Update.Timer;\r
391             }\r
392             if (url.EndsWith("api_req_hensei/combined"))\r
393             {\r
394                 _shipInfo.InspectCombined(request);\r
395                 return Update.Ship;\r
396             }\r
397             if (url.EndsWith("api_req_hokyu/charge"))\r
398             {\r
399                 _shipInfo.InspectCharge(data);\r
400                 _materialInfo.InspectCharge(data);\r
401                 return Update.Item | Update.Ship;\r
402             }\r
403             if (url.EndsWith("api_req_kaisou/powerup"))\r
404             {\r
405                 _shipInfo.InspectPowerup(request, data);\r
406                 _conditionTimer.CheckCond();\r
407                 _akashiTimer.CheckFleet();\r
408                 return Update.Item | Update.Ship;\r
409             }\r
410             if (url.EndsWith("api_req_kaisou/slot_exchange_index"))\r
411             {\r
412                 _shipInfo.InspectSlotExchange(request, data);\r
413                 return Update.Ship;\r
414             }\r
415             if (url.EndsWith("api_req_kaisou/slot_deprive"))\r
416             {\r
417                 _shipInfo.InspectSlotDeprive(data);\r
418                 return Update.Ship;\r
419             }\r
420             if (url.EndsWith("api_req_nyukyo/start"))\r
421             {\r
422                 _dockInfo.InspectNyukyo(request);\r
423                 _conditionTimer.CheckCond();\r
424                 _akashiTimer.CheckFleet();\r
425                 return Update.Item | Update.Ship;\r
426             }\r
427             if (url.EndsWith("api_req_nyukyo/speedchange"))\r
428             {\r
429                 _dockInfo.InspectSpeedChange(request);\r
430                 _conditionTimer.CheckCond();\r
431                 return Update.NDock | Update.Timer | Update.Item | Update.Ship;\r
432             }\r
433             if (url.EndsWith("api_req_map/start"))\r
434             {\r
435                 _shipInfo.InspectMapStart(request); // 出撃中判定が必要なので_conditionTimerより前\r
436                 _conditionTimer.InvalidateCond();\r
437                 _exMapInfo.InspectMapStart(data);\r
438                 _battleInfo.InspectMapStart(data);\r
439                 _logger.InspectMapStart(data);\r
440                 _miscTextInfo.ClearFlag = true;\r
441                 return Update.Timer | Update.Ship;\r
442             }\r
443             if (url.EndsWith("api_req_map/next"))\r
444             {\r
445                 _battleInfo.InspectMapNext(request);\r
446                 _exMapInfo.InspectMapNext(data);\r
447                 _battleInfo.InspectMapNext(data);\r
448                 _logger.InspectMapNext(data);\r
449                 return Update.None;\r
450             }\r
451             if (url.EndsWith("api_req_mission/result"))\r
452             {\r
453                 _materialInfo.InspectMissionResult(data);\r
454                 _logger.InspectMissionResult(data);\r
455                 return Update.Item;\r
456             }\r
457             if (url.EndsWith("api_req_quest/stop"))\r
458             {\r
459                 _questInfo.InspectStop(request);\r
460                 return Update.QuestList;\r
461             }\r
462             if (url.EndsWith("api_req_quest/clearitemget"))\r
463             {\r
464                 _questInfo.InspectClearItemGet(request);\r
465                 _logger.InspectClearItemGet(data);\r
466                 return Update.QuestList;\r
467             }\r
468             if (url.EndsWith("api_req_air_corps/supply"))\r
469             {\r
470                 _materialInfo.InspectAirCorpsSupply(data);\r
471                 _baseAirCoprs.InspectSupply(request, data);\r
472                 return Update.Item;\r
473             }\r
474             if (url.EndsWith("api_req_air_corps/set_plane"))\r
475             {\r
476                 _materialInfo.InspectAirCorpsSetPlane(data);\r
477                 _baseAirCoprs.InspectSetPlane(request, data);\r
478                 return Update.Item | Update.Ship;\r
479             }\r
480             if (url.EndsWith("api_req_air_corps/set_action"))\r
481             {\r
482                 _baseAirCoprs.InspectSetAction(request);\r
483                 return Update.Ship;\r
484             }\r
485             if (url.EndsWith("api_req_air_corps/expand_base"))\r
486             {\r
487                 _baseAirCoprs.InspectExpandBase(request, data);\r
488                 return Update.Ship;\r
489             }\r
490             return Update.None;\r
491         }\r
492 \r
493         public NameAndTimer[] NDock => _dockInfo.NDock;\r
494 \r
495         public RingTimer[] KDock => _dockInfo.KDock;\r
496 \r
497         public ItemInfo Item => _itemInfo;\r
498 \r
499         public MaterialInfo Material => _materialInfo;\r
500 \r
501         public QuestStatus[] Quests => _questInfo.Quests;\r
502 \r
503         public NameAndTimer[] Missions => _missionInfo.Missions;\r
504 \r
505         public DateTime GetConditionTimer(int fleet) => _conditionTimer.GetTimer(fleet);\r
506 \r
507         public int[] GetConditionNotice() => _conditionTimer.GetNotice();\r
508 \r
509         public ShipStatus[] GetShipStatuses(int fleet) => _shipInfo.GetShipStatuses(fleet);\r
510 \r
511         public int[] GetDeck(int fleet) => _shipInfo.GetDeck(fleet);\r
512 \r
513         public List<ShipInfo.ShipStatusPair> WrongBattleResult => _shipInfo.WrongBattleResult;\r
514 \r
515         public int CombinedFleetType => _shipInfo.CombinedFleetType;\r
516 \r
517         public ChargeStatus[] ChargeStatuses => _shipInfo.ChargeStatuses;\r
518 \r
519         public int[] GetFighterPower(int fleet) => _shipInfo.GetFighterPower(fleet);\r
520 \r
521         public double GetContactTriggerRate(int fleet) => _shipInfo.GetContactTriggerRate(fleet);\r
522 \r
523         public double GetFleetLineOfSights(int fleet, int factor) => _shipInfo.GetLineOfSights(fleet, factor);\r
524 \r
525         public ShipStatus[] RepairList => _shipInfo.GetRepairList(_dockInfo);\r
526 \r
527         public ShipStatus[] ShipList => _shipInfo.ShipList;\r
528 \r
529         public string[] BadlyDamagedShips => _shipInfo.BadlyDamagedShips;\r
530 \r
531         public double GetDaihatsuBonus(int fleet) => _shipInfo.GetDaihatsuBonus(fleet);\r
532 \r
533         public double GetTransportPoint(int fleet) => _shipInfo.GetTransportPoint(fleet);\r
534 \r
535         public ItemStatus[] ItemList\r
536         {\r
537             get\r
538             {\r
539                 _itemInfo.ClearHolder();\r
540                 _shipInfo.SetItemHolder();\r
541                 _baseAirCoprs.SetItemHolder();\r
542                 return _itemInfo.ItemList;\r
543             }\r
544         }\r
545 \r
546         public AkashiTimer AkashiTimer => _akashiTimer;\r
547 \r
548         public Achievement Achievement => _achievement;\r
549 \r
550         public BattleInfo Battle => _battleInfo;\r
551 \r
552         public ExMapInfo ExMap => _exMapInfo;\r
553 \r
554         public string MiscText => _miscTextInfo.Text;\r
555 \r
556         public BaseAirCoprs.BaseInfo[] BaseAirCorps => _baseAirCoprs.AllAirCorps;\r
557 \r
558         public bool UseOldEnemyId\r
559         {\r
560             set => _shipInfo.UseOldEnemyId = value;\r
561         }\r
562 \r
563         public void SetLogWriter(Action<string, string, string> writer, Func<DateTime> nowFunc)\r
564         {\r
565             _logger.SetWriter(writer, nowFunc);\r
566         }\r
567 \r
568         public void SkipMaster()\r
569         {\r
570             _start = true;\r
571         }\r
572 \r
573         public void EnableLog(LogType type)\r
574         {\r
575             _logger.EnableLog(type);\r
576         }\r
577 \r
578         public int MaterialLogInterval\r
579         {\r
580             set => _logger.MaterialLogInterval = value;\r
581         }\r
582 \r
583         public string LogOutputDir\r
584         {\r
585             set => _logger.OutputDir = value;\r
586         }\r
587 \r
588         public void FlashLog()\r
589         {\r
590             _logger.FlashLog();\r
591         }\r
592     }\r
593 \r
594     public class NameAndTimer\r
595     {\r
596         public string Name { get; set; }\r
597         public RingTimer Timer { get; set; }\r
598 \r
599         public NameAndTimer()\r
600         {\r
601             Timer = new RingTimer();\r
602         }\r
603     }\r
604 \r
605     public class RingTimer\r
606     {\r
607         private readonly TimeSpan _spare;\r
608 \r
609         public TimeSpan Rest { get; private set; }\r
610 \r
611         public bool IsFinished => EndTime != DateTime.MinValue && Rest <= _spare;\r
612 \r
613         public DateTime EndTime { get; private set; }\r
614 \r
615         public bool NeedRing { get; set; }\r
616 \r
617         public RingTimer(int spare = 60)\r
618         {\r
619             _spare = TimeSpan.FromSeconds(spare);\r
620         }\r
621 \r
622         public void SetEndTime(double time)\r
623         {\r
624             SetEndTime((int)time == 0\r
625                 ? DateTime.MinValue\r
626                 : new DateTime(1970, 1, 1).ToLocalTime().AddSeconds(time / 1000));\r
627         }\r
628 \r
629         public void SetEndTime(DateTime time)\r
630         {\r
631             EndTime = time;\r
632         }\r
633 \r
634         public void Update()\r
635         {\r
636             if (EndTime == DateTime.MinValue)\r
637             {\r
638                 Rest = TimeSpan.Zero;\r
639                 return;\r
640             }\r
641             var prev = Rest;\r
642             Rest = EndTime - DateTime.Now;\r
643             if (Rest < TimeSpan.Zero)\r
644                 Rest = TimeSpan.Zero;\r
645             if (prev > _spare && _spare >= Rest)\r
646                 NeedRing = true;\r
647         }\r
648 \r
649         public string ToString(bool finish = false)\r
650             => EndTime == DateTime.MinValue\r
651                 ? ""\r
652                 : finish ? EndTime.ToString(@"dd\ HH\:mm") : $@"{(int)Rest.TotalHours:d2}:{Rest:mm\:ss}";\r
653     }\r
654 }