Projekt

Obecné

Profil

Stáhnout (20 KB) Statistiky
| Větev: | Tag: | Revize:
1 cdf3c217 A-Konig
//
2
// Author: A. Konig
3
//
4
5 f9dd116f A-Konig
using log4net;
6 cdf3c217 A-Konig
using ServerApp.DataDownload;
7 26ecc756 A-Konig
using ServerApp.Parser.InputData;
8 1407c8ba A-Konig
using ServerApp.Parser.OutputInfo;
9 c9eed50c A-Konig
using System;
10
using System.Collections.Generic;
11 293051ee A-Konig
using System.Diagnostics;
12 1407c8ba A-Konig
using System.Globalization;
13 c9eed50c A-Konig
using System.Text.Json;
14 1407c8ba A-Konig
using static System.Text.Json.JsonElement;
15 c9eed50c A-Konig
16 cdf3c217 A-Konig
namespace ServerApp.WeatherPredictionParser
17 c9eed50c A-Konig
{
18 1407c8ba A-Konig
    /// <summary>
19
    /// Class representing a parser for json prediction data
20
    /// </summary>
21
    /// <author>A. Konig</author>
22 4b847de5 A-Konig
    public class JsonParser : IJsonParser
23 c9eed50c A-Konig
    {
24 f9dd116f A-Konig
        /// <summary> Logger </summary>
25
        private static readonly ILog _log = LogManager.GetLogger(typeof(JsonParser));
26
27 26ecc756 A-Konig
        /// <summary> Data downloader </summary>
28 27aa66b5 A-Konig
        IDataDownloader downloader;
29 1407c8ba A-Konig
        /// <summary> Data loader </summary>
30 26ecc756 A-Konig
        IDataLoader loader;
31 1407c8ba A-Konig
        /// <summary> Currently parsed day </summary>
32
        DateTime currParsedDay;
33
        /// <summary> Sunrise time of currently parsed day </summary>
34 bfd5a848 A-Konig
        List<DateTime> sunriseTime;
35 1407c8ba A-Konig
        /// <summary> Sunset time of currently parsed day </summary>
36 bfd5a848 A-Konig
        List<DateTime> sunsetTime;
37 cdf3c217 A-Konig
38 293051ee A-Konig
        /// <summary> Last prediction </summary>
39
        private string data;
40
        /// <summary> Stopwatch for measuring time </summary>
41
        Stopwatch sw = new Stopwatch();
42
43 1407c8ba A-Konig
        /// <summary>
44
        /// Constructor
45
        /// </summary>
46 26ecc756 A-Konig
        /// <param name="downloader"></param>
47 27aa66b5 A-Konig
        public JsonParser(IDataDownloader downloader, IDataLoader loader)
48 c9eed50c A-Konig
        {
49 26ecc756 A-Konig
            this.downloader = downloader;
50 c9eed50c A-Konig
            this.loader = loader;
51 293051ee A-Konig
            data = null;
52
            sw.Start();
53 c9eed50c A-Konig
        }
54
55 346be8db A-Konig
        /// <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 1407c8ba A-Konig
        /// <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 cdf3c217 A-Konig
        override public void ParsePrediction()
119 c9eed50c A-Konig
        {
120 bfd5a848 A-Konig
            sunriseTime = new List<DateTime>();
121
            sunsetTime = new List<DateTime>();
122
123 c9eed50c A-Konig
            // get file
124 27aa66b5 A-Konig
            //string file = downloader.DownloadWeatherPrediction();
125
            //string data = loader.LoadPredictionFile(file);
126 26ecc756 A-Konig
127 27aa66b5 A-Konig
            // get data
128 293051ee A-Konig
            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 26ecc756 A-Konig
135 c9eed50c A-Konig
            DateTime now = DateTime.Now;
136
137 ce0940b5 Roman Kalivoda
            Current = new WeatherInfo();
138
            Predictions = new List<WeatherInfo>();
139 cdf3c217 A-Konig
140 27aa66b5 A-Konig
            if (data == null || data.Length == 0)
141
                return;
142 c9eed50c A-Konig
143 27aa66b5 A-Konig
            // parse
144
            try
145 c9eed50c A-Konig
            {
146 27aa66b5 A-Konig
                JsonDocument doc = JsonDocument.Parse(data);
147
                JsonElement root = doc.RootElement;
148
                var weatherP = root.EnumerateObject();
149 c9eed50c A-Konig
150 27aa66b5 A-Konig
                while (weatherP.MoveNext())
151 c9eed50c A-Konig
                {
152 27aa66b5 A-Konig
                    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 c9eed50c A-Konig
                }
174
175 27aa66b5 A-Konig
                // sunrise + sunset into data
176
                EncompassSunRiseSetTimes();
177
            } catch
178
            {
179 f9dd116f A-Konig
                _log.Error("Weather prediction file in incorrect format");
180 23e8ae04 A-Konig
                Current = new WeatherInfo();
181
                Predictions = new List<WeatherInfo>();
182 293051ee A-Konig
                data = null;
183 27aa66b5 A-Konig
            }
184 1407c8ba A-Konig
185 9fb55c71 A-Konig
            //TestConsoleOutput();
186 1407c8ba A-Konig
        }
187
188 cdf3c217 A-Konig
        /// <summary>
189
        /// Test console output
190
        /// </summary>
191 1407c8ba A-Konig
        private void TestConsoleOutput()
192
        {
193 ce0940b5 Roman Kalivoda
            Console.WriteLine(Current);
194
            foreach (WeatherInfo w in Predictions)
195 1407c8ba A-Konig
                Console.WriteLine(w);
196 c9eed50c A-Konig
        }
197
198 1407c8ba A-Konig
        /// <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 bfd5a848 A-Konig
            if (sunsetTime.Count < 1 || sunriseTime.Count < 0)
206
                return;
207
208 1407c8ba A-Konig
            // change current weather
209 bfd5a848 A-Konig
            if ((Current.startTime.TimeOfDay > sunsetTime[0].TimeOfDay) || (Current.startTime.TimeOfDay < sunriseTime[0].TimeOfDay))
210 4b847de5 A-Konig
                Current.condition = WeatherConditions.Dark;
211 1407c8ba A-Konig
212
            // change prediction
213 bfd5a848 A-Konig
            int index = 0;
214 4b847de5 A-Konig
            for (int i = 0; i < Predictions.Count - 1; i++)
215 1407c8ba A-Konig
            {
216 4b847de5 A-Konig
                WeatherInfo w = Predictions[i];
217
                WeatherInfo wNext = Predictions[i + 1];
218 8a243ab2 A-Konig
                DateTime sunrise = sunriseTime[index];
219
                DateTime sunset = sunsetTime[index];
220
221 1407c8ba A-Konig
222 c0dffb79 A-Konig
                // if wNext time < than w time then it is prediction from the next day -> add 24 to correctly calculate timespan
223 1407c8ba A-Konig
                int timespan = wNext.startTime.Hour - w.startTime.Hour;
224 8a243ab2 A-Konig
                bool intoTwo = false;
225 c0dffb79 A-Konig
                if (wNext.startTime.Hour < w.startTime.Hour)
226 bfd5a848 A-Konig
                {
227 8a243ab2 A-Konig
                    intoTwo = true;
228 bfd5a848 A-Konig
                    timespan = (wNext.startTime.Hour + 24) - w.startTime.Hour;
229
                }
230 c0dffb79 A-Konig
                
231 1407c8ba A-Konig
                w.intervalLength = timespan;
232 8a243ab2 A-Konig
                TimeSpan endTime = new TimeSpan(w.startTime.TimeOfDay.Hours + timespan, w.startTime.TimeOfDay.Minutes, 0);
233 1407c8ba A-Konig
234
                // if start under sunset
235 bfd5a848 A-Konig
                if (w.startTime.TimeOfDay > sunsetTime[index].TimeOfDay)
236 8a243ab2 A-Konig
                {
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 1407c8ba A-Konig
250
                // if start under sunrise
251 bfd5a848 A-Konig
                if (w.startTime.TimeOfDay < sunriseTime[index].TimeOfDay)
252 1407c8ba A-Konig
                {
253 bfd5a848 A-Konig
                    double howMuch = ((sunriseTime[index].Hour * 60 + sunriseTime[index].Minute) - (w.startTime.Hour * 60 + w.startTime.Minute)) / 60.0;
254 8a243ab2 A-Konig
                    if (endTime > sunset.TimeOfDay)
255
                        howMuch += ((w.startTime.Hour * 60 + w.startTime.Minute) - (sunset.Hour * 60 + sunset.Minute)) / 60.0;
256 1407c8ba A-Konig
257
                    if (howMuch >= timespan / 2.0)
258
                        w.condition = WeatherConditions.Dark;
259
                }
260
261 8a243ab2 A-Konig
                // if end over sunset
262
                else if (endTime > sunsetTime[index].TimeOfDay)
263 1407c8ba A-Konig
                {
264 bfd5a848 A-Konig
                    double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunsetTime[index].Hour * 60 + sunsetTime[index].Minute)) / 60.0;
265 1407c8ba A-Konig
266
                    if (howMuch >= timespan / 2.0)
267
                        w.condition = WeatherConditions.Dark;
268
                }
269 bfd5a848 A-Konig
270
                if (Predictions[i].startTime.Date !=  Predictions[i + 1].startTime.Date)
271
                    index++;
272 1407c8ba A-Konig
            }
273
274
            // last prediction
275 4b847de5 A-Konig
            WeatherInfo wLast = Predictions[Predictions.Count - 1];
276 8a243ab2 A-Konig
            TimeSpan endTimeW = new TimeSpan(23, 59, 59);
277 fffe7190 A-Konig
            int timespanLast = 24 - wLast.startTime.Hour;
278 1407c8ba A-Konig
            wLast.intervalLength = timespanLast;
279
280
            // if start under sunset
281 bfd5a848 A-Konig
            if (wLast.startTime.TimeOfDay > sunsetTime[sunsetTime.Count-1].TimeOfDay)
282 1407c8ba A-Konig
                wLast.condition = WeatherConditions.Dark;
283
284
            // if start under sunrise
285 bfd5a848 A-Konig
            if (wLast.startTime.TimeOfDay < sunriseTime[sunriseTime.Count - 1].TimeOfDay)
286 1407c8ba A-Konig
            {
287 bfd5a848 A-Konig
                double howMuch = ((sunriseTime[sunriseTime.Count - 1].Hour * 60 + sunriseTime[sunriseTime.Count - 1].Minute) - (wLast.startTime.Hour * 60 + wLast.startTime.Minute)) / 60.0;
288 8a243ab2 A-Konig
                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 1407c8ba A-Konig
291
                if (howMuch >= timespanLast / 2.0)
292
                    wLast.condition = WeatherConditions.Dark;
293
            }
294
295
            // if start under sunrise
296 bfd5a848 A-Konig
            if (endTimeW > sunsetTime[sunsetTime.Count - 1].TimeOfDay)
297 1407c8ba A-Konig
            {
298 bfd5a848 A-Konig
                double howMuch = ((endTimeW.Hours * 60 + endTimeW.Minutes) - (sunsetTime[sunsetTime.Count - 1].Hour * 60 + sunsetTime[sunsetTime.Count - 1].Minute)) / 60.0;
299 1407c8ba A-Konig
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 bfd5a848 A-Konig
                                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 1407c8ba A-Konig
                                break;
362
                            }
363
                        case "sunset":
364
                            {
365 bfd5a848 A-Konig
                                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 1407c8ba A-Konig
                                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 bfd5a848 A-Konig
                                weather.lum = ValueToConditions.TransferConditionsToLux(weather.condition);
402 1407c8ba A-Konig
                                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 bfd5a848 A-Konig
                        case "windspeedKmph":
421 1407c8ba A-Konig
                            {
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 4b847de5 A-Konig
                Predictions.Add(weather);
440 1407c8ba A-Konig
            }
441 c9eed50c A-Konig
442
        }
443 1407c8ba A-Konig
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 bfd5a848 A-Konig
            res.intervalLength = 0;
453 1407c8ba A-Konig
            //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 bfd5a848 A-Konig
            res.lum = ValueToConditions.TransferConditionsToLux(res.condition);
500 1407c8ba A-Konig
            return res;
501
        }
502
503 ce0940b5 Roman Kalivoda
      
504 c9eed50c A-Konig
    }
505
}