Projekt

Obecné

Profil

Stáhnout (19.6 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.Globalization;
12
using System.Text.Json;
13
using static System.Text.Json.JsonElement;
14

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

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

    
37
        /// <summary>
38
        /// Constructor
39
        /// </summary>
40
        /// <param name="downloader"></param>
41
        public JsonParser(IDataDownloader downloader, IDataLoader loader)
42
        {
43
            this.downloader = downloader;
44
            this.loader = loader;
45
        }
46

    
47
        /// <summary>
48
        /// Get predictions from Predictions that are within specified time span
49
        /// From-to including
50
        /// If from == DateTime.Min then all until to
51
        /// If to  == DateTime.Max then all starting from from
52
        /// </summary>
53
        /// <param name="from">DateTime from</param>
54
        /// <param name="to">DateTime to</param>
55
        /// <returns>List of predictions that fit specified criteria or null if incorrect input</returns>
56
        public override List<WeatherInfo> GetPredictionForTime(DateTime from, DateTime to)
57
        {
58
            if (Predictions == null)
59
                return null;
60

    
61
            List<WeatherInfo> res = new List<WeatherInfo>();
62

    
63
            if (from == DateTime.MinValue)
64
                from = Predictions[0].startTime;
65

    
66
            if (to == DateTime.MaxValue) {
67
                DateTime dt = Predictions[Predictions.Count - 1].startTime;
68
                int hour = dt.Hour + Predictions[Predictions.Count - 1].intervalLength;
69
                bool addDay = false;
70
                if (hour >= 24)
71
                {
72
                    hour -= 24;
73
                    addDay = true;
74
                }
75
                to = new DateTime(dt.Year, dt.Month, dt.Day, hour, dt.Minute, dt.Second);
76
                if (addDay)
77
                    to = to.AddDays(1);
78
            }
79

    
80
            if (from > to)
81
                return null;
82

    
83
            // for all parsed weather info
84
            foreach (WeatherInfo pred in Predictions)
85
            {
86
                int hour = pred.startTime.Hour + pred.intervalLength;
87
                bool addDay = false;
88
                if (hour >= 24)
89
                {
90
                    hour -= 24;
91
                    addDay = true;
92
                }
93
                DateTime endTime = new DateTime(pred.startTime.Year, pred.startTime.Month, pred.startTime.Day, hour, pred.startTime.Minute, pred.startTime.Second);
94
                if (addDay)
95
                    endTime = endTime.AddDays(1);
96

    
97
                // if both end and start not outside of interval
98
                if (!((pred.startTime < from && endTime <= from) || (pred.startTime > to && endTime > to)))
99
                    res.Add(pred);
100
            }
101

    
102
            return res;
103
        }
104

    
105

    
106
        /// <summary>
107
        /// Parse weather prediction
108
        /// Results is in attributes current for current weather and pred for weather prediction for today, tommorrow and day after tommorrow
109
        /// </summary>
110
        override public void ParsePrediction()
111
        {
112
            sunriseTime = new List<DateTime>();
113
            sunsetTime = new List<DateTime>();
114

    
115
            // get file
116
            //string file = downloader.DownloadWeatherPrediction();
117
            //string data = loader.LoadPredictionFile(file);
118

    
119
            // get data
120
            string data = downloader.DownloadWeatherPrediction();
121

    
122
            DateTime now = DateTime.Now;
123

    
124
            Current = new WeatherInfo();
125
            Predictions = new List<WeatherInfo>();
126

    
127
            if (data == null || data.Length == 0)
128
                return;
129

    
130
            // parse
131
            try
132
            {
133
                JsonDocument doc = JsonDocument.Parse(data);
134
                JsonElement root = doc.RootElement;
135
                var weatherP = root.EnumerateObject();
136

    
137
                while (weatherP.MoveNext())
138
                {
139
                    string name = weatherP.Current.Name;
140

    
141
                    switch (name)
142
                    {
143
                        // current weather
144
                        case "current_condition":
145
                            {
146
                                ArrayEnumerator currentWeather = weatherP.Current.Value.EnumerateArray();
147
                                Current = ParseCurrentWeather(currentWeather);
148

    
149
                                break;
150
                            }
151
                        // weather prediction
152
                        case "weather":
153
                            {
154
                                ArrayEnumerator weather = weatherP.Current.Value.EnumerateArray();
155
                                ParseWeatherPredict(weather);
156

    
157
                                break;
158
                            }
159
                    }
160
                }
161

    
162
                // sunrise + sunset into data
163
                EncompassSunRiseSetTimes();
164
            } catch
165
            {
166
                _log.Error("Weather prediction file in incorrect format");
167
                Current = new WeatherInfo();
168
                Predictions = new List<WeatherInfo>();
169
            }
170

    
171
            //TestConsoleOutput();
172
        }
173

    
174
        /// <summary>
175
        /// Test console output
176
        /// </summary>
177
        private void TestConsoleOutput()
178
        {
179
            Console.WriteLine(Current);
180
            foreach (WeatherInfo w in Predictions)
181
                Console.WriteLine(w);
182
        }
183

    
184
        /// <summary>
185
        /// Change data in a way that they now reflect sunrise and sunset times
186
        /// If current time under sunrise or over sunset -> WeatherConditions is Dark
187
        /// If prediction time is under sunrise or over sunset and more than half of the interval is under/over said time -> WeatherCondition is Dark
188
        /// </summary>
189
        private void EncompassSunRiseSetTimes()
190
        {
191
            if (sunsetTime.Count < 1 || sunriseTime.Count < 0)
192
                return;
193

    
194
            // change current weather
195
            if ((Current.startTime.TimeOfDay > sunsetTime[0].TimeOfDay) || (Current.startTime.TimeOfDay < sunriseTime[0].TimeOfDay))
196
                Current.condition = WeatherConditions.Dark;
197

    
198
            // change prediction
199
            int index = 0;
200
            for (int i = 0; i < Predictions.Count - 1; i++)
201
            {
202
                WeatherInfo w = Predictions[i];
203
                WeatherInfo wNext = Predictions[i + 1];
204
                DateTime sunrise = sunriseTime[index];
205
                DateTime sunset = sunsetTime[index];
206

    
207

    
208
                // if wNext time < than w time then it is prediction from the next day -> add 24 to correctly calculate timespan
209
                int timespan = wNext.startTime.Hour - w.startTime.Hour;
210
                bool intoTwo = false;
211
                if (wNext.startTime.Hour < w.startTime.Hour)
212
                {
213
                    intoTwo = true;
214
                    timespan = (wNext.startTime.Hour + 24) - w.startTime.Hour;
215
                }
216
                
217
                w.intervalLength = timespan;
218
                TimeSpan endTime = new TimeSpan(w.startTime.TimeOfDay.Hours + timespan, w.startTime.TimeOfDay.Minutes, 0);
219

    
220
                // if start under sunset
221
                if (w.startTime.TimeOfDay > sunsetTime[index].TimeOfDay)
222
                {
223
                    // when is end - if end after next sunrise need to compute the extent of dark
224
                    if (intoTwo && endTime > sunriseTime[index+1].TimeOfDay)
225
                    {
226
                        double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunriseTime[index + 1].Hour * 60 + sunriseTime[index + 1].Minute)) / 60.0;
227
                        howMuch = w.intervalLength - howMuch;
228

    
229
                        if (howMuch >= timespan / 2.0)
230
                            w.condition = WeatherConditions.Dark;
231
                    }
232
                    else
233
                        w.condition = WeatherConditions.Dark;
234
                }
235

    
236
                // if start under sunrise
237
                if (w.startTime.TimeOfDay < sunriseTime[index].TimeOfDay)
238
                {
239
                    double howMuch = ((sunriseTime[index].Hour * 60 + sunriseTime[index].Minute) - (w.startTime.Hour * 60 + w.startTime.Minute)) / 60.0;
240
                    if (endTime > sunset.TimeOfDay)
241
                        howMuch += ((w.startTime.Hour * 60 + w.startTime.Minute) - (sunset.Hour * 60 + sunset.Minute)) / 60.0;
242

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

    
247
                // if end over sunset
248
                else if (endTime > sunsetTime[index].TimeOfDay)
249
                {
250
                    double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunsetTime[index].Hour * 60 + sunsetTime[index].Minute)) / 60.0;
251

    
252
                    if (howMuch >= timespan / 2.0)
253
                        w.condition = WeatherConditions.Dark;
254
                }
