Projekt

Obecné

Profil

Stáhnout (20 KB) Statistiky
| Větev: | Tag: | Revize:
1
//
2
// Author: A. Konig
3
//
4

    
5
using log4net;
6
using ServerApp.DataDownload;
7
using ServerApp.Parser.InputData;
8
using ServerApp.Parser.OutputInfo;
9
using System;
10
using System.Collections.Generic;
11
using System.Diagnostics;
12
using System.Globalization;
13
using System.Text.Json;
14
using static System.Text.Json.JsonElement;
15

    
16
namespace ServerApp.WeatherPredictionParser
17
{
18
    /// <summary>
19
    /// Class representing a parser for json prediction data
20
    /// </summary>
21
    /// <author>A. Konig</author>
22
    public class JsonParser : IJsonParser
23
    {
24
        /// <summary> Logger </summary>
25
        private static readonly ILog _log = LogManager.GetLogger(typeof(JsonParser));
26

    
27
        /// <summary> Data downloader </summary>
28
        IDataDownloader downloader;
29
        /// <summary> Data loader </summary>
30
        IDataLoader loader;
31
        /// <summary> Currently parsed day </summary>
32
        DateTime currParsedDay;
33
        /// <summary> Sunrise time of currently parsed day </summary>
34
        List<DateTime> sunriseTime;
35
        /// <summary> Sunset time of currently parsed day </summary>
36
        List<DateTime> sunsetTime;
37

    
38
        /// <summary> Last prediction </summary>
39
        private string data;
40
        /// <summary> Stopwatch for measuring time </summary>
41
        Stopwatch sw = new Stopwatch();
42

    
43
        /// <summary>
44
        /// Constructor
45
        /// </summary>
46
        /// <param name="downloader"></param>
47
        public JsonParser(IDataDownloader downloader, IDataLoader loader)
48
        {
49
            this.downloader = downloader;
50
            this.loader = loader;
51
            data = null;
52
            sw.Start();
53
        }
54

    
55
        /// <summary>
56
        /// Get predictions from Predictions that are within specified time span
57
        /// From-to including
58
        /// If from == DateTime.Min then all until to
59
        /// If to  == DateTime.Max then all starting from from
60
        /// </summary>
61
        /// <param name="from">DateTime from</param>
62
        /// <param name="to">DateTime to</param>
63
        /// <returns>List of predictions that fit specified criteria or null if incorrect input</returns>
64
        public override List<WeatherInfo> GetPredictionForTime(DateTime from, DateTime to)
65
        {
66
            if (Predictions == null)
67
                return null;
68

    
69
            List<WeatherInfo> res = new List<WeatherInfo>();
70

    
71
            if (from == DateTime.MinValue)
72
                from = Predictions[0].startTime;
73

    
74
            if (to == DateTime.MaxValue) {
75
                DateTime dt = Predictions[Predictions.Count - 1].startTime;
76
                int hour = dt.Hour + Predictions[Predictions.Count - 1].intervalLength;
77
                bool addDay = false;
78
                if (hour >= 24)
79
                {
80
                    hour -= 24;
81
                    addDay = true;
82
                }
83
                to = new DateTime(dt.Year, dt.Month, dt.Day, hour, dt.Minute, dt.Second);
84
                if (addDay)
85
                    to = to.AddDays(1);
86
            }
87

    
88
            if (from > to)
89
                return null;
90

    
91
            // for all parsed weather info
92
            foreach (WeatherInfo pred in Predictions)
93
            {
94
                int hour = pred.startTime.Hour + pred.intervalLength;
95
                bool addDay = false;
96
                if (hour >= 24)
97
                {
98
                    hour -= 24;
99
                    addDay = true;
100
                }
101
                DateTime endTime = new DateTime(pred.startTime.Year, pred.startTime.Month, pred.startTime.Day, hour, pred.startTime.Minute, pred.startTime.Second);
102
                if (addDay)
103
                    endTime = endTime.AddDays(1);
104

    
105
                // if both end and start not outside of interval
106
                if (!((pred.startTime < from && endTime <= from) || (pred.startTime > to && endTime > to)))
107
                    res.Add(pred);
108
            }
109

    
110
            return res;
111
        }
112

    
113

    
114
        /// <summary>
115
        /// Parse weather prediction
116
        /// Results is in attributes current for current weather and pred for weather prediction for today, tommorrow and day after tommorrow
117
        /// </summary>
118
        override public void ParsePrediction()
119
        {
120
            sunriseTime = new List<DateTime>();
121
            sunsetTime = new List<DateTime>();
122

    
123
            // get file
124
            //string file = downloader.DownloadWeatherPrediction();
125
            //string data = loader.LoadPredictionFile(file);
126

    
127
            // get data
128
            if (data == null || data.Trim().Length == 0 || (int)((sw.ElapsedMilliseconds / 1000.0) / 60.0) >= 60)
129
            {
130
                data = downloader.DownloadWeatherPrediction();
131
                sw = new Stopwatch();
132
                sw.Start();
133
            }
134

    
135
            DateTime now = DateTime.Now;
136

    
137
            Current = new WeatherInfo();
138
            Predictions = new List<WeatherInfo>();
139

    
140
            if (data == null || data.Length == 0)
141
                return;
142

    
143
            // parse
144
            try
145
            {
146
                JsonDocument doc = JsonDocument.Parse(data);
147
                JsonElement root = doc.RootElement;
148
                var weatherP = root.EnumerateObject();
149

    
150
                while (weatherP.MoveNext())
151
                {
152
                    string name = weatherP.Current.Name;
153

    
154
                    switch (name)
155
                    {
156
                        // current weather
157
                        case "current_condition":
158
                            {
159
                                ArrayEnumerator currentWeather = weatherP.Current.Value.EnumerateArray();
160
                                Current = ParseCurrentWeather(currentWeather);
161

    
162
                                break;
163
                            }
164
                        // weather prediction
165
                        case "weather":
166
                            {
167
                                ArrayEnumerator weather = weatherP.Current.Value.EnumerateArray();
168
                                ParseWeatherPredict(weather);
169

    
170
                                break;
171
                            }
172
                    }
173
                }
174

    
175
                // sunrise + sunset into data
176
                EncompassSunRiseSetTimes();
177
            } catch
178
            {
179
                _log.Error("Weather prediction file in incorrect format");
180
                Current = new WeatherInfo();
181
                Predictions = new List<WeatherInfo>();
182
                data = null;
183
            }
184

    
185
            //TestConsoleOutput();
186
        }
187

    
188
        /// <summary>
189
        /// Test console output
190
        /// </summary>
191
        private void TestConsoleOutput()
192
        {
193
            Console.WriteLine(Current);
194
            foreach (WeatherInfo w in Predictions)
195
                Console.WriteLine(w);
196
        }
197

    
198
        /// <summary>
199
        /// Change data in a way that they now reflect sunrise and sunset times
200
        /// If current time under sunrise or over sunset -> WeatherConditions is Dark
201
        /// If prediction time is under sunrise or over sunset and more than half of the interval is under/over said time -> WeatherCondition is Dark
202
        /// </summary>
203
        private void EncompassSunRiseSetTimes()
204
        {
205
            if (sunsetTime.Count < 1 || sunriseTime.Count < 0)
206
                return;
207

    
208
            // change current weather
209
            if ((Current.startTime.TimeOfDay > sunsetTime[0].TimeOfDay) || (Current.startTime.TimeOfDay < sunriseTime[0].TimeOfDay))
210
                Current.condition = WeatherConditions.Dark;
211

    
212
            // change prediction
213
            int index = 0;
214
            for (int i = 0; i < Predictions.Count - 1; i++)
215
            {
216
                WeatherInfo w = Predictions[i];
217
                WeatherInfo wNext = Predictions[i + 1];
218
                DateTime sunrise = sunriseTime[index];
219
                DateTime sunset = sunsetTime[index];
220

    
221

    
222
                // if wNext time < than w time then it is prediction from the next day -> add 24 to correctly calculate timespan
223
                int timespan = wNext.startTime.Hour - w.startTime.Hour;
224
                bool intoTwo = false;
225
                if (wNext.startTime.Hour < w.startTime.Hour)
226
                {
227
                    intoTwo = true;
228
                    timespan = (wNext.startTime.Hour + 24) - w.startTime.Hour;
229
                }
230
                
231
                w.intervalLength = timespan;
232
                TimeSpan endTime = new TimeSpan(w.startTime.TimeOfDay.Hours + timespan, w.startTime.TimeOfDay.Minutes, 0);
233

    
234
                // if start under sunset
235
                if (w.startTime.TimeOfDay > sunsetTime[index].TimeOfDay)
236
                {
237
                    // when is end - if end after next sunrise need to compute the extent of dark
238
                    if (intoTwo && endTime > sunriseTime[index+1].TimeOfDay)
239
                    {
240
                        double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunriseTime[index + 1].Hour * 60 + sunriseTime[index + 1].Minute)) / 60.0;
241
                        howMuch = w.intervalLength - howMuch;
242

    
243
                        if (howMuch >= timespan / 2.0)
244
                            w.condition = WeatherConditions.Dark;
245
                    }
246
                    else
247
                        w.condition = WeatherConditions.Dark;
248
                }
249

    
250
                // if start under sunrise
251
                if (w.startTime.TimeOfDay < sunriseTime[index].TimeOfDay)
252
                {
253
                    double howMuch = ((sunriseTime[index].Hour * 60 + sunriseTime[index].Minute) - (w.startTime.Hour * 60 + w.startTime.Minute)) / 60.0;
254
                    if (endTime > sunset.TimeOfDay)
255
                        howMuch += ((w.startTime.Hour * 60 + w.startTime.Minute) - (sunset.Hour * 60 + sunset.Minute)) / 60.0;
256

    
257
                    if (howMuch >= timespan / 2.0)
258
                        w.condition = WeatherConditions.Dark;
259
                }
260

    
261
                // if end over sunset
262
                else if (endTime > sunsetTime[index].TimeOfDay)
263
                {
264
                    double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunsetTime[index].Hour * 60 + sunsetTime[index].Minute)) / 60.0;
265

    
266
                    if (howMuch >= timespan / 2.0)
267
                        w.condition = WeatherConditions.Dark;
268
                }
