Projekt

Obecné

Profil

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

    
5
using ServerApp.DataDownload;
6
using ServerApp.Parser.InputData;
7
using ServerApp.Parser.OutputInfo;
8
using System;
9
using System.Collections.Generic;
10
using System.Globalization;
11
using System.IO;
12
using System.Net;
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> Data downloader </summary>
25
        DataDownloader downloader;
26
        /// <summary> Data loader </summary>
27
        IDataLoader loader;
28
        /// <summary> Currently parsed day </summary>
29
        DateTime currParsedDay;
30
        /// <summary> Sunrise time of currently parsed day </summary>
31
        List<DateTime> sunriseTime;
32
        /// <summary> Sunset time of currently parsed day </summary>
33
        List<DateTime> sunsetTime;
34

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

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

    
59
            List<WeatherInfo> res = new List<WeatherInfo>();
60

    
61
            if (from == DateTime.MinValue)
62
                from = Predictions[0].startTime;
63

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

    
78
            if (from > to)
79
                return null;
80

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

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

    
100
            return res;
101
        }
102

    
103

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

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

    
117
            if (data == null)
118
                return;
119

    
120
            DateTime now = DateTime.Now;
121

    
122
            Current = new WeatherInfo();
123
            Predictions = new List<WeatherInfo>();
124

    
125
            // parse
126
            JsonDocument doc = JsonDocument.Parse(data);
127
            JsonElement root = doc.RootElement;
128
            var weatherP = root.EnumerateObject();
129

    
130
            while (weatherP.MoveNext())
131
            {
132
                string name = weatherP.Current.Name;
133
                Console.WriteLine(name);
134

    
135
                switch (name)
136
                {
137
                    // current weather
138
                    case "current_condition":
139
                        {
140
                            ArrayEnumerator currentWeather = weatherP.Current.Value.EnumerateArray();
141
                            Current = ParseCurrentWeather(currentWeather);
142

    
143
                            break;
144
                        }
145
                    // weather prediction
146
                    case "weather":
147
                        {
148
                            ArrayEnumerator weather = weatherP.Current.Value.EnumerateArray();
149
                            ParseWeatherPredict(weather);
150

    
151
                            break;
152
                        }
153
                }
154
            }
155

    
156
            // sunrise + sunset into data
157
            EncompassSunRiseSetTimes();
158

    
159
            //TestConsoleOutput();
160
        }
161

    
162
        /// <summary>
163
        /// Test console output
164
        /// </summary>
165
        private void TestConsoleOutput()
166
        {
167
            Console.WriteLine(Current);
168
            foreach (WeatherInfo w in Predictions)
169
                Console.WriteLine(w);
170
        }
171

    
172
        /// <summary>
173
        /// Change data in a way that they now reflect sunrise and sunset times
174
        /// If current time under sunrise or over sunset -> WeatherConditions is Dark
175
        /// If prediction time is under sunrise or over sunset and more than half of the interval is under/over said time -> WeatherCondition is Dark
176
        /// </summary>
177
        private void EncompassSunRiseSetTimes()