255

    
256
                if (Predictions[i].startTime.Date !=  Predictions[i + 1].startTime.Date)
257
                    index++;
258
            }
259

    
260
            // last prediction
261
            WeatherInfo wLast = Predictions[Predictions.Count - 1];
262
            TimeSpan endTimeW = new TimeSpan(23, 59, 59);
263
            int timespanLast = 24 - wLast.startTime.Hour;
264
            wLast.intervalLength = timespanLast;
265

    
266
            // if start under sunset
267
            if (wLast.startTime.TimeOfDay > sunsetTime[sunsetTime.Count-1].TimeOfDay)
268
                wLast.condition = WeatherConditions.Dark;
269

    
270
            // if start under sunrise
271
            if (wLast.startTime.TimeOfDay < sunriseTime[sunriseTime.Count - 1].TimeOfDay)
272
            {
273
                double howMuch = ((sunriseTime[sunriseTime.Count - 1].Hour * 60 + sunriseTime[sunriseTime.Count - 1].Minute) - (wLast.startTime.Hour * 60 + wLast.startTime.Minute)) / 60.0;
274
                if (endTimeW > sunsetTime[sunriseTime.Count - 1].TimeOfDay)
275
                    howMuch += ((wLast.startTime.Hour * 60 + wLast.startTime.Minute) - (sunsetTime[sunriseTime.Count - 1].Hour * 60 + sunsetTime[sunriseTime.Count - 1].Minute)) / 60.0;
276

    
277
                if (howMuch >= timespanLast / 2.0)
278
                    wLast.condition = WeatherConditions.Dark;
279
            }
280

    
281
            // if start under sunrise
282
            if (endTimeW > sunsetTime[sunsetTime.Count - 1].TimeOfDay)