269

    
270
                if (Predictions[i].startTime.Date !=  Predictions[i + 1].startTime.Date)
271
                    index++;
272
            }
273

    
274
            // last prediction
275
            WeatherInfo wLast = Predictions[Predictions.Count - 1];
276
            TimeSpan endTimeW = new TimeSpan(23, 59, 59);
277
            int timespanLast = 24 - wLast.startTime.Hour;
278
            wLast.intervalLength = timespanLast;
279

    
280
            // if start under sunset
281
            if (wLast.startTime.TimeOfDay > sunsetTime[sunsetTime.Count-1].TimeOfDay)
282
                wLast.condition = WeatherConditions.Dark;
283

    
284
            // if start under sunrise
285
            if (wLast.startTime.TimeOfDay < sunriseTime[sunriseTime.Count - 1].TimeOfDay)
286
            {
287
                double howMuch = ((sunriseTime[sunriseTime.Count - 1].Hour * 60 + sunriseTime[sunriseTime.Count - 1].Minute) - (wLast.startTime.Hour * 60 + wLast.startTime.Minute)) / 60.0;
288
                if (endTimeW > sunsetTime[sunriseTime.Count - 1].TimeOfDay)
289
                    howMuch += ((wLast.startTime.Hour * 60 + wLast.startTime.Minute) - (sunsetTime[sunriseTime.Count - 1].Hour * 60 + sunsetTime[sunriseTime.Count - 1].Minute)) / 60.0;
290

    
291
                if (howMuch >= timespanLast / 2.0)
292
                    wLast.condition = WeatherConditions.Dark;
293
            }