178
        {
179
            if (sunsetTime.Count < 1 || sunriseTime.Count < 0)
180
                return;
181

    
182
            // change current weather
183
            if ((Current.startTime.TimeOfDay > sunsetTime[0].TimeOfDay) || (Current.startTime.TimeOfDay < sunriseTime[0].TimeOfDay))
184
                Current.condition = WeatherConditions.Dark;
185

    
186
            // change prediction
187
            int index = 0;
188
            for (int i = 0; i < Predictions.Count - 1; i++)
189
            {
190
                WeatherInfo w = Predictions[i];
191
                WeatherInfo wNext = Predictions[i + 1];
192
                DateTime sunrise = sunriseTime[index];
193
                DateTime sunset = sunsetTime[index];
194

    
195

    
196
                // if wNext time < than w time then it is prediction from the next day -> add 24 to correctly calculate timespan
197
                int timespan = wNext.startTime.Hour - w.startTime.Hour;
198
                bool intoTwo = false;
199
                if (wNext.startTime.Hour < w.startTime.Hour)
200
                {
201
                    intoTwo = true;
202
                    timespan = (wNext.startTime.Hour + 24) - w.startTime.Hour;
203
                }
204
                
205
                w.intervalLength = timespan;
206
                TimeSpan endTime = new TimeSpan(w.startTime.TimeOfDay.Hours + timespan, w.startTime.TimeOfDay.Minutes, 0);
207

    
208
                // if start under sunset
209
                if (w.startTime.TimeOfDay > sunsetTime[index].TimeOfDay)
210
                {
211
                    // when is end - if end after next sunrise need to compute the extent of dark
212
                    if (intoTwo && endTime > sunriseTime[index+1].TimeOfDay)
213
                    {
214
                        double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunriseTime[index + 1].Hour * 60 + sunriseTime[index + 1].Minute)) / 60.0;
215
                        howMuch = w.intervalLength - howMuch;
216

    
217
                        if (howMuch >= timespan / 2.0)
218
                            w.condition = WeatherConditions.Dark;
219
                    }
220
                    else
221
                        w.condition = WeatherConditions.Dark;
222
                }
223

    
224
                // if start under sunrise
225
                if (w.startTime.TimeOfDay < sunriseTime[index].TimeOfDay)
226
                {
227
                    double howMuch = ((sunriseTime[index].Hour * 60 + sunriseTime[index].Minute) - (w.startTime.Hour * 60 + w.startTime.Minute)) / 60.0;
228
                    if (endTime > sunset.TimeOfDay)
229
                        howMuch += ((w.startTime.Hour * 60 + w.startTime.Minute) - (sunset.Hour * 60 + sunset.Minute)) / 60.0;
230

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

    
235
                // if end over sunset
236
                else if (endTime > sunsetTime[index].TimeOfDay)
237
                {
238
                    double howMuch = ((endTime.Hours * 60 + endTime.Minutes) - (sunsetTime[index].Hour * 60 + sunsetTime[index].Minute)) / 60.0;
239

    
240
                    if (howMuch >= timespan / 2.0)
241
                        w.condition = WeatherConditions.Dark;
242
                }
243

    
244
                if (Predictions[i].startTime.Date !=  Predictions[i + 1].startTime.Date)
245
                    index++;
246
            }
247

    
248
            // last prediction
249
            WeatherInfo wLast = Predictions[Predictions.Count - 1];
250
            TimeSpan endTimeW = new TimeSpan(23, 59, 59);
251
            int timespanLast = 24 - wLast.startTime.Hour;
252
            wLast.intervalLength = timespanLast;
253

    
254
            // if start under sunset
255
            if (wLast.startTime.TimeOfDay > sunsetTime[sunsetTime.Count-1].TimeOfDay)
256
                wLast.condition = WeatherConditions.Dark;
257

    
258
            // if start under sunrise
259
            if (wLast.startTime.TimeOfDay < sunriseTime[sunriseTime.Count - 1].TimeOfDay)
260
            {
261
                double howMuch = ((sunriseTime[sunriseTime.Count - 1].Hour * 60 + sunriseTime[sunriseTime.Count - 1].Minute) - (wLast.startTime.Hour * 60 + wLast.startTime.Minute)) / 60.0;
262
                if (endTimeW > sunsetTime[sunriseTime.Count - 1].TimeOfDay)
263
                    howMuch += ((wLast.startTime.Hour * 60 + wLast.startTime.Minute) - (sunsetTime[sunriseTime.Count - 1].Hour * 60 + sunsetTime[sunriseTime.Count - 1].Minute)) / 60.0;
264

    
265
                if (howMuch >= timespanLast / 2.0)
266
                    wLast.condition = WeatherConditions.Dark;
267
            }
268

    
269
            // if start under sunrise
270
            if (endTimeW > sunsetTime[sunsetTime.Count - 1].TimeOfDay)
271
            {
272
                double howMuch = ((endTimeW.Hours * 60 + endTimeW.Minutes) - (sunsetTime[sunsetTime.Count - 1].Hour * 60 + sunsetTime[sunsetTime.Count - 1].Minute)) / 60.0;
273

    
274
                if (howMuch >= timespanLast / 2.0)
275
                    wLast.condition = WeatherConditions.Dark;
276
            }