283
            {
284
                double howMuch = ((endTimeW.Hours * 60 + endTimeW.Minutes) - (sunsetTime[sunsetTime.Count - 1].Hour * 60 + sunsetTime[sunsetTime.Count - 1].Minute)) / 60.0;
285

    
286
                if (howMuch >= timespanLast / 2.0)
287
                    wLast.condition = WeatherConditions.Dark;
288
            }
289
        }
290

    
291
        /// <summary>
292
        /// Parse weather prediction
293
        /// </summary>
294
        /// <param name="weather"> ArrayEnumerator of weather predictions </param>
295
        private void ParseWeatherPredict(ArrayEnumerator weather)
296
        {
297
            while (weather.MoveNext())
298
            {
299
                // prediction for one day
300
                var obj = weather.Current.EnumerateObject();
301

    
302
                while (obj.MoveNext())
303
                {
304
                    switch (obj.Current.Name)
305
                    {
306
                        case "date":
307
                            {
308
                                DateTime.TryParseExact(obj.Current.Value.GetString(), "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out currParsedDay);
309
                                break;
310
                            }
311
                        // hourly predictions
312
                        case "hourly":
313
                            {
314
                                ParseHourly(obj.Current.Value.EnumerateArray());
315
                                break;
316
                            }
317
                        // sunset / sunrise hours
318
                        case "astronomy":
319
                            {
320
                                ParseAstronomy(obj.Current.Value.EnumerateArray());
321
                                break;
322
                            }
323
                    }
324
                }
325
            }
326
        }
327

    
328
        /// <summary>
329
        /// Parse sunrise and sunset times
330
        /// </summary>
331
        /// <param name="astronomy"> Astronomy array enumerator </param>
332
        private void ParseAstronomy(ArrayEnumerator astronomy)
333
        {
334
            while (astronomy.MoveNext())
335
            {
336
                var astrInfo = astronomy.Current.EnumerateObject();
337
                
338
                while (astrInfo.MoveNext())
339
                {
340
                    switch (astrInfo.Current.Name)
341
                    {
342
                        case "sunrise":
343
                            {
344
                                DateTime newSunrise = new DateTime();
345
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out newSunrise);
346
                                sunriseTime.Add(newSunrise);
347
                                break;
348
                            }
349
                        case "sunset":
350
                            {
351
                                DateTime newSunset = new DateTime();
352
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out newSunset);
353
                                sunsetTime.Add(newSunset);
354
                                break;
355
                            }
356
                    }
357
                }
358
            }
359
        }
360

    
361
        /// <summary>
362
        /// Parse hourly predictions
363
        /// </summary>
364
        /// <param name="hourly">Enumerated array of hourly predictions</param>
365
        private void ParseHourly(ArrayEnumerator hourly)
366
        {
367
            while (hourly.MoveNext())
368
            {
369
                // one hourly prediction
370
                var oneH = hourly.Current.EnumerateObject();
371
                WeatherInfo weather = new WeatherInfo();
372

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

    
425
                Predictions.Add(weather);
426
            }
427

    
428
        }
429

    
430
        /// <summary>
431
        /// Parse current weather
432
        /// </summary>
433
        /// <param name="currentWeather">Enumerated hour of weather data</param>
434
        /// <returns>WeatherInfo with current weather</returns>
435
        private WeatherInfo ParseCurrentWeather(ArrayEnumerator currentWeather)
436
        {
437
            WeatherInfo res = new WeatherInfo();
438
            res.intervalLength = 0;
439
            //res.current = true;
440

    
441
            while (currentWeather.MoveNext())
442
            {
443
                var obj = currentWeather.Current.EnumerateObject();
444

    
445
                while (obj.MoveNext())
446
                {
447
                    switch (obj.Current.Name)
448
                    {
449
                        case "localObsDateTime":
450
                            {
451
                                DateTime.TryParse(obj.Current.Value.GetString(), out res.startTime);
452
                                break;
453
                            }
454
                        case "FeelsLikeC":
455
                            {
456
                                Double.TryParse(obj.Current.Value.GetString(), out res.temp);
457
                                break;
458
                            }
459
                        case "cloudcover":
460
                            {
461
                                int cloudCover;
462
                                Int32.TryParse(obj.Current.Value.GetString(), out cloudCover);
463
                                res.condition = ValueToConditions.CloudCoverToConditions(cloudCover);
464
                                break;
465
                            }
466
                        case "precipMM":
467
                            {
468
                                double rainMM;
469
                                Double.TryParse(obj.Current.Value.GetString(), out rainMM);
470
                                res.rain = rainMM > 0 ? 100 : 0;
471
                                break;
472
                            }
473
                        case "windspeedKmph":
474
                            {
475
                                double wind;
476
                                Double.TryParse(obj.Current.Value.GetString(), out wind);
477
                                res.wind = wind * 1000 / (60.0 * 60.0);
478
                                break;
479
                            }
480
                    }
481
                }
482

    
483
            }
484

    
485
            res.lum = ValueToConditions.TransferConditionsToLux(res.condition);
486
            return res;
487
        }
488

    
489
      
490
    }
491
}
(2-2/2)