294

    
295
            // if start under sunrise
296
            if (endTimeW > sunsetTime[sunsetTime.Count - 1].TimeOfDay)
297
            {
298
                double howMuch = ((endTimeW.Hours * 60 + endTimeW.Minutes) - (sunsetTime[sunsetTime.Count - 1].Hour * 60 + sunsetTime[sunsetTime.Count - 1].Minute)) / 60.0;
299

    
300
                if (howMuch >= timespanLast / 2.0)
301
                    wLast.condition = WeatherConditions.Dark;
302
            }
303
        }
304

    
305
        /// <summary>
306
        /// Parse weather prediction
307
        /// </summary>
308
        /// <param name="weather"> ArrayEnumerator of weather predictions </param>
309
        private void ParseWeatherPredict(ArrayEnumerator weather)
310
        {
311
            while (weather.MoveNext())
312
            {
313
                // prediction for one day
314
                var obj = weather.Current.EnumerateObject();
315

    
316
                while (obj.MoveNext())
317
                {
318
                    switch (obj.Current.Name)
319
                    {
320
                        case "date":
321
                            {
322
                                DateTime.TryParseExact(obj.Current.Value.GetString(), "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out currParsedDay);
323
                                break;
324
                            }
325
                        // hourly predictions
326
                        case "hourly":
327
                            {
328
                                ParseHourly(obj.Current.Value.EnumerateArray());
329
                                break;
330
                            }
331
                        // sunset / sunrise hours
332
                        case "astronomy":
333
                            {
334
                                ParseAstronomy(obj.Current.Value.EnumerateArray());
335
                                break;
336
                            }
337
                    }
338
                }
339
            }
340
        }
341

    
342
        /// <summary>
343
        /// Parse sunrise and sunset times
344
        /// </summary>
345
        /// <param name="astronomy"> Astronomy array enumerator </param>
346
        private void ParseAstronomy(ArrayEnumerator astronomy)
347
        {
348
            while (astronomy.MoveNext())
349
            {
350
                var astrInfo = astronomy.Current.EnumerateObject();
351
                
352
                while (astrInfo.MoveNext())
353
                {
354
                    switch (astrInfo.Current.Name)
355
                    {
356
                        case "sunrise":
357
                            {
358
                                DateTime newSunrise = new DateTime();
359
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out newSunrise);
360
                                sunriseTime.Add(newSunrise);
361
                                break;
362
                            }
363
                        case "sunset":
364
                            {
365
                                DateTime newSunset = new DateTime();
366
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out newSunset);
367
                                sunsetTime.Add(newSunset);
368
                                break;
369
                            }
370
                    }
371
                }
372
            }