277
        }
278

    
279
        /// <summary>
280
        /// Parse weather prediction
281
        /// </summary>
282
        /// <param name="weather"> ArrayEnumerator of weather predictions </param>
283
        private void ParseWeatherPredict(ArrayEnumerator weather)
284
        {
285
            while (weather.MoveNext())
286
            {
287
                // prediction for one day
288
                var obj = weather.Current.EnumerateObject();
289

    
290
                while (obj.MoveNext())
291
                {
292
                    switch (obj.Current.Name)
293
                    {
294
                        case "date":
295
                            {
296
                                DateTime.TryParseExact(obj.Current.Value.GetString(), "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out currParsedDay);
297
                                break;
298
                            }
299
                        // hourly predictions
300
                        case "hourly":
301
                            {
302
                                ParseHourly(obj.Current.Value.EnumerateArray());
303
                                break;
304
                            }
305
                        // sunset / sunrise hours
306
                        case "astronomy":
307
                            {
308
                                ParseAstronomy(obj.Current.Value.EnumerateArray());
309
                                break;
310
                            }
311
                    }
312
                }
313
            }
314
        }
315

    
316
        /// <summary>
317
        /// Parse sunrise and sunset times
318
        /// </summary>
319
        /// <param name="astronomy"> Astronomy array enumerator </param>
320
        private void ParseAstronomy(ArrayEnumerator astronomy)
321
        {
322
            while (astronomy.MoveNext())
323
            {
324
                var astrInfo = astronomy.Current.EnumerateObject();
325
                
326
                while (astrInfo.MoveNext())
327
                {
328
                    switch (astrInfo.Current.Name)
329
                    {
330
                        case "sunrise":
331
                            {
332
                                DateTime newSunrise = new DateTime();
333
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out newSunrise);
334
                                sunriseTime.Add(newSunrise);
335
                                //Console.WriteLine("\t sunrise time : " + sunriseTime + " " + astrInfo.Current.Value.GetString());
336
                                break;
337
                            }
338
                        case "sunset":
339
                            {
340
                                DateTime newSunset = new DateTime();
341
                                DateTime.TryParseExact(astrInfo.Current.Value.GetString(), "hh:mm tt", CultureInfo.InvariantCulture, DateTimeStyles.None, out newSunset);
342
                                sunsetTime.Add(newSunset);
343
                                //Console.WriteLine("\t sunset time : " + sunsetTime + " " + astrInfo.Current.Value.GetString());
344
                                break;
345
                            }
346
                    }
347
                }
348
            }
349
        }
350

    
351
        /// <summary>
352
        /// Parse hourly predictions
353
        /// </summary>
354
        /// <param name="hourly">Enumerated array of hourly predictions</param>
355
        private void ParseHourly(ArrayEnumerator hourly)
356
        {
357
            while (hourly.MoveNext())
358
            {
359
                // one hourly prediction
360
                var oneH = hourly.Current.EnumerateObject();
361
                WeatherInfo weather = new WeatherInfo();
362

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

    
415
                // Console.WriteLine(weather.ToString());
416
                Predictions.Add(weather);
417
            }
418

    
419
        }
420

    
421
        /// <summary>
422
        /// Parse current weather
423
        /// </summary>
424
        /// <param name="currentWeather">Enumerated hour of weather data</param>
425
        /// <returns>WeatherInfo with current weather</returns>
426
        private WeatherInfo ParseCurrentWeather(ArrayEnumerator currentWeather)
427
        {
428
            WeatherInfo res = new WeatherInfo();
429
            res.intervalLength = 0;
430
            //res.current = true;
431

    
432
            while (currentWeather.MoveNext())
433
            {
434
                var obj = currentWeather.Current.EnumerateObject();
435

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

    
474
            }
475

    
476
            res.lum = ValueToConditions.TransferConditionsToLux(res.condition);
477
            return res;
478
        }
479

    
480
      
481
    }
482
}
(2-2/2)