373
        }
374

    
375
        /// <summary>
376
        /// Parse hourly predictions
377
        /// </summary>
378
        /// <param name="hourly">Enumerated array of hourly predictions</param>
379
        private void ParseHourly(ArrayEnumerator hourly)
380
        {
381
            while (hourly.MoveNext())
382
            {
383
                // one hourly prediction
384
                var oneH = hourly.Current.EnumerateObject();
385
                WeatherInfo weather = new WeatherInfo();
386

    
387
                while (oneH.MoveNext())
388
                {
389
                    switch (oneH.Current.Name)
390
                    {
391
                        case "FeelsLikeC":
392
                            {
393
                                Double.TryParse(oneH.Current.Value.GetString(), out weather.temp);
394
                                break;
395
                            }
396
                        case "cloudcover":
397
                            {
398
                                int cloudCover;
399
                                Int32.TryParse(oneH.Current.Value.GetString(), out cloudCover);
400
                                weather.condition = ValueToConditions.CloudCoverToConditions(cloudCover);
401
                                weather.lum = ValueToConditions.TransferConditionsToLux(weather.condition);
402
                                break;
403
                            }
404
                        // take into account highest value from "chanceofrain" and "chaceofsnow"
405
                        case "chanceofrain":
406
                            {
407
                                int rain;
408
                                Int32.TryParse(oneH.Current.Value.GetString(), out rain);
409
                                weather.rain = rain > weather.rain ? rain : weather.rain;
410
                                break;
411
                            }
412
                        case "chanceofsnow":
413
                            {
414
                                int snow;
415
                                Int32.TryParse(oneH.Current.Value.GetString(), out snow);
416
                                weather.rain = snow > weather.rain ? snow : weather.rain;
417
                                break;
418
                            }
419
                        // wind kmph has to be translated to mps
420
                        case "windspeedKmph":
421
                            {
422
                                double windkmh;
423
                                Double.TryParse(oneH.Current.Value.GetString(), out windkmh);
424
                                weather.wind= windkmh * 1000 / (60.0*60.0);
425
                                break;
426
                            }
427
                        case "time":
428
                            {
429
                                int h;
430
                                Int32.TryParse(oneH.Current.Value.GetString(), out h);
431
                                h /= 100;
432
                                DateTime time = new DateTime(currParsedDay.Year, currParsedDay.Month, currParsedDay.Day, h, 0, 0);
433
                                weather.startTime = time;
434
                                break;
435
                            }
436
                    }
437
                }
438

    
439
                Predictions.Add(weather);
440
            }
441

    
442
        }
443

    
444
        /// <summary>
445
        /// Parse current weather
446
        /// </summary>
447
        /// <param name="currentWeather">Enumerated hour of weather data</param>
448
        /// <returns>WeatherInfo with current weather</returns>
449
        private WeatherInfo ParseCurrentWeather(ArrayEnumerator currentWeather)
450
        {
451
            WeatherInfo res = new WeatherInfo();
452
            res.intervalLength = 0;
453
            //res.current = true;
454

    
455
            while (currentWeather.MoveNext())
456
            {
457
                var obj = currentWeather.Current.EnumerateObject();
458

    
459
                while (obj.MoveNext())
460
                {
461
                    switch (obj.Current.Name)
462
                    {
463
                        case "localObsDateTime":
464
                            {
465
                                DateTime.TryParse(obj.Current.Value.GetString(), out res.startTime);
466
                                break;
467
                            }
468
                        case "FeelsLikeC":
469
                            {
470
                                Double.TryParse(obj.Current.Value.GetString(), out res.temp);
471
                                break;
472
                            }
473
                        case "cloudcover":
474
                            {
475
                                int cloudCover;
476
                                Int32.TryParse(obj.Current.Value.GetString(), out cloudCover);
477
                                res.condition = ValueToConditions.CloudCoverToConditions(cloudCover);
478
                                break;
479
                            }
480
                        case "precipMM":
481
                            {
482
                                double rainMM;
483
                                Double.TryParse(obj.Current.Value.GetString(), out rainMM);
484
                                res.rain = rainMM > 0 ? 100 : 0;
485
                                break;
486
                            }
487
                        case "windspeedKmph":
488
                            {
489
                                double wind;
490
                                Double.TryParse(obj.Current.Value.GetString(), out wind);
491
                                res.wind = wind * 1000 / (60.0 * 60.0);
492
                                break;
493
                            }
494
                    }
495
                }
496

    
497
            }
498

    
499
            res.lum = ValueToConditions.TransferConditionsToLux(res.condition);
500
            return res;
501
        }
502

    
503
      
504
    }
505
}
(2-2/2)