From c2fc0ec1aaba9676b860690817728639afbcbc08 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sun, 29 Oct 2023 14:56:02 +0000 Subject: [PATCH 01/45] build/version update --- CumulusMX/CumulusMX.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index c61cf21e..3e9a1bd1 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -77,7 +77,7 @@ CumulusMX.Program AnyCPU Copyright © 2015-2023 Cumulus MX - 3.27.0.3257 + 3.27.1.3258 From 85b382ce53326d37e98dacfcc4f1d58bc583c8c5 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sun, 29 Oct 2023 15:26:35 +0000 Subject: [PATCH 02/45] Make all third-party upload issues warnings --- CumulusMX/Cumulus.cs | 144 +++++++++++++++++++++++++++++++++---------- 1 file changed, 110 insertions(+), 34 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index a0edabdf..d8785fbc 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -2321,7 +2321,7 @@ internal async void UpdateWunderground(DateTime timestamp) Wund.ErrorFlagCount++; if (!Wund.RapidFireEnabled || Wund.ErrorFlagCount >= 12) { - LogMessage("Wunderground: Response = " + response.StatusCode + ": " + responseBodyAsText); + LogWarningMessage("Wunderground: Response = " + response.StatusCode + ": " + responseBodyAsText); ThirdPartyAlarm.LastMessage = "Wunderground: HTTP response - " + response.StatusCode; ThirdPartyAlarm.Triggered = true; Wund.ErrorFlagCount = 0; @@ -2382,7 +2382,7 @@ internal async void UpdateWindy(DateTime timestamp) } catch (Exception ex) { - LogErrorMessage("Windy: ERROR - " + ex.Message); + LogWarningMessage("Windy: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "Windy: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2428,7 +2428,7 @@ internal async void UpdateWindGuru(DateTime timestamp) } catch (Exception ex) { - LogErrorMessage("WindGuru: ERROR - " + ex.Message); + LogWarningMessage("WindGuru: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "WindGuru: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2487,7 +2487,7 @@ internal async void UpdateAwekas(DateTime timestamp) catch (Exception ex) { LogMessage("AWEKAS: Exception deserializing response = " + ex.Message); - LogErrorMessage($"AWEKAS: ERROR - Response body = {responseBodyAsText}"); + LogWarningMessage($"AWEKAS: ERROR - Response body = {responseBodyAsText}"); ThirdPartyAlarm.LastMessage = "AWEKAS deserializing response: " + ex.Message; ThirdPartyAlarm.Triggered = true; AWEKAS.Updating = false; @@ -2580,7 +2580,7 @@ internal async void UpdateAwekas(DateTime timestamp) } catch (Exception ex) { - LogErrorMessage("AWEKAS: Exception = " + ex.Message); + LogWarningMessage("AWEKAS: Exception = " + ex.Message); ThirdPartyAlarm.LastMessage = "AWEKAS: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2655,7 +2655,7 @@ internal async void UpdateWCloud(DateTime timestamp) } catch (Exception ex) { - LogErrorMessage("WeatherCloud: ERROR - " + ex.Message); + LogWarningMessage("WeatherCloud: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "WeatherCloud: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2705,7 +2705,7 @@ internal async void UpdateOpenWeatherMap(DateTime timestamp) } catch (Exception ex) { - LogErrorMessage("OpenWeatherMap: ERROR - " + ex.Message); + LogWarningMessage("OpenWeatherMap: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "OpenWeatherMap: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2736,7 +2736,7 @@ internal OpenWeatherMapStation[] GetOpenWeatherMapStations() } catch (Exception ex) { - LogErrorMessage("OpenWeatherMap: Get Stations ERROR - " + ex.Message); + LogWarningMessage("OpenWeatherMap: Get Stations ERROR - " + ex.Message); } return retVal; @@ -2786,7 +2786,7 @@ internal void CreateOpenWeatherMapStation() } catch (Exception ex) { - LogErrorMessage("OpenWeatherMap: Create station ERROR - " + ex.Message); + LogWarningMessage("OpenWeatherMap: Create station ERROR - " + ex.Message); } } @@ -2934,7 +2934,7 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs } catch (Exception ex) { - LogDebugMessage($"Realtime[{cycle}]: Error in realtime program - {RealtimeProgram}. Error: {ex.Message}"); + LogErrorMessage($"Realtime[{cycle}]: Error in realtime program - {RealtimeProgram}. Error: {ex.Message}"); } } } @@ -3034,11 +3034,11 @@ await Task.Run(() => catch (Exception ex) { reinit = true; - LogDebugMessage($"RealtimeReconnect: Error reconnecting ftp server - {ex.Message}"); + LogErrorMessage($"RealtimeReconnect: Error reconnecting ftp server - {ex.Message}"); if (ex.InnerException != null) { ex = Utils.GetOriginalException(ex); - LogDebugMessage($"RealtimeReconnect: Base exception - {ex.Message}"); + LogErrorMessage($"RealtimeReconnect: Base exception - {ex.Message}"); } } } @@ -3109,7 +3109,7 @@ await Task.Run(() => } catch (Exception ex) { - LogDebugMessage($"RealtimeReconnect: Realtime ftp connection test Failed - {ex.Message}"); + LogErrorMessage($"RealtimeReconnect: Realtime ftp connection test Failed - {ex.Message}"); if (ex.InnerException != null) { @@ -12827,11 +12827,27 @@ private async void WundCatchup() try { using (var response = await MyHttpClient.GetAsync(WundList[i])) - LogMessage("WU Response: " + response.StatusCode + ": " + response.ReasonPhrase); + { + if (response.StatusCode == HttpStatusCode.OK) + { + LogDebugMessage("WU Response: " + response.ReasonPhrase); + ThirdPartyAlarm.Triggered = false; + } + else + { + var msg = "WU Response: " + response.StatusCode + ": " + response.ReasonPhrase; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; + } + } } catch (Exception ex) { - LogErrorMessage("WU update: " + ex.Message); + var msg = "WU update: " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; } } @@ -12851,11 +12867,27 @@ private async void WindyCatchUp() try { using (var response = await MyHttpClient.GetAsync(WindyList[i])) - LogMessage("Windy Response: " + response.StatusCode + ": " + response.ReasonPhrase); + { + if (response.StatusCode == HttpStatusCode.OK) + { + LogDebugMessage("Windy Response: " + response.ReasonPhrase); + ThirdPartyAlarm.Triggered = false; + } + else + { + var msg = "Windy Response: " + response.StatusCode + ": " + response.ReasonPhrase; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; + } + } } catch (Exception ex) { - LogErrorMessage("Windy update: " + ex.Message); + var msg = "Windy update: " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; } } @@ -12880,12 +12912,26 @@ private async void PWSCatchUp() using (var response = await MyHttpClient.GetAsync(PWSList[i])) { var responseBodyAsText = await response.Content.ReadAsStringAsync(); - LogMessage("PWS Response: " + response.StatusCode + ": " + responseBodyAsText); + if (response.StatusCode == HttpStatusCode.OK) + { + LogDebugMessage("PWS Response: " + responseBodyAsText); + ThirdPartyAlarm.Triggered = false; + } + else + { + var msg = "PWS Response: " + response.StatusCode + ": " + responseBodyAsText; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; + } } } catch (Exception ex) { - LogErrorMessage("PWS update: " + ex.Message); + var msg = "PWS update: " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; } } @@ -12910,12 +12956,26 @@ private async void WOWCatchUp() using (var response = await MyHttpClient.GetAsync(WOWList[i])) { var responseBodyAsText = await response.Content.ReadAsStringAsync(); - LogMessage("WOW Response: " + response.StatusCode + ": " + responseBodyAsText); + if (response.StatusCode == HttpStatusCode.OK) + { + LogDebugMessage("WOW Response: " + responseBodyAsText); + ThirdPartyAlarm.Triggered = false; + } + else + { + var msg = "WOW Response: " + response.StatusCode + ": " + responseBodyAsText; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; + } } } catch (Exception ex) { - LogErrorMessage("WOW update: " + ex.Message); + var msg = "WOW update: " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; } } @@ -12948,14 +13008,26 @@ private async void OpenWeatherMapCatchUp() { var responseBodyAsText = await response.Content.ReadAsStringAsync(); var status = response.StatusCode == HttpStatusCode.NoContent ? "OK" : "Error"; // Returns a 204 response for OK! - LogDebugMessage($"OpenWeatherMap: Response code = {status} - {response.StatusCode}"); - if (response.StatusCode != HttpStatusCode.NoContent) - LogDataMessage($"OpenWeatherMap: Response data = {responseBodyAsText}"); + if (status == "OK") + { + LogDebugMessage($"OpenWeatherMap: Response code = {status} - {response.StatusCode}"); + ThirdPartyAlarm.Triggered = false; + } + else + { + var msg = $"OpenWeatherMap: Status = {response.StatusCode}, Response data = {responseBodyAsText}"; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; + } } } catch (Exception ex) { - LogErrorMessage("OpenWeatherMap: Update error = " + ex.Message); + var msg = "OpenWeatherMap: Update error = " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; + ThirdPartyAlarm.Triggered = true; } } @@ -12987,21 +13059,23 @@ public async void UpdatePWSweather(DateTime timestamp) var responseBodyAsText = await response.Content.ReadAsStringAsync(); if (response.StatusCode != HttpStatusCode.OK) { - LogWarningMessage($"PWS Response: ERROR - Response code = {response.StatusCode}, Body = {responseBodyAsText}"); - ThirdPartyAlarm.LastMessage = $"PWS: HTTP Response code = {response.StatusCode}, Body = {responseBodyAsText}"; + var msg = $"PWS Response: ERROR - Response code = {response.StatusCode}, Body = {responseBodyAsText}"; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; ThirdPartyAlarm.Triggered = true; } else { - LogDebugMessage("PWS Response: " + response.StatusCode + ": " + responseBodyAsText); + LogDebugMessage("PWS Response: " + responseBodyAsText); ThirdPartyAlarm.Triggered = false; } } } catch (Exception ex) { - LogErrorMessage("PWS update: " + ex.Message); - ThirdPartyAlarm.LastMessage = "PWS: " + ex.Message; + var msg = "PWS update: " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; ThirdPartyAlarm.Triggered = true; } finally @@ -13032,8 +13106,9 @@ public async void UpdateWOW(DateTime timestamp) var responseBodyAsText = await response.Content.ReadAsStringAsync(); if (response.StatusCode != HttpStatusCode.OK) { - LogMessage($"WOW Response: ERROR - Response code = {response.StatusCode}, body = {responseBodyAsText}"); - ThirdPartyAlarm.LastMessage = $"WOW: HTTP response - Response code = {response.StatusCode}, body = {responseBodyAsText}"; + var msg = $"WOW Response: ERROR - Response code = {response.StatusCode}, body = {responseBodyAsText}"; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; ThirdPartyAlarm.Triggered = true; } else @@ -13045,8 +13120,9 @@ public async void UpdateWOW(DateTime timestamp) } catch (Exception ex) { - LogErrorMessage("WOW update: " + ex.Message); - ThirdPartyAlarm.LastMessage = "WOW: " + ex.Message; + var msg = "WOW update: " + ex.Message; + LogWarningMessage(msg); + ThirdPartyAlarm.LastMessage = msg; ThirdPartyAlarm.Triggered = true; } finally From 9e28c65e824b77b6165b4b02d6fddf7d0e09cc24 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Mon, 30 Oct 2023 11:05:19 +0000 Subject: [PATCH 03/45] Fix for PHP uploads when no compression is supported --- CumulusMX/Cumulus.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index d8785fbc..96975b8a 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -31,6 +31,7 @@ using MySqlConnector; using Renci.SshNet; +using Renci.SshNet.Compression; using ServiceStack; using ServiceStack.Text; @@ -11625,6 +11626,17 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s request.Content = streamContent; } } + else + { + request.Headers.Add("Content_Type", "text/plain"); + + outStream = new MemoryStream(Encoding.UTF8.GetBytes(data)); + streamContent = new StreamContent(outStream); + streamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain"); + streamContent.Headers.ContentLength = outStream.Length; + + request.Content = streamContent; + } } LogDebugMessage($"PHP[{cycleStr}]: Sending via {request.Method}"); From fbf8872f26d2ae14350dcdf316491bcebee5b0bc Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Mon, 30 Oct 2023 11:16:15 +0000 Subject: [PATCH 04/45] Catch SQLite compression exception and delete it --- CumulusMX/WeatherStation.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 5375fe1b..65ef002f 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -481,9 +481,22 @@ private void CheckSqliteDatabase(bool giveup) else if (errorCount > 0) { cumulus.LogErrorMessage("SQLite integrity check Failed, trying to compact database"); - RecentDataDb.Execute("vacuum;"); - cumulus.LogErrorMessage("SQLite compact database complete, retesting integriry..."); - CheckSqliteDatabase(true); + try + { + RecentDataDb.Execute("vacuum;"); + cumulus.LogMessage("SQLite compact database complete, retesting integriry..."); + CheckSqliteDatabase(true); + } + catch (Exception ex) + { + cumulus.LogErrorMessage("SQLite compress failed - " + ex.Message); + cumulus.LogErrorMessage("Deleting RecentData database.."); + RecentDataDb.Close(); + File.Delete(cumulus.dbfile); + // Open database (create file if it doesn't exist) + SQLiteOpenFlags flags = SQLiteOpenFlags.Create | SQLiteOpenFlags.ReadWrite; + RecentDataDb = new SQLiteConnection(new SQLiteConnectionString(cumulus.dbfile, flags, false, null, null, null, null, "yyyy-MM-dd HH:mm:ss")); + } } } From 719a90147d12220a7c254c4cde7ea375074b1958 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Mon, 30 Oct 2023 16:13:28 +0000 Subject: [PATCH 05/45] Fix object not initialised error when station is null Chabge station = null status codes to 503 --- CumulusMX/Api.cs | 62 ++++++++++++++++++++++++------------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/CumulusMX/Api.cs b/CumulusMX/Api.cs index 7bd46568..5900203d 100644 --- a/CumulusMX/Api.cs +++ b/CumulusMX/Api.cs @@ -62,9 +62,9 @@ public async Task GetEditData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -137,9 +137,9 @@ public async Task PostEditData(string req) { if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -236,9 +236,9 @@ public async Task GetData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -313,9 +313,9 @@ public async Task PostTags(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -352,9 +352,9 @@ public async Task GetTags(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -389,9 +389,9 @@ public async Task GetGraphData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -563,9 +563,9 @@ public async Task SetGraphData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -602,9 +602,9 @@ public async Task GetDailyGraphData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -717,9 +717,9 @@ public async Task GetMonthlyRecordData(string mon, string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -772,9 +772,9 @@ public async Task GetThisMonthRecordData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -970,9 +970,9 @@ public async Task GetYesterdayData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -1024,9 +1024,9 @@ public async Task GetExtraData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) - await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"The station is not running\"}}"); - Response.StatusCode = 500; + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"503\",\"Description\":\"The station is not running\"}}"); return; } @@ -1413,7 +1413,7 @@ public async Task PostTags(string req) } else { - Response.StatusCode = 500; + Response.StatusCode = 503; await writer.WriteAsync("{\"Error\":\"HTTP Station (Ecowitt) is not running}\""); } break; @@ -1424,7 +1424,7 @@ public async Task PostTags(string req) } else { - Response.StatusCode = 500; + Response.StatusCode = 503; await writer.WriteAsync("{\"Error\":\"HTTP Station (Ecowitt) is not running}\""); } break; @@ -1460,7 +1460,7 @@ public async Task GetStation(string req) } else { - Response.StatusCode = 500; + Response.StatusCode = 503; await writer.WriteAsync("HTTP Station (Wunderground) is not running"); } break; @@ -1472,7 +1472,7 @@ public async Task GetStation(string req) } else { - Response.StatusCode = 500; + Response.StatusCode = 503; await writer.WriteAsync("HTTP Station (Ambient) is not running"); } break; @@ -1484,7 +1484,7 @@ public async Task GetStation(string req) } else { - Response.StatusCode = 500; + Response.StatusCode = 503; await writer.WriteAsync("HTTP Station (Ambient) is not running"); } break; @@ -1512,9 +1512,9 @@ public async Task GetUtilData(string req) if (Station == null) { + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) await writer.WriteAsync("The station is not running"); - Response.StatusCode = 500; return; } @@ -1568,7 +1568,7 @@ public async Task PostUtilsData(string req) { if (Station == null) { - Response.StatusCode = 500; + Response.StatusCode = 503; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) await writer.WriteAsync("The station is not running"); return; From b886361bd5a6df5b63b0a729929191937bf513bc Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Mon, 30 Oct 2023 16:14:24 +0000 Subject: [PATCH 06/45] Fix spelling errors --- CumulusMX/Alarm.cs | 4 ++-- CumulusMX/NOAAReports.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CumulusMX/Alarm.cs b/CumulusMX/Alarm.cs index 356bf3c0..3e9c2322 100644 --- a/CumulusMX/Alarm.cs +++ b/CumulusMX/Alarm.cs @@ -15,7 +15,7 @@ public virtual bool Enabled { enabled = value; - // if we are disabled, clear any exisitng alarms + // if we are disabled, clear any existing alarms if (!value) { triggered = false; @@ -178,7 +178,7 @@ public override bool Enabled { enabled = value; - // if we are disabled, clear any exisitng alarms + // if we are disabled, clear any existing alarms if (!value) { upTriggered = false; diff --git a/CumulusMX/NOAAReports.cs b/CumulusMX/NOAAReports.cs index 00910fdc..bc2f41c2 100644 --- a/CumulusMX/NOAAReports.cs +++ b/CumulusMX/NOAAReports.cs @@ -109,7 +109,7 @@ public string GenerateMissing() // increment the month // note this may reset the day checkDate = checkDate.AddMonths(1); - + if (checkDate.Year == lastRptDate.Year && checkDate.Month == lastRptDate.Month) { doMore = false; @@ -162,7 +162,7 @@ public string GenerateMissing() } else { - return "There are no missing reports to recreate. If you want to recreate some exisitng reports you must first delete them from your Reports folder"; + return "There are no missing reports to recreate. If you want to recreate some existing reports you must first delete them from your Reports folder"; } } From fd10d834212b2e08e61c1d3f6cf3871702eda571 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Tue, 31 Oct 2023 08:59:15 +0000 Subject: [PATCH 07/45] Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript --- CumulusMX/webtags.cs | 12 ++++++++++++ Updates.txt | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index ff42c1f8..855c2122 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -5140,6 +5140,16 @@ private string TagLatestError(Dictionary tagParams) return cumulus.LatestError; } + private string TagLatestErrorEnc(Dictionary tagParams) + { + return EncodeForWeb(cumulus.LatestError); + } + + private string TagLatestErrorJsEnc(Dictionary tagParams) + { + return EncodeForJs(cumulus.LatestError); + } + private string TagLatestErrorDate(Dictionary tagParams) { return cumulus.LatestErrorTS == DateTime.MinValue ? "------" : GetFormattedDateTime(cumulus.LatestErrorTS, "ddddd", tagParams); @@ -6387,6 +6397,8 @@ public void InitialiseWebtags() { "YearLowDailyTempRangeD", TagYearLowDailyTempRangeD }, // misc { "LatestError", TagLatestError }, + { "LatestErrorEnc", TagLatestErrorEnc }, + { "LatestErrorJsEnc", TagLatestErrorJsEnc }, { "LatestErrorDate", TagLatestErrorDate }, { "LatestErrorTime", TagLatestErrorTime }, { "ErrorLight", Tagerrorlight }, diff --git a/Updates.txt b/Updates.txt index 5e860d73..74f6dfa0 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,3 +1,18 @@ +3.27.1 - b3258 +—————————————— +New +- Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: #LatestErrorEnc, #LatestErrorJsEnc + +Changed +- All third-party upload issues are now classified as Warnings + +Fixed +- "Object is not intialized" error from the API when the station is not yet ready +- MX crash on start-up if the SQLite database has certain corruptions +- Fix for PHP uploads when no compression is supported by the server (or the network was down when MX started) + + + 3.27.0 - b3257 —————————————— New From 50eb574a93fc22ec8c5a38011f5c6d9a0453bc8f Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Tue, 31 Oct 2023 09:06:26 +0000 Subject: [PATCH 08/45] Latest error now set according to logging level --- CumulusMX/Cumulus.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 96975b8a..65409871 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -11714,7 +11714,7 @@ public void LogMessage(string message, LogLevel level=LogLevel.Info) ErrorList.Enqueue((DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss - ") + WebUtility.HtmlEncode(message))); } - if (level >= LogLevel.Error) + if (level >= ErrorListLoggingLevel) { LatestError = message; LatestErrorTS = DateTime.Now; From 0bbc29705791a1212b841af945392ea844372d36 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Tue, 31 Oct 2023 09:31:04 +0000 Subject: [PATCH 09/45] VP2 error message "No Ack in response to DMPAFT" on start-up --- CumulusMX/DavisStation.cs | 19 ++++++++++++++++--- Updates.txt | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index 36b36cf0..7d6b9a18 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -802,6 +802,18 @@ private void bw_DoWork(object sender, DoWorkEventArgs e) do { GetArchiveData(); + + // The VP" seems to need a nudge after a DMPAFT command + if (isSerial) + { + WakeVP(comport, true); + } + else + { + WakeVP(socket, true); + } + + archiveRun++; } while (archiveRun < maxArchiveRuns); @@ -2087,6 +2099,7 @@ private void GetArchiveData() { comport.DiscardInBuffer(); + if (!WakeVP(comport)) { cumulus.LogWarningMessage("GetArchiveData: Unable to wake VP"); @@ -2814,10 +2827,10 @@ private byte[] GetData(SerialPort serialPort, string commandString, int returnLe // 4. If the console has not woken up after 3 attempts, then signal a connection error // After the console has woken up, it will remain awake for 2 minutes. Every time the VP // receives another character, the 2 minute timer will be reset. - private bool WakeVP(SerialPort serialPort) + private bool WakeVP(SerialPort serialPort, bool force = false) { // Check if we haven't sent a command within the last two minutes - use 1:50 (110,000 ms) to be safe - if (awakeStopWatch.IsRunning && awakeStopWatch.ElapsedMilliseconds < 110000) + if (awakeStopWatch.IsRunning && awakeStopWatch.ElapsedMilliseconds < 110000 && !force) { cumulus.LogDebugMessage("WakeVP: Not required"); awakeStopWatch.Restart(); @@ -2894,7 +2907,7 @@ private bool WakeVP(SerialPort serialPort) } } - private bool WakeVP(TcpClient thePort) + private bool WakeVP(TcpClient thePort, bool force = false) { const int maxPasses = 3; int retryCount = 0; diff --git a/Updates.txt b/Updates.txt index 74f6dfa0..fc6142d0 100644 --- a/Updates.txt +++ b/Updates.txt @@ -10,7 +10,7 @@ Fixed - "Object is not intialized" error from the API when the station is not yet ready - MX crash on start-up if the SQLite database has certain corruptions - Fix for PHP uploads when no compression is supported by the server (or the network was down when MX started) - +- VP2 error message "No Ack in response to DMPAFT" on start-up 3.27.0 - b3257 From 6ad1877c727b0ca8e17c750837b875ff8a019e9f Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Tue, 31 Oct 2023 15:27:50 +0000 Subject: [PATCH 10/45] Added an Internet advanced option to disable the use of GET and fallback to using POST for all files Change VP2 Loop zero length messages to Warnings --- CumulusMX/Cumulus.cs | 14 ++++++++++---- CumulusMX/DavisStation.cs | 4 ++-- CumulusMX/InternetSettings.cs | 3 +++ Updates.txt | 3 +++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 65409871..721e1ff4 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -1061,6 +1061,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) if (FtpOptions.FtpMode == FtpProtocols.PHP) { LogMessage("Maximum concurrent PHP Uploads = " + FtpOptions.MaxConcurrentUploads); + LogMessage("PHP using GET = " + FtpOptions.PhpUseGet); } uploadCountLimitSemaphoreSlim = new SemaphoreSlim(FtpOptions.MaxConcurrentUploads); @@ -3268,6 +3269,9 @@ private async void RealtimeUpload(byte cycle) var idx = i; tasklist.Add(Task.Run(async () => { +#if DEBUG + LogDebugMessage($"Realtime[{cycle}]: Processing Real time file [{idx}] - {RealtimeFiles[idx].LocalFileName} to {RealtimeFiles[idx].RemoteFileName}"); +#endif // realtime file if (RealtimeFiles[idx].LocalFileName == "realtime.txt") { @@ -3288,7 +3292,7 @@ private async void RealtimeUpload(byte cycle) { uploadCountLimitSemaphoreSlim.Release(); #if DEBUG - LogDebugMessage($"Realtime[{cycle}]: Real time file {RealtimeFiles[idx].RemoteFileName} released semaphore [{uploadCountLimitSemaphoreSlim.CurrentCount}]"); + LogDebugMessage($"Realtime[{cycle}]: Real time file [{idx}] {RealtimeFiles[idx].RemoteFileName} released semaphore [{uploadCountLimitSemaphoreSlim.CurrentCount}]"); #endif } return true; @@ -3314,7 +3318,6 @@ private async void RealtimeUpload(byte cycle) .ToList() .ForEach(item => { - var uploadfile = item.local; var remotefile = item.remote; @@ -4836,6 +4839,7 @@ private void ReadIniFile() FtpOptions.PhpSecret = Guid.NewGuid().ToString(); FtpOptions.PhpIgnoreCertErrors = ini.GetValue("FTP site", "PHP-IgnoreCertErrors", false); FtpOptions.MaxConcurrentUploads = ini.GetValue("FTP site", "MaxConcurrentUploads", boolWindows ? 4 : 1); + FtpOptions.PhpUseGet = ini.GetValue("FTP site", "PHP-UseGet", true); MoonImage.Ftp = ini.GetValue("FTP site", "IncludeMoonImage", false); MoonImage.Copy = ini.GetValue("FTP site", "CopyMoonImage", false); @@ -6369,6 +6373,7 @@ internal void WriteIniFile() ini.SetValue("FTP site", "PHP-URL", FtpOptions.PhpUrl); ini.SetValue("FTP site", "PHP-Secret", FtpOptions.PhpSecret); ini.SetValue("FTP site", "PHP-IgnoreCertErrors", FtpOptions.PhpIgnoreCertErrors); + ini.SetValue("FTP site", "PHP-UseGet", FtpOptions.PhpUseGet); ini.SetValue("FTP site", "MaxConcurrentUploads", FtpOptions.MaxConcurrentUploads); @@ -11574,7 +11579,7 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s } // if content < 7 KB-ish - if (len < 7000) + if (len < 7000 && FtpOptions.PhpUseGet) { if (!binary) @@ -11585,7 +11590,7 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s request.Method = HttpMethod.Get; request.Headers.Add("DATA", data); } - // else > 7 kB + // else > 7 kB or GET is disabled else { // send as POST @@ -14033,6 +14038,7 @@ public class FtpOptionsClass public bool PhpIgnoreCertErrors { get; set; } public string PhpCompression { get; set; } = "none"; public int MaxConcurrentUploads { get; set; } + public bool PhpUseGet { get; set; } } public class FileGenerationOptions diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index 7d6b9a18..7b6ca644 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -1358,7 +1358,7 @@ private void GetAndProcessLoopData(int number) tmrComm.Stop(); if (comport.BytesToRead < loopDataLength) { - cumulus.LogErrorMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {comport.BytesToRead}"); + cumulus.LogWarningMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {comport.BytesToRead}"); } comport.Read(loopString, 0, loopDataLength); @@ -1427,7 +1427,7 @@ private void GetAndProcessLoopData(int number) if (socket.Available < loopDataLength) { - cumulus.LogErrorMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {socket.Available}"); + cumulus.LogWarningMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {socket.Available}"); } // Read the first 99 bytes of the buffer into the array diff --git a/CumulusMX/InternetSettings.cs b/CumulusMX/InternetSettings.cs index f4306495..467185c9 100644 --- a/CumulusMX/InternetSettings.cs +++ b/CumulusMX/InternetSettings.cs @@ -107,6 +107,7 @@ public string UpdateConfig(IHttpContext context) cumulus.FtpOptions.PhpUrl = settings.website.phpurl; cumulus.FtpOptions.PhpSecret = settings.website.phpsecret; cumulus.FtpOptions.PhpIgnoreCertErrors = settings.website.advanced.phpignorecerts; + cumulus.FtpOptions.PhpUseGet = settings.website.advanced.phpuseget; cumulus.FtpOptions.MaxConcurrentUploads = settings.website.advanced.maxuploads; } } @@ -392,6 +393,7 @@ public string GetAlpacaFormData() disableftpsexplicit = cumulus.FtpOptions.DisableExplicit, ignorecerts = cumulus.FtpOptions.IgnoreCertErrors, phpignorecerts = cumulus.FtpOptions.PhpIgnoreCertErrors, + phpuseget = cumulus.FtpOptions.PhpUseGet, maxuploads = cumulus.FtpOptions.MaxConcurrentUploads }; @@ -710,6 +712,7 @@ public class JsonInternetSettingsWebsiteAdvanced public bool ignorecerts { get; set; } public bool phpignorecerts { get; set; } public int maxuploads { get; set; } + public bool phpuseget { get; set; } } public class JsonInternetSettingsWebsite diff --git a/Updates.txt b/Updates.txt index fc6142d0..52f91133 100644 --- a/Updates.txt +++ b/Updates.txt @@ -11,6 +11,9 @@ Fixed - MX crash on start-up if the SQLite database has certain corruptions - Fix for PHP uploads when no compression is supported by the server (or the network was down when MX started) - VP2 error message "No Ack in response to DMPAFT" on start-up +- Ecowitt Cloud API settings are no longer mandatory for Ecowitt station (bug introduced in 3.27.0) +- Fix for error 500 in upload.php when debug is enabled (only occurred on some servers) +- PHP uploads using HTTP GET were uploading to the same destination file on one server. Added an Internet advanced option to disable the use of GET and fallback to using POST for all files 3.27.0 - b3257 From 86e8395947e7bc6fa331bb6215d7ebffd1c92809 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 1 Nov 2023 09:00:16 +0000 Subject: [PATCH 11/45] Ecowitt Extra Cloud sensor station type not starting as expected --- CumulusMX/Cumulus.cs | 1 + CumulusMX/CumulusMX.csproj | 2 +- Updates.txt | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 721e1ff4..ef7bd8f1 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -12171,6 +12171,7 @@ public void StartTimersAndSensors() airLinkIn?.Start(); ecowittExtra?.Start(); ambientExtra?.Start(); + ecowittCloudExtra?.Start(); LogMessage("Start Timers"); // start the general one-minute timer diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index 3e9a1bd1..d0f5cb67 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -77,7 +77,7 @@ CumulusMX.Program AnyCPU Copyright © 2015-2023 Cumulus MX - 3.27.1.3258 + 3.27.1.3259 diff --git a/Updates.txt b/Updates.txt index 52f91133..c8073253 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,4 @@ -3.27.1 - b3258 +3.27.1 - b3259 —————————————— New - Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: #LatestErrorEnc, #LatestErrorJsEnc @@ -14,6 +14,7 @@ Fixed - Ecowitt Cloud API settings are no longer mandatory for Ecowitt station (bug introduced in 3.27.0) - Fix for error 500 in upload.php when debug is enabled (only occurred on some servers) - PHP uploads using HTTP GET were uploading to the same destination file on one server. Added an Internet advanced option to disable the use of GET and fallback to using POST for all files +- Ecowitt Extra Cloud sensor station type not starting as expected 3.27.0 - b3257 From 86a3b612a508aa4707e6127bb4d2e2aa6a32fe9c Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 1 Nov 2023 09:21:37 +0000 Subject: [PATCH 12/45] Missing save of new record alarm msg to string.ini --- CumulusMX/Cumulus.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index ef7bd8f1..d477cb1c 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -7452,10 +7452,11 @@ public void WriteStringsFile() ini.SetValue("AlarmEmails", "batteryLow", BatteryLowAlarm.EmailMsg); ini.SetValue("AlarmEmails", "dataSpike", SpikeAlarm.EmailMsg); ini.SetValue("AlarmEmails", "upgrade", UpgradeAlarm.EmailMsg); - ini.SetValue("AlarmEmails", "ftpStopped", FtpAlarm.EmailMsg); ini.SetValue("AlarmEmails", "httpStopped", ThirdPartyAlarm.EmailMsg); ini.SetValue("AlarmEmails", "mySqlStopped", MySqlUploadAlarm.EmailMsg); ini.SetValue("AlarmEmails", "isRaining", IsRainingAlarm.EmailMsg); + ini.SetValue("AlarmEmails", "newRecord", NewRecordAlarm.EmailMsg); + ini.SetValue("AlarmEmails", "ftpStopped", FtpAlarm.EmailMsg); ini.Flush(); From 9109b65cc74edf4b115fe56dcd415bb2b6b275a5 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 1 Nov 2023 10:04:40 +0000 Subject: [PATCH 13/45] Fix lots of instances of Alarms being trigger before the last message was set --- CumulusMX/Cumulus.cs | 92 ++++++++++++++++---------------- CumulusMX/EcowittCloudStation.cs | 10 ++-- CumulusMX/ExtraSensorSettings.cs | 2 +- CumulusMX/WeatherStation.cs | 2 +- 4 files changed, 54 insertions(+), 52 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index d477cb1c..2165e6c9 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -2961,8 +2961,8 @@ private async void RealtimeFTPReconnect() return; RealtimeFtpReconnecting = true; - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "Realtime re-connecting"; + FtpAlarm.Triggered = true; await Task.Run(() => { bool connected; @@ -10003,8 +10003,8 @@ public async Task DoIntervalUpload() { LogFtpMessage($"SFTP[Int]: Error connecting SFTP - {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "Error connecting SFTP - " + ex.Message; + FtpAlarm.Triggered = true; if ((uint) ex.HResult == 0x80004005) // Could not resolve host { @@ -10038,8 +10038,8 @@ public async Task DoIntervalUpload() catch (Exception e) { LogFtpMessage($"SFTP[Int]: Error uploading file - {e.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "Error uploading NOAA report file - " + e.Message; + FtpAlarm.Triggered = true; } NOAAconf.NeedFtp = false; } @@ -10087,15 +10087,15 @@ public async Task DoIntervalUpload() { LogFtpMessage($"SFTP[Int]: Error uploading Extra web file #{i} [{uploadfile}]"); LogFtpMessage($"SFTP[Int]: Error = {e.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading Extra web file #{i} [{uploadfile}"; + FtpAlarm.Triggered = true; } } else { LogFtpMessage($"SFTP[Int]: Extra web file #{i} [{uploadfile}] not found!"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error Extra web file #{i} [{uploadfile} not found"; + FtpAlarm.Triggered = true; } } } @@ -10135,8 +10135,8 @@ public async Task DoIntervalUpload() { LogFtpMessage($"SFTP[Int]: Error uploading standard data file [{StdWebFiles[i].RemoteFileName}]"); LogFtpMessage($"SFTP[Int]: Error = {e}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading standard web file {StdWebFiles[i].RemoteFileName} - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10169,8 +10169,8 @@ public async Task DoIntervalUpload() { LogFtpMessage($"SFTP[Int]: Error uploading graph data file [{uploadfile}]"); LogFtpMessage($"SFTP[Int]: Error = {e}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading graph data file [{uploadfile}] - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10198,8 +10198,8 @@ public async Task DoIntervalUpload() { LogFtpMessage($"SFTP[Int]: Error uploading daily graph data file [{uploadfile}]"); LogFtpMessage($"SFTP[Int]: Error = {e}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading daily graph data file [{uploadfile}] - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10217,8 +10217,8 @@ public async Task DoIntervalUpload() catch (Exception e) { LogErrorMessage($"SFTP[Int]: Error uploading moon image - {e.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading moon image - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10297,8 +10297,8 @@ public async Task DoIntervalUpload() { LogFtpMessage("FTP[Int]: Error connecting ftp - " + ex.Message); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "Error connecting ftp - " + ex.Message; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -10338,8 +10338,8 @@ public async Task DoIntervalUpload() catch (Exception e) { LogFtpMessage($"FTP[Int]: Error uploading NOAA files: {e.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "Error connecting ftp - " + e.Message; + FtpAlarm.Triggered = true; } NOAAconf.NeedFtp = false; } @@ -10388,8 +10388,8 @@ public async Task DoIntervalUpload() catch (Exception e) { LogFtpMessage($"FTP[Int]: Error uploading file {uploadfile}: {e.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading extra file {uploadfile} - {e.Message}"; + FtpAlarm.Triggered = true; } } else @@ -10435,8 +10435,8 @@ public async Task DoIntervalUpload() catch (Exception e) { LogFtpMessage($"FTP[Int]: Error uploading file {StdWebFiles[i].RemoteFileName}: {e}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading file {StdWebFiles[i].RemoteFileName} - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10468,8 +10468,8 @@ public async Task DoIntervalUpload() { LogFtpMessage($"FTP[Int]: Error uploading graph data file [{GraphDataFiles[i].RemoteFileName}]"); LogFtpMessage($"FTP[Int]: Error = {e}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading file {GraphDataFiles[i].RemoteFileName} - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10498,8 +10498,8 @@ public async Task DoIntervalUpload() { LogFtpMessage($"FTP[Int]: Error uploading daily graph data file [{GraphDataEodFiles[i].RemoteFileName}]"); LogFtpMessage($"FTP[Int]: Error = {e}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading file {GraphDataEodFiles[i].RemoteFileName} - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10517,8 +10517,8 @@ public async Task DoIntervalUpload() catch (Exception e) { LogErrorMessage($"FTP[Int]: Error uploading moon image - {e.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading moon image - {e.Message}"; + FtpAlarm.Triggered = true; } } } @@ -10576,8 +10576,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, $"PHP[Int]: Error uploading NOAA files"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading NOAA files - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -10635,8 +10635,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, $"PHP[Int]: Error uploading NOAA Year file"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading NOAA files - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -10725,8 +10725,8 @@ public async Task DoIntervalUpload() catch (Exception ex) when (!(ex is TaskCanceledException)) { LogExceptionMessage(ex, $"PHP[Int]: Error uploading file {uploadfile} to: {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading file {uploadfile} to: {remotefile} - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -10815,8 +10815,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, $"PHP[Int]: Error uploading file {item.RemoteFileName}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading file {item.RemoteFileName} - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -10907,8 +10907,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, $"PHP[Int]: Error uploading graph data file [{item.RemoteFileName}]"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading graph data file [{item.RemoteFileName}] - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -10985,8 +10985,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, $"PHP[Int]: Error uploading daily graph data file [{item.RemoteFileName}]"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading daily graph data file [{item.RemoteFileName}] - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -11051,8 +11051,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, "PHP[Int]: Error uploading moon image"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading moon image - {ex.Message}"; + FtpAlarm.Triggered = true; } finally { @@ -11089,8 +11089,8 @@ public async Task DoIntervalUpload() catch (Exception ex) { LogExceptionMessage(ex, "PHP[Int]: Error waiting on upload tasks"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "Error waiting on upload tasks"; + FtpAlarm.Triggered = true; } } //LogDebugMessage($"PHP[Int]: EOD Graph files upload complete, {tasklist.Count()} files processed"); @@ -11123,8 +11123,8 @@ private bool UploadFile(FtpClient conn, string localfile, string remotefile, int if (!File.Exists(localfile)) { LogWarningMessage($"FTP[{cycleStr}]: Error! Local file not found, aborting upload: {localfile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error! Local file not found, aborting upload: {localfile}"; + FtpAlarm.Triggered = true; return true; } @@ -11136,8 +11136,8 @@ private bool UploadFile(FtpClient conn, string localfile, string remotefile, int catch (Exception ex) { LogFtpMessage($"FTP[{cycleStr}]: Error reading {localfile} - {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error reading {localfile} - {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11158,8 +11158,8 @@ private bool UploadStream(FtpClient conn, string remotefile, Stream dataStream, if (dataStream.Length == 0) { LogWarningMessage($"FTP[{cycleStr}]: The data is empty - skipping upload of {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The data is empty - skipping upload of {remotefile}"; + FtpAlarm.Triggered = true; return true; } @@ -11199,8 +11199,8 @@ private bool UploadStream(FtpClient conn, string remotefile, Stream dataStream, { LogFtpMessage($"FTP[{cycleStr}]: Error deleting {remotefile} : {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error deleting {remotefile} : {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11234,8 +11234,8 @@ private bool UploadStream(FtpClient conn, string remotefile, Stream dataStream, { LogFtpMessage($"FTP[{cycleStr}]: Error renaming {remotefiletmp} to {remotefile} : {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error renaming {remotefiletmp} to {remotefile} : {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11251,8 +11251,8 @@ private bool UploadStream(FtpClient conn, string remotefile, Stream dataStream, { LogFtpMessage($"FTP[{cycleStr}]: Error uploading {remotefile} : {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading {remotefile} : {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11282,8 +11282,8 @@ private bool UploadFile(SftpClient conn, string localfile, string remotefile, in if (!File.Exists(localfile)) { LogWarningMessage($"SFTP[{cycleStr}]: Error! Local file not found, aborting upload: {localfile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error! Local file not found, aborting upload: {localfile}"; + FtpAlarm.Triggered = true; return true; } @@ -11303,8 +11303,8 @@ private bool UploadFile(SftpClient conn, string localfile, string remotefile, in catch (ObjectDisposedException) { LogFtpMessage($"SFTP[{cycleStr}]: The SFTP object is disposed - skipping upload of {localfile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The SFTP object is disposed - skipping upload of {localfile}"; + FtpAlarm.Triggered = true; if (cycle >= 0) RealtimeFTPReconnect(); @@ -11323,8 +11323,8 @@ private bool UploadFile(SftpClient conn, string localfile, string remotefile, in { LogFtpMessage($"SFTP[{cycleStr}]: Error reading {localfile} - {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error reading {localfile} - {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11344,8 +11344,8 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, if (dataStream.Length == 0) { LogFtpMessage($"SFTP[{cycleStr}]: The data is empty - skipping upload of {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The data is empty - skipping upload of {remotefile}"; + FtpAlarm.Triggered = true; return false; } @@ -11354,8 +11354,8 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, if (conn == null || !conn.IsConnected) { LogFtpMessage($"SFTP[{cycleStr}]: The SFTP object is null or not connected - skipping upload of {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The SFTP object is null or not connected - skipping upload of {remotefile}"; + FtpAlarm.Triggered = true; if (cycle >= 0) RealtimeFTPReconnect(); @@ -11367,8 +11367,8 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, { LogFtpMessage($"SFTP[{cycleStr}]: The SFTP object is disposed - skipping upload of {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The SFTP object is disposed - skipping upload of {remotefile}"; + FtpAlarm.Triggered = true; if (cycle >= 0) RealtimeFTPReconnect(); @@ -11392,16 +11392,16 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, catch (ObjectDisposedException) { LogFtpMessage($"SFTP[{cycleStr}]: The SFTP object is disposed"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The SFTP object is disposed - skipping upload of {remotefile}"; + FtpAlarm.Triggered = true; return false; } catch (Exception ex) { LogFtpMessage($"SFTP[{cycleStr}]: Error uploading {remotefilename} : {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading {remotefilename} : {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.Message.Contains("Permission denied")) // Non-fatal return true; @@ -11429,16 +11429,16 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, catch (ObjectDisposedException) { LogFtpMessage($"SFTP[{cycleStr}]: The SFTP object is disposed"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The SFTP object is disposed during renaming of {remotefile}"; + FtpAlarm.Triggered = true; return false; } catch (Exception ex) { LogFtpMessage($"SFTP[{cycleStr}]: Error renaming {remotefilename} to {remotefile} : {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error renaming {remotefilename} to {remotefile} : {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11454,16 +11454,16 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, catch (ObjectDisposedException) { LogFtpMessage($"SFTP[{cycleStr}]: The SFTP object is disposed"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = "The SFTP object is disposed"; + FtpAlarm.Triggered = true; return false; } catch (Exception ex) { LogFtpMessage($"SFTP[{cycleStr}]: Error uploading {remotefile} - {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading {remotefile} - {ex.Message}"; + FtpAlarm.Triggered = true; if (ex.InnerException != null) { @@ -11489,8 +11489,8 @@ private async Task UploadFile(HttpClient httpclient, string localfile, str { LogWarningMessage($"PHP[{cycleStr}]: Error! Local file not found, aborting upload: {localfile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error! Local file not found, aborting upload: {localfile}"; + FtpAlarm.Triggered = true; return false; } @@ -11514,8 +11514,8 @@ private async Task UploadFile(HttpClient httpclient, string localfile, str { LogDebugMessage($"PHP[{cycleStr}]: Error - {ex.Message}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $" Error - {ex.Message}"; + FtpAlarm.Triggered = true; return false; } @@ -11678,8 +11678,8 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s { LogExceptionMessage(ex, $"PHP[{cycleStr}]: Error uploading to {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $" Error uploading to {remotefile} - {ex.Message}"; + FtpAlarm.Triggered = true; retry++; if (retry < 2) @@ -11690,8 +11690,8 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s catch (Exception ex) { LogExceptionMessage(ex, $"PHP[{cycleStr}]: Error uploading to {remotefile}"); - FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $" Error uploading to {remotefile} - {ex.Message}"; + FtpAlarm.Triggered = true; retry = 99; } finally diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs index 2804dbae..595b38d5 100644 --- a/CumulusMX/EcowittCloudStation.cs +++ b/CumulusMX/EcowittCloudStation.cs @@ -95,6 +95,8 @@ public EcowittCloudStation(Cumulus cumulus, WeatherStation station = null) : bas cumulus.Units.LeafWetnessUnitText = "%"; } + ecowittApi = new EcowittApi(cumulus, this); + // Only perform the Start-up if we are a proper station, not a Extra Sensor if (main) { @@ -114,7 +116,10 @@ public override void Start() cumulus.LogMessage("Starting Ecowitt Cloud station"); - cumulus.StartTimersAndSensors(); + if (station == null) + { + cumulus.StartTimersAndSensors(); + } liveTask = Task.Run(() => { @@ -170,9 +175,6 @@ public override void getAndProcessHistoryData() try { - - ecowittApi = new EcowittApi(cumulus, this); - do { GetHistoricData(); diff --git a/CumulusMX/ExtraSensorSettings.cs b/CumulusMX/ExtraSensorSettings.cs index 73042538..49f69bd4 100644 --- a/CumulusMX/ExtraSensorSettings.cs +++ b/CumulusMX/ExtraSensorSettings.cs @@ -371,7 +371,7 @@ public string UpdateConfig(IHttpContext context) cumulus.EcowittMapWN34[8] = settings.httpSensors.ecowitt.mappings.wn34chan8; } - cumulus.EcowittExtraUseMainForwarders = settings.httpSensors.ecowitt.forwarders.usemain; + cumulus.EcowittExtraUseMainForwarders = settings.httpSensors.ecowitt.forwarders == null ? true : settings.httpSensors.ecowitt.forwarders.usemain; if (!cumulus.EcowittExtraUseMainForwarders) { diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 65ef002f..10bca957 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -6578,8 +6578,8 @@ public void SetAlltime(AllTimeRec rec, double value, DateTime timestamp) File.AppendAllText(cumulus.Alltimelogfile, sb.ToString()); } - cumulus.NewRecordAlarm.Triggered = true; cumulus.NewRecordAlarm.LastMessage = rec.Desc + " = " + string.Format("{0,7:0.000}", value); + cumulus.NewRecordAlarm.Triggered = true; } public void SetMonthlyAlltime(AllTimeRec rec, double value, DateTime timestamp) From 509f6e3315f0b151f44a848587711673ae70f110 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 1 Nov 2023 10:45:37 +0000 Subject: [PATCH 14/45] Logging changes --- CumulusMX/Cumulus.cs | 3 ++- CumulusMX/WeatherStation.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 2165e6c9..37fff2b2 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -31,7 +31,6 @@ using MySqlConnector; using Renci.SshNet; -using Renci.SshNet.Compression; using ServiceStack; using ServiceStack.Text; @@ -1587,6 +1586,8 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) break; } + LogMessage($"Wind settings: Calc avg speed = {StationOptions.CalcuateAverageWindSpeed}, Use speed for avg = {StationOptions.UseSpeedForLatest}"); + if (station != null) { Api.Station = station; diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 10bca957..6dcba42a 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -6165,7 +6165,7 @@ private string TimeToStrHHMM(DateTime timestamp) // Use -1 for the average if you want to feedback the current average for a calculated moving average public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime timestamp) { - cumulus.LogDebugMessage($"DoWind: gust={gustpar:F1}, speed={speedpar:F1} - Current: gust={RecentMaxGust:F1}, speed={WindAverage:F1}"); + cumulus.LogDebugMessage($"DoWind: latest={gustpar:F1}, speed={speedpar:F1} - Current: gust={RecentMaxGust:F1}, speed={WindAverage:F1}"); // Spike removal is in m/s var windGustMS = ConvertUserWindToMS(gustpar); From aa4b845eb73549fc79e62a40f0a30d3aeeb170e1 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 1 Nov 2023 11:42:14 +0000 Subject: [PATCH 15/45] Supress some FO logging for non-FO stations --- CumulusMX/Cumulus.cs | 2 +- CumulusMX/WeatherStation.cs | 2 +- Updates.txt | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 37fff2b2..b8f712d7 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -1586,7 +1586,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) break; } - LogMessage($"Wind settings: Calc avg speed = {StationOptions.CalcuateAverageWindSpeed}, Use speed for avg = {StationOptions.UseSpeedForLatest}"); + LogMessage($"Wind settings: Calc avg speed={StationOptions.CalcuateAverageWindSpeed}, Use speed for avg={StationOptions.UseSpeedForLatest}, Gust time={StationOptions.PeakGustMinutes}, Avg time={StationOptions.AvgSpeedMinutes}"); if (station != null) { diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 6dcba42a..1db737b2 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -716,7 +716,7 @@ public void ReadTodayFile() FOSensorClockTime = ini.GetValue("FineOffset", "FOSensorClockTime", DateTime.MinValue); FOStationClockTime = ini.GetValue("FineOffset", "FOStationClockTime", DateTime.MinValue); FOSolarClockTime = ini.GetValue("FineOffset", "FOSolarClockTime", DateTime.MinValue); - if (cumulus.FineOffsetOptions.SyncReads) + if (cumulus.FineOffsetOptions.SyncReads && (cumulus.StationType == StationTypes.FineOffset || cumulus.StationType == StationTypes.FineOffsetSolar)) { cumulus.LogMessage("Sensor clock " + FOSensorClockTime.ToLongTimeString()); cumulus.LogMessage("Station clock " + FOStationClockTime.ToLongTimeString()); diff --git a/Updates.txt b/Updates.txt index c8073253..cf502b40 100644 --- a/Updates.txt +++ b/Updates.txt @@ -15,6 +15,7 @@ Fixed - Fix for error 500 in upload.php when debug is enabled (only occurred on some servers) - PHP uploads using HTTP GET were uploading to the same destination file on one server. Added an Internet advanced option to disable the use of GET and fallback to using POST for all files - Ecowitt Extra Cloud sensor station type not starting as expected +- Some new record alarms not setting the email message correctly 3.27.0 - b3257 From a6f61287ede307c730a8bd3e4aa5f5236d4df9e7 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 1 Nov 2023 15:56:22 +0000 Subject: [PATCH 16/45] Ecowitt Extra Cloud sensor station can now pick up the latest web cam URL --- CumulusMX/Cumulus.cs | 3 +++ CumulusMX/ExtraSensorSettings.cs | 45 +++++++++++++++++++++++++++++--- CumulusMX/StationSettings.cs | 8 +----- Updates.txt | 2 ++ 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index b8f712d7..69b25482 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -4697,6 +4697,7 @@ private void ReadIniFile() EcowittExtraUseCo2 = ini.GetValue("GW1000", "ExtraSensorUseCo2", true); EcowittExtraUseLightning = ini.GetValue("GW1000", "ExtraSensorUseLightning", true); EcowittExtraUseLeak = ini.GetValue("GW1000", "ExtraSensorUseLeak", true); + EcowittExtraUseCamera = ini.GetValue("GW1000", "ExtraSensorUseCamera", true); EcowittSetCustomServer = ini.GetValue("GW1000", "SetCustomServer", false); EcowittGatewayAddr = ini.GetValue("GW1000", "EcowittGwAddr", "0.0.0.0"); var localIp = Utils.GetIpWithDefaultGateway(); @@ -6192,6 +6193,7 @@ internal void WriteIniFile() ini.SetValue("GW1000", "ExtraSensorUseCo2", EcowittExtraUseCo2); ini.SetValue("GW1000", "ExtraSensorUseLightning", EcowittExtraUseLightning); ini.SetValue("GW1000", "ExtraSensorUseLeak", EcowittExtraUseLeak); + ini.SetValue("GW1000", "ExtraSensorUseCamera", EcowittExtraUseCamera); ini.SetValue("GW1000", "SetCustomServer", EcowittSetCustomServer); ini.SetValue("GW1000", "EcowittGwAddr", EcowittGatewayAddr); ini.SetValue("GW1000", "EcowittLocalAddr", EcowittLocalAddr); @@ -7539,6 +7541,7 @@ public void WriteStringsFile() public bool EcowittExtraUseCo2 { get; set; } public bool EcowittExtraUseLightning { get; set; } public bool EcowittExtraUseLeak { get; set; } + public bool EcowittExtraUseCamera { get; set; } public string EcowittApplicationKey { get; set; } public string EcowittUserApiKey { get; set; } public string EcowittMacAddress { get; set; } diff --git a/CumulusMX/ExtraSensorSettings.cs b/CumulusMX/ExtraSensorSettings.cs index 49f69bd4..84cdb2b2 100644 --- a/CumulusMX/ExtraSensorSettings.cs +++ b/CumulusMX/ExtraSensorSettings.cs @@ -79,6 +79,7 @@ public string GetAlpacaFormData() useCo2 = cumulus.EcowittExtraUseCo2, useLightning = cumulus.EcowittExtraUseLightning, useLeak = cumulus.EcowittExtraUseLeak, + useCamera = cumulus.EcowittExtraUseCamera, setcustom = cumulus.EcowittExtraSetCustomServer, gwaddr = cumulus.EcowittExtraGatewayAddr, @@ -102,6 +103,13 @@ public string GetAlpacaFormData() } } + var ecowittapi = new JsonStationSettingsEcowittApi() + { + applicationkey = cumulus.EcowittApplicationKey, + userkey = cumulus.EcowittUserApiKey, + mac = cumulus.EcowittMacAddress + }; + var ambient = new JsonExtraSensorAmbient() { useSolar = cumulus.AmbientExtraUseSolar, @@ -119,6 +127,7 @@ public string GetAlpacaFormData() var httpStation = new JsonExtraSensorHttp() { ecowitt = ecowitt, + ecowittapi = ecowittapi, ambient = ambient }; @@ -283,11 +292,15 @@ public string UpdateConfig(IHttpContext context) cumulus.EcowittExtraUseCo2 = settings.httpSensors.ecowitt.useCo2; cumulus.EcowittExtraUseLightning = settings.httpSensors.ecowitt.useLightning; cumulus.EcowittExtraUseLeak = settings.httpSensors.ecowitt.useLeak; + cumulus.EcowittExtraUseCamera = settings.httpSensors.ecowitt.useCamera; cumulus.EcowittExtraSetCustomServer = settings.httpSensors.ecowitt.setcustom; - cumulus.EcowittExtraGatewayAddr = settings.httpSensors.ecowitt.gwaddr; - cumulus.EcowittExtraLocalAddr = settings.httpSensors.ecowitt.localaddr; - cumulus.EcowittExtraCustomInterval = settings.httpSensors.ecowitt.interval; + if (cumulus.EcowittExtraSetCustomServer) + { + cumulus.EcowittExtraGatewayAddr = settings.httpSensors.ecowitt.gwaddr; + cumulus.EcowittExtraLocalAddr = settings.httpSensors.ecowitt.localaddr; + cumulus.EcowittExtraCustomInterval = settings.httpSensors.ecowitt.interval; + } cumulus.Gw1000PrimaryTHSensor = settings.httpSensors.ecowitt.mappings.primaryTHsensor; @@ -405,6 +418,25 @@ public string UpdateConfig(IHttpContext context) context.Response.StatusCode = 500; } + // Ecowitt API + try + { + if (settings.httpSensors.ecowittapi != null) + { + cumulus.EcowittApplicationKey = string.IsNullOrWhiteSpace(settings.httpSensors.ecowittapi.applicationkey) ? null : settings.httpSensors.ecowittapi.applicationkey.Trim(); + cumulus.EcowittUserApiKey = string.IsNullOrWhiteSpace(settings.httpSensors.ecowittapi.userkey) ? null : settings.httpSensors.ecowittapi.userkey.Trim(); + cumulus.EcowittMacAddress = string.IsNullOrWhiteSpace(settings.httpSensors.ecowittapi.mac) ? null : settings.httpSensors.ecowittapi.mac.Trim(); + } + } + catch (Exception ex) + { + var msg = "Error processing Ecowitt API settings: " + ex.Message; + cumulus.LogErrorMessage(msg); + errorMsg += msg + "\n\n"; + context.Response.StatusCode = 500; + } + + // Ambient Extra settings try { @@ -538,6 +570,7 @@ public class JsonExtraSensorHttp public int extraStation { get; set; } public JsonExtraSensorEcowitt ecowitt { get; set; } public JsonExtraSensorAmbient ambient { get; set; } + public JsonStationSettingsEcowittApi ecowittapi { get; set; } } public class JsonExtraSensorAmbient @@ -553,6 +586,7 @@ public class JsonExtraSensorAmbient public bool useCo2 { get; set; } public bool useLightning { get; set; } public bool useLeak { get; set; } + public bool useCamera { get; set; } } public class JsonExtraSensorEcowitt : JsonExtraSensorAmbient @@ -565,6 +599,11 @@ public class JsonExtraSensorEcowitt : JsonExtraSensorAmbient public JsonExtraSensorForwarders forwarders { get; set; } } + public class JsonExtraSensorForwarders + { + public bool usemain { get; set; } + public List forward { get; set; } + } public class JsonExtraSensorBlakeLarsen { diff --git a/CumulusMX/StationSettings.cs b/CumulusMX/StationSettings.cs index 830d4f72..c954e9f3 100644 --- a/CumulusMX/StationSettings.cs +++ b/CumulusMX/StationSettings.cs @@ -1673,7 +1673,7 @@ public class JsonEcowittForwardList public string url { get; set; } } - internal class JsonStationSettingsEcowittApi + public class JsonStationSettingsEcowittApi { public string applicationkey { get; set; } public string userkey { get; set; } @@ -1695,12 +1695,6 @@ public class JsonStationSettingsEcowittMappings public int wn34chan8 { get; set; } } - public class JsonExtraSensorForwarders - { - public bool usemain { get; set; } - public List forward { get; set; } - } - internal class JsonStationSettingsWMR928 { public string comportname { get; set; } diff --git a/Updates.txt b/Updates.txt index cf502b40..fca5f5f4 100644 --- a/Updates.txt +++ b/Updates.txt @@ -15,9 +15,11 @@ Fixed - Fix for error 500 in upload.php when debug is enabled (only occurred on some servers) - PHP uploads using HTTP GET were uploading to the same destination file on one server. Added an Internet advanced option to disable the use of GET and fallback to using POST for all files - Ecowitt Extra Cloud sensor station type not starting as expected +- Ecowitt Extra Cloud sensor station can now pick up the latest web cam URL - Some new record alarms not setting the email message correctly + 3.27.0 - b3257 —————————————— New From a287fd8fe638984511848e1164f842c77b5f22d5 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 2 Nov 2023 13:53:50 +0000 Subject: [PATCH 17/45] Bit streamling to ToWind() --- CumulusMX/WeatherStation.cs | 63 ++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 1db737b2..901ba95b 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -6327,12 +6327,31 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim // Now add up all the values within the required period double totalwindX = 0; double totalwindY = 0; + int diffFrom = 0; + int diffTo = 0; + for (int i = 0; i < MaxWindRecent; i++) { if (timestamp - WindVec[i].Timestamp < cumulus.AvgBearingTime) { totalwindX += WindVec[i].X; totalwindY += WindVec[i].Y; + + if (WindVec[i].Bearing != 0) + { + // this reading was within the last N minutes + int difference = getShortestAngle(AvgBearing, WindVec[i].Bearing); + if ((difference > diffTo)) + { + diffTo = difference; + BearingRangeTo = WindVec[i].Bearing; + } + if ((difference < diffFrom)) + { + diffFrom = difference; + BearingRangeFrom = WindVec[i].Bearing; + } + } } } if (totalwindX == 0) @@ -6365,38 +6384,26 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim AvgBearingText = CompassPoint(AvgBearing); - int diffFrom = 0; - int diffTo = 0; - BearingRangeFrom = AvgBearing; - BearingRangeTo = AvgBearing; - if (AvgBearing != 0) + if (Math.Abs(WindAverage) < 0.01) { - for (int i = 0; i <= MaxWindRecent - 1; i++) - { - if ((timestamp - WindVec[i].Timestamp < cumulus.AvgBearingTime) && (WindVec[i].Bearing != 0)) - { - // this reading was within the last N minutes - int difference = getShortestAngle(AvgBearing, WindVec[i].Bearing); - if ((difference > diffTo)) - { - diffTo = difference; - BearingRangeTo = WindVec[i].Bearing; - // Calculate rounded up value - BearingRangeTo10 = (int) (Math.Ceiling(WindVec[i].Bearing / 10.0) * 10); - } - if ((difference < diffFrom)) - { - diffFrom = difference; - BearingRangeFrom = WindVec[i].Bearing; - BearingRangeFrom10 = (int) (Math.Floor(WindVec[i].Bearing / 10.0) * 10); - } - } - } + BearingRangeFrom = 0; + BearingRangeFrom10 = 0; + BearingRangeTo = 0; + BearingRangeTo10 = 0; } else { - BearingRangeFrom10 = 0; - BearingRangeTo10 = 0; + // Calculate rounded up/down values + BearingRangeFrom10 = (int) (Math.Floor(BearingRangeFrom / 10.0) * 10); + BearingRangeTo10 = (int) (Math.Ceiling(BearingRangeTo / 10.0) * 10) % 360; + if (cumulus.StationOptions.UseZeroBearing && BearingRangeFrom10 == 0) + { + BearingRangeFrom10 = 360; + } + if (cumulus.StationOptions.UseZeroBearing && BearingRangeTo10 == 0) + { + BearingRangeTo10 = 360; + } } WindReadyToPlot = true; From df3715c82bf7bf0211c04480b22d9015c5f2e554 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 2 Nov 2023 14:16:11 +0000 Subject: [PATCH 18/45] Fix for DoWind() spike testing applying conversion to MS multiple times --- CumulusMX/WeatherStation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 901ba95b..110e7cce 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -6169,7 +6169,7 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim // Spike removal is in m/s var windGustMS = ConvertUserWindToMS(gustpar); - var windAvgMS = ConvertUserWindToMS(speedpar == -1 ? previousWind : speedpar); + var windAvgMS = speedpar == -1 ? previousWind : ConvertUserWindToMS(speedpar); if (((Math.Abs(windGustMS - previousGust) > cumulus.Spike.GustDiff) && (previousGust != 999)) || ((Math.Abs(windAvgMS - previousWind) > cumulus.Spike.WindDiff) && (previousWind != 999)) || From 1767d2f57668eea27e62520b156e47035e0bb561 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 2 Nov 2023 14:31:31 +0000 Subject: [PATCH 19/45] Fix not being able to disbale Ecowitt Extra Cloud Sensor Davis VP2, if requested changing the logger interval is now done after reading catch-up data so the archive is not cleared before catch-up --- CumulusMX/DavisStation.cs | 8 +++++--- CumulusMX/ExtraSensorSettings.cs | 16 ++++++---------- Updates.txt | 3 ++- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index 7b6ca644..ce12d255 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -120,9 +120,6 @@ public DavisStation(Cumulus cumulus) : base(cumulus) DecodeReceptionStats(recepStats); } - // check the logger interval - CheckLoggerInterval(); - cumulus.LogMessage("Last update time = " + cumulus.LastUpdateTime); var consoleclock = GetTime(); @@ -200,6 +197,11 @@ public DavisStation(Cumulus cumulus) : base(cumulus) // Read the data from the logger startReadingHistoryData(); } + + // check the logger interval + // do this after reading the history so we do not wipe it out before we read it! + CheckLoggerInterval(); + } private void DecodeReceptionStats(string recepStats) diff --git a/CumulusMX/ExtraSensorSettings.cs b/CumulusMX/ExtraSensorSettings.cs index 84cdb2b2..8a6b2156 100644 --- a/CumulusMX/ExtraSensorSettings.cs +++ b/CumulusMX/ExtraSensorSettings.cs @@ -276,11 +276,11 @@ public string UpdateConfig(IHttpContext context) // Ecowitt Extra settings try { - if (settings.httpSensors.extraStation == 0 || settings.httpSensors.extraStation == 2) - { - cumulus.EcowittExtraEnabled = settings.httpSensors.extraStation == 0; - cumulus.EcowittCloudExtraEnabled = settings.httpSensors.extraStation == 2; + cumulus.EcowittExtraEnabled = settings.httpSensors.extraStation == 0; + cumulus.EcowittCloudExtraEnabled = settings.httpSensors.extraStation == 2; + if (cumulus.EcowittExtraEnabled || cumulus.EcowittCloudExtraEnabled) + { cumulus.EcowittExtraUseSolar = settings.httpSensors.ecowitt.useSolar; cumulus.EcowittExtraUseUv = settings.httpSensors.ecowitt.useUv; cumulus.EcowittExtraUseTempHum = settings.httpSensors.ecowitt.useTempHum; @@ -407,8 +407,6 @@ public string UpdateConfig(IHttpContext context) cumulus.StationOptions.LogExtraSensors = true; } } - else - cumulus.EcowittExtraEnabled = false; } catch (Exception ex) { @@ -440,9 +438,9 @@ public string UpdateConfig(IHttpContext context) // Ambient Extra settings try { - if (settings.httpSensors.extraStation == 1) + cumulus.AmbientExtraEnabled = settings.httpSensors.extraStation == 1; + if (cumulus.AmbientExtraEnabled) { - cumulus.AmbientExtraEnabled = true; cumulus.AmbientExtraUseSolar = settings.httpSensors.ambient.useSolar; cumulus.AmbientExtraUseUv = settings.httpSensors.ambient.useUv; cumulus.AmbientExtraUseTempHum = settings.httpSensors.ambient.useTempHum; @@ -460,8 +458,6 @@ public string UpdateConfig(IHttpContext context) cumulus.StationOptions.LogExtraSensors = true; } } - else - cumulus.AmbientExtraEnabled = false; } catch (Exception ex) { diff --git a/Updates.txt b/Updates.txt index fca5f5f4..caf392f6 100644 --- a/Updates.txt +++ b/Updates.txt @@ -17,7 +17,8 @@ Fixed - Ecowitt Extra Cloud sensor station type not starting as expected - Ecowitt Extra Cloud sensor station can now pick up the latest web cam URL - Some new record alarms not setting the email message correctly - +- Fix for wind spike testing applying unit conversion multiple times in some circumstances +- Davis VP2, if requested changing the logger interval is now done after reading catch-up data so the archive is not cleared before catch-up 3.27.0 - b3257 From 191d1607668cdbc5d8ea0898f56fd14cd022db16 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 2 Nov 2023 20:47:21 +0000 Subject: [PATCH 20/45] Refactor DataEditor LogMessage -> LogErrorMessage --- CumulusMX/Alarm.cs | 6 +- CumulusMX/DataEditor.cs | 1072 +++++++++++++++++++-------------------- CumulusMX/NOAA.cs | 5 + 3 files changed, 532 insertions(+), 551 deletions(-) diff --git a/CumulusMX/Alarm.cs b/CumulusMX/Alarm.cs index 3e9c2322..c9d351d7 100644 --- a/CumulusMX/Alarm.cs +++ b/CumulusMX/Alarm.cs @@ -131,7 +131,7 @@ private void doTriggered(bool value) } catch (Exception ex) { - cumulus.LogMessage($"Alarm ({Name}): Error executing external program '{Action}': {ex.Message}"); + cumulus.LogErrorMessage($"Alarm ({Name}): Error executing external program '{Action}': {ex.Message}"); } } } @@ -297,7 +297,7 @@ private void doUpTriggered(bool value) } catch (Exception ex) { - cumulus.LogMessage($"Alarm: Error executing external program '{Action}': {ex.Message}"); + cumulus.LogErrorMessage($"Alarm: Error executing external program '{Action}': {ex.Message}"); } } } @@ -371,7 +371,7 @@ private void doDownTriggered(bool value) } catch (Exception ex) { - cumulus.LogMessage($"Alarm: Error executing external program '{Action}': {ex.Message}"); + cumulus.LogErrorMessage($"Alarm: Error executing external program '{Action}': {ex.Message}"); } } } diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs index affbcb19..5a4c5710 100644 --- a/CumulusMX/DataEditor.cs +++ b/CumulusMX/DataEditor.cs @@ -8,6 +8,7 @@ using EmbedIO; using ServiceStack; +using ServiceStack.Text; namespace CumulusMX { @@ -64,25 +65,28 @@ internal string EditRainToday(IHttpContext context) } } - var json = "{\"raintoday\":\"" + station.RainToday.ToString(cumulus.RainFormat, invC) + - "\",\"raincounter\":\"" + station.Raincounter.ToString(cumulus.RainFormat, invC) + - "\",\"startofdayrain\":\"" + station.raindaystart.ToString(cumulus.RainFormat, invC) + - "\",\"rainmult\":\"" + cumulus.Calib.Rain.Mult.ToString("F3", invC) + "\"}"; - - return json; + return new JsonObject + { + ["raintoday"] = station.RainToday.ToString(cumulus.RainFormat, invC), + ["raincounter"] = station.Raincounter.ToString(cumulus.RainFormat, invC), + ["startofdayrain"] = station.raindaystart.ToString(cumulus.RainFormat, invC), + ["rainmult"] = cumulus.Calib.Rain.Mult.ToString("F3", invC) + }.ToJson(); } internal string GetRainTodayEditData() { var invC = new CultureInfo(""); var step = (cumulus.RainDPlaces == 1 ? "0.1" : "0.01"); - var json = "{\"raintoday\":\"" + station.RainToday.ToString(cumulus.RainFormat, invC) + - "\",\"raincounter\":\"" + station.Raincounter.ToString(cumulus.RainFormat, invC) + - "\",\"startofdayrain\":\"" + station.raindaystart.ToString(cumulus.RainFormat, invC) + - "\",\"rainmult\":\"" + cumulus.Calib.Rain.Mult.ToString("F3", invC) + - "\",\"step\":\"" + step + "\"}"; - return json; + return new JsonObject + { + ["raintoday"] = station.RainToday.ToString(cumulus.RainFormat, invC), + ["raincounter"] = station.Raincounter.ToString(cumulus.RainFormat, invC), + ["startofdayrain"] = station.raindaystart.ToString(cumulus.RainFormat, invC), + ["rainmult"] = cumulus.Calib.Rain.Mult.ToString("F3", invC), + ["step"] = step + }.ToJson(); } internal string EditDiary(IHttpContext context) @@ -99,7 +103,7 @@ internal string EditDiary(IHttpContext context) } - ServiceStack.Text.JsConfig.DeSerializeFn = datetimeStr => + JsConfig.DeSerializeFn = datetimeStr => { if (string.IsNullOrWhiteSpace(datetimeStr)) { @@ -119,7 +123,7 @@ internal string EditDiary(IHttpContext context) return "{\"result\":\"" + ((result == 1) ? "Success" : "Failed") + "\"}"; - } + } catch (Exception ex) { cumulus.LogErrorMessage("Edit Diary: " + ex.Message); @@ -171,78 +175,77 @@ internal string GetAllTimeRecData() const string dateStampFormat = "d"; const string monthFormat = "MMM yyyy"; - // Records - Temperature values - var json = new StringBuilder("{", 1700); - json.Append($"\"highTempVal\":\"{station.AllTime.HighTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowTempVal\":\"{station.AllTime.LowTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDewPointVal\":\"{station.AllTime.HighDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDewPointVal\":\"{station.AllTime.LowDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highApparentTempVal\":\"{station.AllTime.HighAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowApparentTempVal\":\"{station.AllTime.LowAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highFeelsLikeVal\":\"{station.AllTime.HighFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowFeelsLikeVal\":\"{station.AllTime.LowFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHumidexVal\":\"{station.AllTime.HighHumidex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowWindChillVal\":\"{station.AllTime.LowChill.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHeatIndexVal\":\"{station.AllTime.HighHeatIndex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highMinTempVal\":\"{station.AllTime.HighMinTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowMaxTempVal\":\"{station.AllTime.LowMaxTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDailyTempRangeVal\":\"{station.AllTime.HighDailyTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDailyTempRangeVal\":\"{station.AllTime.LowDailyTempRange.GetValString(cumulus.TempFormat)}\","); - // Records - Temperature timestamps - json.Append($"\"highTempTime\":\"{station.AllTime.HighTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowTempTime\":\"{station.AllTime.LowTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDewPointTime\":\"{station.AllTime.HighDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowDewPointTime\":\"{station.AllTime.LowDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"highApparentTempTime\":\"{station.AllTime.HighAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowApparentTempTime\":\"{station.AllTime.LowAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highFeelsLikeTime\":\"{station.AllTime.HighFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowFeelsLikeTime\":\"{station.AllTime.LowFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHumidexTime\":\"{station.AllTime.HighHumidex.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowWindChillTime\":\"{station.AllTime.LowChill.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHeatIndexTime\":\"{station.AllTime.HighHeatIndex.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMinTempTime\":\"{station.AllTime.HighMinTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowMaxTempTime\":\"{station.AllTime.LowMaxTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyTempRangeTime\":\"{station.AllTime.HighDailyTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"lowDailyTempRangeTime\":\"{station.AllTime.LowDailyTempRange.GetTsString(dateStampFormat)}\","); - // Records - Humidity values - json.Append($"\"highHumidityVal\":\"{station.AllTime.HighHumidity.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"lowHumidityVal\":\"{station.AllTime.LowHumidity.GetValString(cumulus.HumFormat)}\","); - // Records - Humidity times - json.Append($"\"highHumidityTime\":\"{station.AllTime.HighHumidity.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowHumidityTime\":\"{station.AllTime.LowHumidity.GetTsString(timeStampFormat)}\","); - // Records - Pressure values - json.Append($"\"highBarometerVal\":\"{station.AllTime.HighPress.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"lowBarometerVal\":\"{station.AllTime.LowPress.GetValString(cumulus.PressFormat)}\","); - // Records - Pressure times - json.Append($"\"highBarometerTime\":\"{station.AllTime.HighPress.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowBarometerTime\":\"{station.AllTime.LowPress.GetTsString(timeStampFormat)}\","); - // Records - Wind values - json.Append($"\"highGustVal\":\"{station.AllTime.HighGust.GetValString(cumulus.WindFormat)}\","); - json.Append($"\"highWindVal\":\"{station.AllTime.HighWind.GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"highWindRunVal\":\"{station.AllTime.HighWindRun.GetValString(cumulus.WindRunFormat)}\","); - // Records - Wind times - json.Append($"\"highGustTime\":\"{station.AllTime.HighGust.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindTime\":\"{station.AllTime.HighWind.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindRunTime\":\"{station.AllTime.HighWindRun.GetTsString(dateStampFormat)}\","); - // Records - Rain values - json.Append($"\"highRainRateVal\":\"{station.AllTime.HighRainRate.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highHourlyRainVal\":\"{station.AllTime.HourlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highDailyRainVal\":\"{station.AllTime.DailyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRain24hVal\":\"{station.AllTime.HighRain24Hours.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highMonthlyRainVal\":\"{station.AllTime.MonthlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"longestDryPeriodVal\":\"{station.AllTime.LongestDryPeriod.GetValString("f0")}\","); - json.Append($"\"longestWetPeriodVal\":\"{station.AllTime.LongestWetPeriod.GetValString("f0")}\","); - // Records - Rain times - json.Append($"\"highRainRateTime\":\"{station.AllTime.HighRainRate.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHourlyRainTime\":\"{station.AllTime.HourlyRain.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyRainTime\":\"{station.AllTime.DailyRain.GetTsString(dateStampFormat)}\","); - json.Append($"\"highRain24hTime\":\"{station.AllTime.HighRain24Hours.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMonthlyRainTime\":\"{station.AllTime.MonthlyRain.GetTsString(monthFormat)}\","); - json.Append($"\"longestDryPeriodTime\":\"{station.AllTime.LongestDryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"longestWetPeriodTime\":\"{station.AllTime.LongestWetPeriod.GetTsString(dateStampFormat)}\""); - json.Append('}'); - - return json.ToString(); + return new JsonObject + { + // Records - Temperature values + ["highTempVal"] = station.AllTime.HighTemp.GetValString(cumulus.TempFormat), + ["lowTempVal"] = station.AllTime.LowTemp.GetValString(cumulus.TempFormat), + ["highDewPointVal"] = station.AllTime.HighDewPoint.GetValString(cumulus.TempFormat), + ["lowDewPointVal"] = station.AllTime.LowDewPoint.GetValString(cumulus.TempFormat), + ["highApparentTempVal"] = station.AllTime.HighAppTemp.GetValString(cumulus.TempFormat), + ["lowApparentTempVal"] = station.AllTime.LowAppTemp.GetValString(cumulus.TempFormat), + ["highFeelsLikeVal"] = station.AllTime.HighFeelsLike.GetValString(cumulus.TempFormat), + ["lowFeelsLikeVal"] = station.AllTime.LowFeelsLike.GetValString(cumulus.TempFormat), + ["highHumidexVal"] = station.AllTime.HighHumidex.GetValString(cumulus.TempFormat), + ["lowWindChillVal"] = station.AllTime.LowChill.GetValString(cumulus.TempFormat), + ["highHeatIndexVal"] = station.AllTime.HighHeatIndex.GetValString(cumulus.TempFormat), + ["highMinTempVal"] = station.AllTime.HighMinTemp.GetValString(cumulus.TempFormat), + ["lowMaxTempVal"] = station.AllTime.LowMaxTemp.GetValString(cumulus.TempFormat), + ["highDailyTempRangeVal"] = station.AllTime.HighDailyTempRange.GetValString(cumulus.TempFormat), + ["lowDailyTempRangeVal"] = station.AllTime.LowDailyTempRange.GetValString(cumulus.TempFormat), + // Records - Temperature timestamps + ["highTempTime"] = station.AllTime.HighTemp.GetTsString(timeStampFormat), + ["lowTempTime"] = station.AllTime.LowTemp.GetTsString(timeStampFormat), + ["highDewPointTime"] = station.AllTime.HighDewPoint.GetTsString(timeStampFormat), + ["lowDewPointTime"] = station.AllTime.LowDewPoint.GetTsString(timeStampFormat), + ["highApparentTempTime"] = station.AllTime.HighAppTemp.GetTsString(timeStampFormat), + ["lowApparentTempTime"] = station.AllTime.LowAppTemp.GetTsString(timeStampFormat), + ["highFeelsLikeTime"] = station.AllTime.HighFeelsLike.GetTsString(timeStampFormat), + ["lowFeelsLikeTime"] = station.AllTime.LowFeelsLike.GetTsString(timeStampFormat), + ["highHumidexTime"] = station.AllTime.HighHumidex.GetTsString(timeStampFormat), + ["lowWindChillTime"] = station.AllTime.LowChill.GetTsString(timeStampFormat), + ["highHeatIndexTime"] = station.AllTime.HighHeatIndex.GetTsString(timeStampFormat), + ["highMinTempTime"] = station.AllTime.HighMinTemp.GetTsString(timeStampFormat), + ["lowMaxTempTime"] = station.AllTime.LowMaxTemp.GetTsString(timeStampFormat), + ["highDailyTempRangeTime"] = station.AllTime.HighDailyTempRange.GetTsString(dateStampFormat), + ["lowDailyTempRangeTime"] = station.AllTime.LowDailyTempRange.GetTsString(dateStampFormat), + // Records - Humidity values + ["highHumidityVal"] = station.AllTime.HighHumidity.GetValString(cumulus.HumFormat), + ["lowHumidityVal"] = station.AllTime.LowHumidity.GetValString(cumulus.HumFormat), + // Records - Humidity times + ["highHumidityTime"] = station.AllTime.HighHumidity.GetTsString(timeStampFormat), + ["lowHumidityTime"] = station.AllTime.LowHumidity.GetTsString(timeStampFormat), + // Records - Pressure values + ["highBarometerVal"] = station.AllTime.HighPress.GetValString(cumulus.PressFormat), + ["lowBarometerVal"] = station.AllTime.LowPress.GetValString(cumulus.PressFormat), + // Records - Pressure times + ["highBarometerTime"] = station.AllTime.HighPress.GetTsString(timeStampFormat), + ["lowBarometerTime"] = station.AllTime.LowPress.GetTsString(timeStampFormat), + // Records - Wind values + ["highGustVal"] = station.AllTime.HighGust.GetValString(cumulus.WindFormat), + ["highWindVal"] = station.AllTime.HighWind.GetValString(cumulus.WindAvgFormat), + ["highWindRunVal"] = station.AllTime.HighWindRun.GetValString(cumulus.WindRunFormat), + // Records - Wind times + ["highGustTime"] = station.AllTime.HighGust.GetTsString(timeStampFormat), + ["highWindTime"] = station.AllTime.HighWind.GetTsString(timeStampFormat), + ["highWindRunTime"] = station.AllTime.HighWindRun.GetTsString(dateStampFormat), + // Records - Rain values + ["highRainRateVal"] = station.AllTime.HighRainRate.GetValString(cumulus.RainFormat), + ["highHourlyRainVal"] = station.AllTime.HourlyRain.GetValString(cumulus.RainFormat), + ["highDailyRainVal"] = station.AllTime.DailyRain.GetValString(cumulus.RainFormat), + ["highRain24hVal"] = station.AllTime.HighRain24Hours.GetValString(cumulus.RainFormat), + ["highMonthlyRainVal"] = station.AllTime.MonthlyRain.GetValString(cumulus.RainFormat), + ["longestDryPeriodVal"] = station.AllTime.LongestDryPeriod.GetValString("f0"), + ["longestWetPeriodVal"] = station.AllTime.LongestWetPeriod.GetValString("f0"), + // Records - Rain times + ["highRainRateTime"] = station.AllTime.HighRainRate.GetTsString(timeStampFormat), + ["highHourlyRainTime"] = station.AllTime.HourlyRain.GetTsString(timeStampFormat), + ["highDailyRainTime"] = station.AllTime.DailyRain.GetTsString(dateStampFormat), + ["highRain24hTime"] = station.AllTime.HighRain24Hours.GetTsString(timeStampFormat), + ["highMonthlyRainTime"] = station.AllTime.MonthlyRain.GetTsString(monthFormat), + ["longestDryPeriodTime"] = station.AllTime.LongestDryPeriod.GetTsString(dateStampFormat), + ["longestWetPeriodTime"] = station.AllTime.LongestWetPeriod.GetTsString(dateStampFormat) + }.ToJson(); } internal string GetRecordsDayFile(string recordType, DateTime? start = null, DateTime? end = null) @@ -317,8 +320,6 @@ internal string GetRecordsDayFile(string recordType, DateTime? start = null, Dat var thisDateWet = DateTime.MinValue; var firstRec = true; - var json = new StringBuilder("{", 2048); - int rainThreshold; if (cumulus.RainDayThreshold > 0) { @@ -588,70 +589,67 @@ internal string GetRecordsDayFile(string recordType, DateTime? start = null, Dat cumulus.LogWarningMessage("GetRecordsDayFile: Error no day file records found"); } - json.Append($"\"highTempValDayfile\":\"{highTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highTempTimeDayfile\":\"{highTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowTempValDayfile\":\"{lowTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowTempTimeDayfile\":\"{lowTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDewPointValDayfile\":\"{highDewPt.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDewPointTimeDayfile\":\"{highDewPt.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowDewPointValDayfile\":\"{lowDewPt.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDewPointTimeDayfile\":\"{lowDewPt.GetTsString(timeStampFormat)}\","); - json.Append($"\"highApparentTempValDayfile\":\"{highAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highApparentTempTimeDayfile\":\"{highAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowApparentTempValDayfile\":\"{lowAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowApparentTempTimeDayfile\":\"{lowAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highFeelsLikeValDayfile\":\"{highFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highFeelsLikeTimeDayfile\":\"{highFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowFeelsLikeValDayfile\":\"{lowFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowFeelsLikeTimeDayfile\":\"{lowFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHumidexValDayfile\":\"{highHumidex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHumidexTimeDayfile\":\"{highHumidex.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowWindChillValDayfile\":\"{lowWindChill.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowWindChillTimeDayfile\":\"{lowWindChill.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHeatIndexValDayfile\":\"{highHeatInd.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHeatIndexTimeDayfile\":\"{highHeatInd.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMinTempValDayfile\":\"{highMinTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highMinTempTimeDayfile\":\"{highMinTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowMaxTempValDayfile\":\"{lowMaxTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowMaxTempTimeDayfile\":\"{lowMaxTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyTempRangeValDayfile\":\"{highTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDailyTempRangeTimeDayfile\":\"{highTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"lowDailyTempRangeValDayfile\":\"{lowTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDailyTempRangeTimeDayfile\":\"{lowTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"highHumidityValDayfile\":\"{highHum.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"highHumidityTimeDayfile\":\"{highHum.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowHumidityValDayfile\":\"{lowHum.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"lowHumidityTimeDayfile\":\"{lowHum.GetTsString(timeStampFormat)}\","); - json.Append($"\"highBarometerValDayfile\":\"{highBaro.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"highBarometerTimeDayfile\":\"{highBaro.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowBarometerValDayfile\":\"{lowBaro.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"lowBarometerTimeDayfile\":\"{lowBaro.GetTsString(timeStampFormat)}\","); - json.Append($"\"highGustValDayfile\":\"{highGust.GetValString(cumulus.WindFormat)}\","); - json.Append($"\"highGustTimeDayfile\":\"{highGust.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindValDayfile\":\"{highWind.GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"highWindTimeDayfile\":\"{highWind.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindRunValDayfile\":\"{highWindRun.GetValString(cumulus.WindRunFormat)}\","); - json.Append($"\"highWindRunTimeDayfile\":\"{highWindRun.GetTsString(dateStampFormat)}\","); - json.Append($"\"highRainRateValDayfile\":\"{highRainRate.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRainRateTimeDayfile\":\"{highRainRate.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHourlyRainValDayfile\":\"{highRainHour.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highHourlyRainTimeDayfile\":\"{highRainHour.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyRainValDayfile\":\"{highRainDay.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highDailyRainTimeDayfile\":\"{highRainDay.GetTsString(dateStampFormat)}\","); - if (recordType != "thismonth") - { - json.Append($"\"highMonthlyRainValDayfile\":\"{highRainMonth.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highMonthlyRainTimeDayfile\":\"{highRainMonth.GetTsString(monthFormat)}\","); - } - json.Append($"\"highRain24hValDayfile\":\"{highRain24h.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRain24hTimeDayfile\":\"{highRain24h.GetTsString(timeStampFormat)}\","); - json.Append($"\"longestDryPeriodValDayfile\":\"{dryPeriod.GetValString()}\","); - json.Append($"\"longestDryPeriodTimeDayfile\":\"{dryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"longestWetPeriodValDayfile\":\"{wetPeriod.GetValString()}\","); - json.Append($"\"longestWetPeriodTimeDayfile\":\"{wetPeriod.GetTsString(dateStampFormat)}\""); - json.Append('}'); - - return json.ToString(); + return new JsonObject + { + ["highTempValDayfile"] = highTemp.GetValString(cumulus.TempFormat), + ["highTempTimeDayfile"] = highTemp.GetTsString(timeStampFormat), + ["lowTempValDayfile"] = lowTemp.GetValString(cumulus.TempFormat), + ["lowTempTimeDayfile"] = lowTemp.GetTsString(timeStampFormat), + ["highDewPointValDayfile"] = highDewPt.GetValString(cumulus.TempFormat), + ["highDewPointTimeDayfile"] = highDewPt.GetTsString(timeStampFormat), + ["lowDewPointValDayfile"] = lowDewPt.GetValString(cumulus.TempFormat), + ["lowDewPointTimeDayfile"] = lowDewPt.GetTsString(timeStampFormat), + ["highApparentTempValDayfile"] = highAppTemp.GetValString(cumulus.TempFormat), + ["highApparentTempTimeDayfile"] = highAppTemp.GetTsString(timeStampFormat), + ["lowApparentTempValDayfile"] = lowAppTemp.GetValString(cumulus.TempFormat), + ["lowApparentTempTimeDayfile"] = lowAppTemp.GetTsString(timeStampFormat), + ["highFeelsLikeValDayfile"] = highFeelsLike.GetValString(cumulus.TempFormat), + ["highFeelsLikeTimeDayfile"] = highFeelsLike.GetTsString(timeStampFormat), + ["lowFeelsLikeValDayfile"] = lowFeelsLike.GetValString(cumulus.TempFormat), + ["lowFeelsLikeTimeDayfile"] = lowFeelsLike.GetTsString(timeStampFormat), + ["highHumidexValDayfile"] = highHumidex.GetValString(cumulus.TempFormat), + ["highHumidexTimeDayfile"] = highHumidex.GetTsString(timeStampFormat), + ["lowWindChillValDayfile"] = lowWindChill.GetValString(cumulus.TempFormat), + ["lowWindChillTimeDayfile"] = lowWindChill.GetTsString(timeStampFormat), + ["highHeatIndexValDayfile"] = highHeatInd.GetValString(cumulus.TempFormat), + ["highHeatIndexTimeDayfile"] = highHeatInd.GetTsString(timeStampFormat), + ["highMinTempValDayfile"] = highMinTemp.GetValString(cumulus.TempFormat), + ["highMinTempTimeDayfile"] = highMinTemp.GetTsString(timeStampFormat), + ["lowMaxTempValDayfile"] = lowMaxTemp.GetValString(cumulus.TempFormat), + ["lowMaxTempTimeDayfile"] = lowMaxTemp.GetTsString(timeStampFormat), + ["highDailyTempRangeValDayfile"] = highTempRange.GetValString(cumulus.TempFormat), + ["highDailyTempRangeTimeDayfile"] = highTempRange.GetTsString(dateStampFormat), + ["lowDailyTempRangeValDayfile"] = lowTempRange.GetValString(cumulus.TempFormat), + ["lowDailyTempRangeTimeDayfile"] = lowTempRange.GetTsString(dateStampFormat), + ["highHumidityValDayfile"] = highHum.GetValString(cumulus.HumFormat), + ["highHumidityTimeDayfile"] = highHum.GetTsString(timeStampFormat), + ["lowHumidityValDayfile"] = lowHum.GetValString(cumulus.HumFormat), + ["lowHumidityTimeDayfile"] = lowHum.GetTsString(timeStampFormat), + ["highBarometerValDayfile"] = highBaro.GetValString(cumulus.PressFormat), + ["highBarometerTimeDayfile"] = highBaro.GetTsString(timeStampFormat), + ["lowBarometerValDayfile"] = lowBaro.GetValString(cumulus.PressFormat), + ["lowBarometerTimeDayfile"] = lowBaro.GetTsString(timeStampFormat), + ["highGustValDayfile"] = highGust.GetValString(cumulus.WindFormat), + ["highGustTimeDayfile"] = highGust.GetTsString(timeStampFormat), + ["highWindValDayfile"] = highWindRun.GetValString(cumulus.WindRunFormat), + ["highWindTimeDayfile"] = highWindRun.GetTsString(dateStampFormat), + ["highWindRunValDayfile"] = highWindRun.GetValString(cumulus.WindRunFormat), + ["highWindRunTimeDayfile"] = highWindRun.GetTsString(dateStampFormat), + ["highRainRateValDayfile"] = highRainRate.GetValString(cumulus.RainFormat), + ["highRainRateTimeDayfile"] = highRainRate.GetTsString(timeStampFormat), + ["highHourlyRainValDayfile"] = highRainHour.GetValString(cumulus.RainFormat), + ["highHourlyRainTimeDayfile"] = highRainHour.GetTsString(timeStampFormat), + ["highDailyRainValDayfile"] = highRainDay.GetValString(cumulus.RainFormat), + ["highDailyRainTimeDayfile"] = highRainDay.GetTsString(dateStampFormat), + ["highMonthlyRainValDayfile"] = highRainMonth.GetValString(cumulus.RainFormat), + ["highMonthlyRainTimeDayfile"] = highRainMonth.GetTsString(monthFormat), + ["highRain24hValDayfile"] = highRain24h.GetValString(cumulus.RainFormat), + ["highRain24hTimeDayfile"] = highRain24h.GetTsString(timeStampFormat), + ["longestDryPeriodValDayfile"] = dryPeriod.GetValString(), + ["longestDryPeriodTimeDayfile"] = dryPeriod.GetTsString(dateStampFormat), + ["longestWetPeriodValDayfile"] = wetPeriod.GetValString(), + ["longestWetPeriodTimeDayfile"] = wetPeriod.GetTsString(dateStampFormat) + }.ToJson(); } internal string GetRecordsLogFile(string recordType) @@ -660,7 +658,6 @@ internal string GetRecordsLogFile(string recordType) const string dateStampFormat = "d"; const string monthFormat = "MMM yyyy"; - var json = new StringBuilder("{", 2048); DateTime filedate, datefrom; switch (recordType) @@ -1136,81 +1133,71 @@ internal string GetRecordsLogFile(string recordType) dryPeriod.Ts = thisDateDry; } - json.Append($"\"highTempValLogfile\":\"{highTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highTempTimeLogfile\":\"{highTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowTempValLogfile\":\"{lowTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowTempTimeLogfile\":\"{lowTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDewPointValLogfile\":\"{highDewPt.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDewPointTimeLogfile\":\"{highDewPt.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowDewPointValLogfile\":\"{lowDewPt.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDewPointTimeLogfile\":\"{lowDewPt.GetTsString(timeStampFormat)}\","); - json.Append($"\"highApparentTempValLogfile\":\"{highAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highApparentTempTimeLogfile\":\"{highAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowApparentTempValLogfile\":\"{lowAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowApparentTempTimeLogfile\":\"{lowAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highFeelsLikeValLogfile\":\"{highFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highFeelsLikeTimeLogfile\":\"{highFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowFeelsLikeValLogfile\":\"{lowFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowFeelsLikeTimeLogfile\":\"{lowFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHumidexValLogfile\":\"{highHumidex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHumidexTimeLogfile\":\"{highHumidex.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowWindChillValLogfile\":\"{lowWindChill.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowWindChillTimeLogfile\":\"{lowWindChill.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHeatIndexValLogfile\":\"{highHeatInd.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHeatIndexTimeLogfile\":\"{highHeatInd.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMinTempValLogfile\":\"{highMinTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highMinTempTimeLogfile\":\"{highMinTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowMaxTempValLogfile\":\"{lowMaxTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowMaxTempTimeLogfile\":\"{lowMaxTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyTempRangeValLogfile\":\"{highTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDailyTempRangeTimeLogfile\":\"{highTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"lowDailyTempRangeValLogfile\":\"{lowTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDailyTempRangeTimeLogfile\":\"{lowTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"highHumidityValLogfile\":\"{highHum.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"highHumidityTimeLogfile\":\"{highHum.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowHumidityValLogfile\":\"{lowHum.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"lowHumidityTimeLogfile\":\"{lowHum.GetTsString(timeStampFormat)}\","); - json.Append($"\"highBarometerValLogfile\":\"{highBaro.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"highBarometerTimeLogfile\":\"{highBaro.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowBarometerValLogfile\":\"{lowBaro.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"lowBarometerTimeLogfile\":\"{lowBaro.GetTsString(timeStampFormat)}\","); - json.Append($"\"highGustValLogfile\":\"{highGust.GetValString(cumulus.WindFormat)}\","); - json.Append($"\"highGustTimeLogfile\":\"{highGust.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindValLogfile\":\"{highWind.GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"highWindTimeLogfile\":\"{highWind.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindRunValLogfile\":\"{highWindRun.GetValString(cumulus.WindRunFormat)}\","); - json.Append($"\"highWindRunTimeLogfile\":\"{highWindRun.GetTsString(dateStampFormat)}\","); - json.Append($"\"highRainRateValLogfile\":\"{highRainRate.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRainRateTimeLogfile\":\"{highRainRate.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHourlyRainValLogfile\":\"{highRainHour.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highHourlyRainTimeLogfile\":\"{highRainHour.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyRainValLogfile\":\"{highRainDay.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highDailyRainTimeLogfile\":\"{highRainDay.GetTsString(dateStampFormat)}\","); - json.Append($"\"highRain24hValLogfile\":\"{highRain24h.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRain24hTimeLogfile\":\"{highRain24h.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMonthlyRainValLogfile\":\"{highRainMonth.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highMonthlyRainTimeLogfile\":\"{highRainMonth.GetTsString(monthFormat)}\","); - if (recordType == "alltime") - { - json.Append($"\"longestDryPeriodValLogfile\":\"{dryPeriod.GetValString()}\","); - json.Append($"\"longestDryPeriodTimeLogfile\":\"{dryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"longestWetPeriodValLogfile\":\"{wetPeriod.GetValString()}\","); - json.Append($"\"longestWetPeriodTimeLogfile\":\"{wetPeriod.GetTsString(dateStampFormat)}\""); - } - else - { - json.Append($"\"longestDryPeriodValLogfile\":\"{dryPeriod.GetValString()}\","); - json.Append($"\"longestDryPeriodTimeLogfile\":\"{dryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"longestWetPeriodValLogfile\":\"{wetPeriod.GetValString()}\","); - json.Append($"\"longestWetPeriodTimeLogfile\":\"{wetPeriod.GetTsString(dateStampFormat)}\""); - } - json.Append('}'); - watch.Stop(); var elapsed = watch.ElapsedMilliseconds; cumulus.LogDebugMessage($"GetRecordsLogFile: Logfiles parse = {elapsed} ms"); - return json.ToString(); + return new JsonObject + { + ["highTempValLogfile"] = highTemp.GetValString(cumulus.TempFormat), + ["highTempTimeLogfile"] = highTemp.GetTsString(timeStampFormat), + ["lowTempValLogfile"] = lowTemp.GetValString(timeStampFormat), + ["lowTempTimeLogfile"] = lowTemp.GetTsString(timeStampFormat), + ["highDewPointValLogfile"] = highDewPt.GetValString(cumulus.TempFormat), + ["highDewPointTimeLogfile"] = highDewPt.GetTsString(timeStampFormat), + ["lowDewPointValLogfile"] = lowDewPt.GetValString(cumulus.TempFormat), + ["lowDewPointTimeLogfile"] = lowDewPt.GetTsString(timeStampFormat), + ["highApparentTempValLogfile"] = highAppTemp.GetValString(cumulus.TempFormat), + ["highApparentTempTimeLogfile"] = highAppTemp.GetTsString(timeStampFormat), + ["lowApparentTempValLogfile"] = lowAppTemp.GetValString(cumulus.TempFormat), + ["lowApparentTempTimeLogfile"] = lowAppTemp.GetTsString(timeStampFormat), + ["highFeelsLikeValLogfile"] = highFeelsLike.GetValString(cumulus.TempFormat), + ["highFeelsLikeTimeLogfile"] = highFeelsLike.GetTsString(timeStampFormat), + ["lowFeelsLikeValLogfile"] = lowFeelsLike.GetValString(cumulus.TempFormat), + ["lowFeelsLikeTimeLogfile"] = lowFeelsLike.GetTsString(timeStampFormat), + ["highHumidexValLogfile"] = highHumidex.GetValString(cumulus.TempFormat), + ["highHumidexTimeLogfile"] = highHumidex.GetTsString(timeStampFormat), + ["lowWindChillValLogfile"] = lowWindChill.GetValString(cumulus.TempFormat), + ["lowWindChillTimeLogfile"] = lowWindChill.GetTsString(timeStampFormat), + ["highHeatIndexValLogfile"] = highHeatInd.GetValString(cumulus.TempFormat), + ["highHeatIndexTimeLogfile"] = highHeatInd.GetTsString(timeStampFormat), + ["highMinTempValLogfile"] = highMinTemp.GetValString(cumulus.TempFormat), + ["highMinTempTimeLogfile"] = highMinTemp.GetTsString(timeStampFormat), + ["lowMaxTempValLogfile"] = lowMaxTemp.GetValString(cumulus.TempFormat), + ["lowMaxTempTimeLogfile"] = lowMaxTemp.GetTsString(timeStampFormat), + ["highDailyTempRangeValLogfile"] = highTempRange.GetValString(cumulus.TempFormat), + ["highDailyTempRangeTimeLogfile"] = highTempRange.GetTsString(dateStampFormat), + ["lowDailyTempRangeValLogfile"] = lowTempRange.GetValString(cumulus.TempFormat), + ["lowDailyTempRangeTimeLogfile"] = lowTempRange.GetTsString(dateStampFormat), + ["highHumidityValLogfile"] = highHum.GetValString(cumulus.HumFormat), + ["highHumidityTimeLogfile"] = highHum.GetTsString(timeStampFormat), + ["lowHumidityValLogfile"] = lowHum.GetValString(cumulus.HumFormat), + ["lowHumidityTimeLogfile"] = lowHum.GetTsString(timeStampFormat), + ["highBarometerValLogfile"] = highBaro.GetValString(cumulus.PressFormat), + ["highBarometerTimeLogfile"] = highBaro.GetTsString(timeStampFormat), + ["lowBarometerValLogfile"] = lowBaro.GetValString(cumulus.PressFormat), + ["lowBarometerTimeLogfile"] = lowBaro.GetTsString(timeStampFormat), + ["highGustValLogfile"] = highGust.GetValString(cumulus.WindFormat), + ["highGustTimeLogfile"] = highGust.GetTsString(timeStampFormat), + ["highWindValLogfile"] = highWind.GetValString(cumulus.WindAvgFormat), + ["highWindTimeLogfile"] = highWind.GetTsString(timeStampFormat), + ["highWindRunValLogfile"] = highWindRun.GetValString(cumulus.WindRunFormat), + ["highWindRunTimeLogfile"] = highWindRun.GetTsString(dateStampFormat), + ["highRainRateValLogfile"] = highRainRate.GetValString(cumulus.RainFormat), + ["highRainRateTimeLogfile"] = highRainRate.GetTsString(timeStampFormat), + ["highHourlyRainValLogfile"] = highRainHour.GetValString(cumulus.RainFormat), + ["highHourlyRainTimeLogfile"] = highRainHour.GetTsString(timeStampFormat), + ["highDailyRainValLogfile"] = highRainDay.GetValString(cumulus.RainFormat), + ["highDailyRainTimeLogfile"] = highRainDay.GetTsString(dateStampFormat), + ["highRain24hValLogfile"] = highRain24h.GetValString(cumulus.RainFormat), + ["highRain24hTimeLogfile"] = highRain24h.GetTsString(timeStampFormat), + ["highMonthlyRainValLogfile"] = highRainMonth.GetValString(cumulus.RainFormat), + ["highMonthlyRainTimeLogfile"] = highRainMonth.GetTsString(monthFormat), + ["longestDryPeriodValLogfile"] = dryPeriod.GetValString(), + ["longestDryPeriodTimeLogfile"] = dryPeriod.GetTsString(dateStampFormat), + ["longestWetPeriodValLogfile"] = wetPeriod.GetValString(), + ["longestWetPeriodTimeLogfile"] = wetPeriod.GetTsString(dateStampFormat) + }.ToJson(); } /* @@ -1639,82 +1626,80 @@ internal string GetMonthlyRecData() const string dateStampFormat = "d"; const string monthFormat = "MMM yyyy"; - var json = new StringBuilder("{", 21000); + var jsonObj = new JsonObject(); for (var m = 1; m <= 12; m++) { // Records - Temperature values - json.Append($"\"{m}-highTempVal\":\"{station.MonthlyRecs[m].HighTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowTempVal\":\"{station.MonthlyRecs[m].LowTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highDewPointVal\":\"{station.MonthlyRecs[m].HighDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowDewPointVal\":\"{station.MonthlyRecs[m].LowDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highApparentTempVal\":\"{station.MonthlyRecs[m].HighAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowApparentTempVal\":\"{station.MonthlyRecs[m].LowAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highFeelsLikeVal\":\"{station.MonthlyRecs[m].HighFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowFeelsLikeVal\":\"{station.MonthlyRecs[m].LowFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highHumidexVal\":\"{station.MonthlyRecs[m].HighHumidex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowWindChillVal\":\"{station.MonthlyRecs[m].LowChill.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highHeatIndexVal\":\"{station.MonthlyRecs[m].HighHeatIndex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highMinTempVal\":\"{station.MonthlyRecs[m].HighMinTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowMaxTempVal\":\"{station.MonthlyRecs[m].LowMaxTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highDailyTempRangeVal\":\"{station.MonthlyRecs[m].HighDailyTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowDailyTempRangeVal\":\"{station.MonthlyRecs[m].LowDailyTempRange.GetValString(cumulus.TempFormat)}\","); + jsonObj.Add($"{m}-highTempVal", station.MonthlyRecs[m].HighTemp.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowTempVal", station.MonthlyRecs[m].LowTemp.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highDewPointVal", station.MonthlyRecs[m].HighDewPoint.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowDewPointVal", station.MonthlyRecs[m].LowDewPoint.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highApparentTempVal", station.MonthlyRecs[m].HighAppTemp.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowApparentTempVal", station.MonthlyRecs[m].LowAppTemp.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highFeelsLikeVal", station.MonthlyRecs[m].HighFeelsLike.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowFeelsLikeVal", station.MonthlyRecs[m].LowFeelsLike.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highHumidexVal", station.MonthlyRecs[m].HighHumidex.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowWindChillVal", station.MonthlyRecs[m].LowChill.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highHeatIndexVal", station.MonthlyRecs[m].HighHeatIndex.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highMinTempVal", station.MonthlyRecs[m].HighMinTemp.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowMaxTempVal", station.MonthlyRecs[m].LowMaxTemp.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highDailyTempRangeVal", station.MonthlyRecs[m].HighDailyTempRange.GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowDailyTempRangeVal", station.MonthlyRecs[m].LowDailyTempRange.GetValString(cumulus.TempFormat)); // Records - Temperature timestamps - json.Append($"\"{m}-highTempTime\":\"{station.MonthlyRecs[m].HighTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowTempTime\":\"{station.MonthlyRecs[m].LowTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDewPointTime\":\"{station.MonthlyRecs[m].HighDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowDewPointTime\":\"{station.MonthlyRecs[m].LowDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highApparentTempTime\":\"{station.MonthlyRecs[m].HighAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowApparentTempTime\":\"{station.MonthlyRecs[m].LowAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highFeelsLikeTime\":\"{station.MonthlyRecs[m].HighFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowFeelsLikeTime\":\"{station.MonthlyRecs[m].LowFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHumidexTime\":\"{station.MonthlyRecs[m].HighHumidex.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowWindChillTime\":\"{station.MonthlyRecs[m].LowChill.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHeatIndexTime\":\"{station.MonthlyRecs[m].HighHeatIndex.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highMinTempTime\":\"{station.MonthlyRecs[m].HighMinTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowMaxTempTime\":\"{station.MonthlyRecs[m].LowMaxTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDailyTempRangeTime\":\"{station.MonthlyRecs[m].HighDailyTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-lowDailyTempRangeTime\":\"{station.MonthlyRecs[m].LowDailyTempRange.GetTsString(dateStampFormat)}\","); + jsonObj.Add($"{m}-highTempTime", station.MonthlyRecs[m].HighTemp.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowTempTime", station.MonthlyRecs[m].LowTemp.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDewPointTime", station.MonthlyRecs[m].HighDewPoint.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowDewPointTime", station.MonthlyRecs[m].LowDewPoint.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highApparentTempTime", station.MonthlyRecs[m].HighAppTemp.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowApparentTempTime", station.MonthlyRecs[m].LowAppTemp.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highFeelsLikeTime", station.MonthlyRecs[m].HighFeelsLike.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowFeelsLikeTime", station.MonthlyRecs[m].LowFeelsLike.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHumidexTime", station.MonthlyRecs[m].HighHumidex.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowWindChillTime", station.MonthlyRecs[m].LowChill.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHeatIndexTime", station.MonthlyRecs[m].HighHeatIndex.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highMinTempTime", station.MonthlyRecs[m].HighMinTemp.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowMaxTempTime", station.MonthlyRecs[m].LowMaxTemp.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDailyTempRangeTime", station.MonthlyRecs[m].HighDailyTempRange.GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-lowDailyTempRangeTime", station.MonthlyRecs[m].LowDailyTempRange.GetTsString(dateStampFormat)); // Records - Humidity values - json.Append($"\"{m}-highHumidityVal\":\"{station.MonthlyRecs[m].HighHumidity.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"{m}-lowHumidityVal\":\"{station.MonthlyRecs[m].LowHumidity.GetValString(cumulus.HumFormat)}\","); + jsonObj.Add($"{m}-highHumidityVal", station.MonthlyRecs[m].HighHumidity.GetValString(cumulus.HumFormat)); + jsonObj.Add($"{m}-lowHumidityVal", station.MonthlyRecs[m].LowHumidity.GetValString(cumulus.HumFormat)); // Records - Humidity times - json.Append($"\"{m}-highHumidityTime\":\"{station.MonthlyRecs[m].HighHumidity.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowHumidityTime\":\"{station.MonthlyRecs[m].LowHumidity.GetTsString(timeStampFormat)}\","); + jsonObj.Add($"{m}-highHumidityTime", station.MonthlyRecs[m].HighHumidity.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowHumidityTime", station.MonthlyRecs[m].LowHumidity.GetTsString(timeStampFormat)); // Records - Pressure values - json.Append($"\"{m}-highBarometerVal\":\"{station.MonthlyRecs[m].HighPress.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"{m}-lowBarometerVal\":\"{station.MonthlyRecs[m].LowPress.GetValString(cumulus.PressFormat)}\","); + jsonObj.Add($"{m}-highBarometerVal", station.MonthlyRecs[m].HighPress.GetValString(cumulus.PressFormat)); + jsonObj.Add($"{m}-lowBarometerVal", station.MonthlyRecs[m].LowPress.GetValString(cumulus.PressFormat)); // Records - Pressure times - json.Append($"\"{m}-highBarometerTime\":\"{station.MonthlyRecs[m].HighPress.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowBarometerTime\":\"{station.MonthlyRecs[m].LowPress.GetTsString(timeStampFormat)}\","); + jsonObj.Add($"{m}-highBarometerTime", station.MonthlyRecs[m].HighPress.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowBarometerTime", station.MonthlyRecs[m].LowPress.GetTsString(timeStampFormat)); // Records - Wind values - json.Append($"\"{m}-highGustVal\":\"{station.MonthlyRecs[m].HighGust.GetValString(cumulus.WindFormat)}\","); - json.Append($"\"{m}-highWindVal\":\"{station.MonthlyRecs[m].HighWind.GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"{m}-highWindRunVal\":\"{station.MonthlyRecs[m].HighWindRun.GetValString(cumulus.WindRunFormat)}\","); + jsonObj.Add($"{m}-highGustVal", station.MonthlyRecs[m].HighGust.GetValString(cumulus.WindFormat)); + jsonObj.Add($"{m}-highWindVal", station.MonthlyRecs[m].HighWind.GetValString(cumulus.WindAvgFormat)); + jsonObj.Add($"{m}-highWindRunVal", station.MonthlyRecs[m].HighWindRun.GetValString(cumulus.WindRunFormat)); // Records - Wind times - json.Append($"\"{m}-highGustTime\":\"{station.MonthlyRecs[m].HighGust.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highWindTime\":\"{station.MonthlyRecs[m].HighWind.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highWindRunTime\":\"{station.MonthlyRecs[m].HighWindRun.GetTsString(dateStampFormat)}\","); + jsonObj.Add($"{m}-highGustTime", station.MonthlyRecs[m].HighGust.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highWindTime", station.MonthlyRecs[m].HighWind.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highWindRunTime", station.MonthlyRecs[m].HighWindRun.GetTsString(dateStampFormat)); // Records - Rain values - json.Append($"\"{m}-highRainRateVal\":\"{station.MonthlyRecs[m].HighRainRate.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highHourlyRainVal\":\"{station.MonthlyRecs[m].HourlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highDailyRainVal\":\"{station.MonthlyRecs[m].DailyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highRain24hVal\":\"{station.MonthlyRecs[m].HighRain24Hours.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highMonthlyRainVal\":\"{station.MonthlyRecs[m].MonthlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-longestDryPeriodVal\":\"{station.MonthlyRecs[m].LongestDryPeriod.GetValString("f0")}\","); - json.Append($"\"{m}-longestWetPeriodVal\":\"{station.MonthlyRecs[m].LongestWetPeriod.GetValString("f0")}\","); + jsonObj.Add($"{m}-highRainRateVal", station.MonthlyRecs[m].HighRainRate.GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highHourlyRainVal", station.MonthlyRecs[m].HourlyRain.GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highDailyRainVal", station.MonthlyRecs[m].DailyRain.GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highRain24hVal", station.MonthlyRecs[m].HighRain24Hours.GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highMonthlyRainVal", station.MonthlyRecs[m].MonthlyRain.GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-longestDryPeriodVal", station.MonthlyRecs[m].LongestDryPeriod.GetValString("f0")); + jsonObj.Add($"{m}-longestWetPeriodVal", station.MonthlyRecs[m].LongestWetPeriod.GetValString("f0")); // Records - Rain times - json.Append($"\"{m}-highRainRateTime\":\"{station.MonthlyRecs[m].HighRainRate.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHourlyRainTime\":\"{station.MonthlyRecs[m].HourlyRain.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDailyRainTime\":\"{station.MonthlyRecs[m].DailyRain.GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highRain24hTime\":\"{station.MonthlyRecs[m].HighRain24Hours.GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highMonthlyRainTime\":\"{station.MonthlyRecs[m].MonthlyRain.GetTsString(monthFormat)}\","); - json.Append($"\"{m}-longestDryPeriodTime\":\"{station.MonthlyRecs[m].LongestDryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-longestWetPeriodTime\":\"{station.MonthlyRecs[m].LongestWetPeriod.GetTsString(dateStampFormat)}\","); + jsonObj.Add($"{m}-highRainRateTime", station.MonthlyRecs[m].HighRainRate.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHourlyRainTime", station.MonthlyRecs[m].HourlyRain.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDailyRainTime", station.MonthlyRecs[m].DailyRain.GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highRain24hTime", station.MonthlyRecs[m].HighRain24Hours.GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highMonthlyRainTime", station.MonthlyRecs[m].MonthlyRain.GetTsString(monthFormat)); + jsonObj.Add($"{m}-longestDryPeriodTime", station.MonthlyRecs[m].LongestDryPeriod.GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-longestWetPeriodTime", station.MonthlyRecs[m].LongestWetPeriod.GetTsString(dateStampFormat)); } - json.Length--; - json.Append('}'); - return json.ToString(); + return jsonObj.ToJson(); } internal string GetMonthlyRecDayFile() @@ -1796,8 +1781,6 @@ internal string GetMonthlyRecDayFile() int monthOffset; var firstEntry = true; - var json = new StringBuilder("{", 25500); - int rainThreshold; if (cumulus.RainDayThreshold > 0) { @@ -2084,72 +2067,72 @@ internal string GetMonthlyRecDayFile() cumulus.LogWarningMessage("Error failed to find day records"); } + var jsonObj = new JsonObject(); + for (var i = 0; i < 12; i++) { var m = i + 1; - json.Append($"\"{m}-highTempValDayfile\":\"{highTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highTempTimeDayfile\":\"{highTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowTempValDayfile\":\"{lowTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowTempTimeDayfile\":\"{lowTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDewPointValDayfile\":\"{highDewPt[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highDewPointTimeDayfile\":\"{highDewPt[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowDewPointValDayfile\":\"{lowDewPt[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowDewPointTimeDayfile\":\"{lowDewPt[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highApparentTempValDayfile\":\"{highAppTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highApparentTempTimeDayfile\":\"{highAppTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowApparentTempValDayfile\":\"{lowAppTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowApparentTempTimeDayfile\":\"{lowAppTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highFeelsLikeValDayfile\":\"{highFeelsLike[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highFeelsLikeTimeDayfile\":\"{highFeelsLike[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowFeelsLikeValDayfile\":\"{lowFeelsLike[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowFeelsLikeTimeDayfile\":\"{lowFeelsLike[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHumidexValDayfile\":\"{highHumidex[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highHumidexTimeDayfile\":\"{highHumidex[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowWindChillValDayfile\":\"{lowWindChill[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowWindChillTimeDayfile\":\"{lowWindChill[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHeatIndexValDayfile\":\"{highHeatInd[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highHeatIndexTimeDayfile\":\"{highHeatInd[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highMinTempValDayfile\":\"{highMinTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highMinTempTimeDayfile\":\"{highMinTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowMaxTempValDayfile\":\"{lowMaxTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowMaxTempTimeDayfile\":\"{lowMaxTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDailyTempRangeValDayfile\":\"{highTempRange[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highDailyTempRangeTimeDayfile\":\"{highTempRange[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-lowDailyTempRangeValDayfile\":\"{lowTempRange[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowDailyTempRangeTimeDayfile\":\"{lowTempRange[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highHumidityValDayfile\":\"{highHum[i].GetValString(cumulus.HumFormat)}\","); - json.Append($"\"{m}-highHumidityTimeDayfile\":\"{highHum[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowHumidityValDayfile\":\"{lowHum[i].GetValString(cumulus.HumFormat)}\","); - json.Append($"\"{m}-lowHumidityTimeDayfile\":\"{lowHum[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highBarometerValDayfile\":\"{highBaro[i].GetValString(cumulus.PressFormat)}\","); - json.Append($"\"{m}-highBarometerTimeDayfile\":\"{highBaro[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowBarometerValDayfile\":\"{lowBaro[i].GetValString(cumulus.PressFormat)}\","); - json.Append($"\"{m}-lowBarometerTimeDayfile\":\"{lowBaro[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highGustValDayfile\":\"{highGust[i].GetValString(cumulus.WindFormat)}\","); - json.Append($"\"{m}-highGustTimeDayfile\":\"{highGust[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highWindValDayfile\":\"{highWind[i].GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"{m}-highWindTimeDayfile\":\"{highWind[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highWindRunValDayfile\":\"{highWindRun[i].GetValString(cumulus.WindRunFormat)}\","); - json.Append($"\"{m}-highWindRunTimeDayfile\":\"{highWindRun[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highRainRateValDayfile\":\"{highRainRate[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highRainRateTimeDayfile\":\"{highRainRate[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHourlyRainValDayfile\":\"{highRainHour[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highHourlyRainTimeDayfile\":\"{highRainHour[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDailyRainValDayfile\":\"{highRainDay[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highDailyRainTimeDayfile\":\"{highRainDay[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highRain24hValDayfile\":\"{highRain24h[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highRain24hTimeDayfile\":\"{highRain24h[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highMonthlyRainValDayfile\":\"{highRainMonth[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highMonthlyRainTimeDayfile\":\"{highRainMonth[i].GetTsString(monthFormat)}\","); - json.Append($"\"{m}-longestDryPeriodValDayfile\":\"{dryPeriod[i].GetValString()}\","); - json.Append($"\"{m}-longestDryPeriodTimeDayfile\":\"{dryPeriod[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-longestWetPeriodValDayfile\":\"{wetPeriod[i].GetValString()}\","); - json.Append($"\"{m}-longestWetPeriodTimeDayfile\":\"{wetPeriod[i].GetTsString(dateStampFormat)}\","); - } - json.Length--; - json.Append('}'); - - return json.ToString(); + jsonObj.Add($"{m}-highTempValDayfile", highTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highTempTimeDayfile", highTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowTempValDayfile", lowTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowTempTimeDayfile", lowTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDewPointValDayfile", highDewPt[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highDewPointTimeDayfile", highDewPt[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowDewPointValDayfile", lowDewPt[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowDewPointTimeDayfile", lowDewPt[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highApparentTempValDayfile", highAppTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highApparentTempTimeDayfile", highAppTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowApparentTempValDayfile", lowAppTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowApparentTempTimeDayfile", lowAppTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highFeelsLikeValDayfile", highFeelsLike[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highFeelsLikeTimeDayfile", highFeelsLike[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowFeelsLikeValDayfile", lowFeelsLike[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowFeelsLikeTimeDayfile", lowFeelsLike[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHumidexValDayfile", highHumidex[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highHumidexTimeDayfile", highHumidex[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowWindChillValDayfile", lowWindChill[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowWindChillTimeDayfile", lowWindChill[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHeatIndexValDayfile", highHeatInd[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highHeatIndexTimeDayfile", highHeatInd[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highMinTempValDayfile", highMinTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highMinTempTimeDayfile", highMinTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowMaxTempValDayfile", lowMaxTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowMaxTempTimeDayfile", lowMaxTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDailyTempRangeValDayfile", highTempRange[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highDailyTempRangeTimeDayfile", highTempRange[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-lowDailyTempRangeValDayfile", lowTempRange[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowDailyTempRangeTimeDayfile", lowTempRange[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highHumidityValDayfile", highHum[i].GetValString(cumulus.HumFormat)); + jsonObj.Add($"{m}-highHumidityTimeDayfile", highHum[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowHumidityValDayfile", lowHum[i].GetValString(cumulus.HumFormat)); + jsonObj.Add($"{m}-lowHumidityTimeDayfile", lowHum[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highBarometerValDayfile", highBaro[i].GetValString(cumulus.PressFormat)); + jsonObj.Add($"{m}-highBarometerTimeDayfile", highBaro[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowBarometerValDayfile", lowBaro[i].GetValString(cumulus.PressFormat)); + jsonObj.Add($"{m}-lowBarometerTimeDayfile", lowBaro[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highGustValDayfile", highGust[i].GetValString(cumulus.WindFormat)); + jsonObj.Add($"{m}-highGustTimeDayfile", highGust[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highWindValDayfile", highWind[i].GetValString(cumulus.WindAvgFormat)); + jsonObj.Add($"{m}-highWindTimeDayfile", highWind[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highWindRunValDayfile", highWindRun[i].GetValString(cumulus.WindRunFormat)); + jsonObj.Add($"{m}-highWindRunTimeDayfile", highWindRun[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highRainRateValDayfile", highRainRate[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highRainRateTimeDayfile", highRainRate[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHourlyRainValDayfile", highRainHour[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highHourlyRainTimeDayfile", highRainHour[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDailyRainValDayfile", highRainDay[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highDailyRainTimeDayfile", highRainDay[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highRain24hValDayfile", highRain24h[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highRain24hTimeDayfile", highRain24h[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highMonthlyRainValDayfile", highRainMonth[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highMonthlyRainTimeDayfile", highRainMonth[i].GetTsString(monthFormat)); + jsonObj.Add($"{m}-longestDryPeriodValDayfile", dryPeriod[i].GetValString()); + jsonObj.Add($"{m}-longestDryPeriodTimeDayfile", dryPeriod[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-longestWetPeriodValDayfile", wetPeriod[i].GetValString()); + jsonObj.Add($"{m}-longestWetPeriodTimeDayfile", wetPeriod[i].GetTsString(dateStampFormat)); + } + + return jsonObj.ToJson(); } internal string GetMonthlyRecLogFile() @@ -2158,7 +2141,6 @@ internal string GetMonthlyRecLogFile() const string dateStampFormat = "d"; const string monthFormat = "MMM yyyy"; - var json = new StringBuilder("{", 25500); var datefrom = cumulus.RecordsBeganDateTime; datefrom = new DateTime(datefrom.Year, datefrom.Month, 1, 0, 0, 0); var dateto = DateTime.Now; @@ -2627,77 +2609,76 @@ internal string GetMonthlyRecLogFile() hourRainLog.Clear(); rain24hLog.Clear(); + var jsonObj = new JsonObject(); + for (var i = 0; i < 12; i++) { var m = i + 1; - json.Append($"\"{m}-highTempValLogfile\":\"{highTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highTempTimeLogfile\":\"{highTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowTempValLogfile\":\"{lowTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowTempTimeLogfile\":\"{lowTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDewPointValLogfile\":\"{highDewPt[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highDewPointTimeLogfile\":\"{highDewPt[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowDewPointValLogfile\":\"{lowDewPt[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowDewPointTimeLogfile\":\"{lowDewPt[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highApparentTempValLogfile\":\"{highAppTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highApparentTempTimeLogfile\":\"{highAppTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowApparentTempValLogfile\":\"{lowAppTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowApparentTempTimeLogfile\":\"{lowAppTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highFeelsLikeValLogfile\":\"{highFeelsLike[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highFeelsLikeTimeLogfile\":\"{highFeelsLike[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowFeelsLikeValLogfile\":\"{lowFeelsLike[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowFeelsLikeTimeLogfile\":\"{lowFeelsLike[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHumidexValLogfile\":\"{highHumidex[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highHumidexTimeLogfile\":\"{highHumidex[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowWindChillValLogfile\":\"{lowWindChill[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowWindChillTimeLogfile\":\"{lowWindChill[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHeatIndexValLogfile\":\"{highHeatInd[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highHeatIndexTimeLogfile\":\"{highHeatInd[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highMinTempValLogfile\":\"{highMinTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highMinTempTimeLogfile\":\"{highMinTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowMaxTempValLogfile\":\"{lowMaxTemp[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowMaxTempTimeLogfile\":\"{lowMaxTemp[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDailyTempRangeValLogfile\":\"{highTempRange[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-highDailyTempRangeTimeLogfile\":\"{highTempRange[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-lowDailyTempRangeValLogfile\":\"{lowTempRange[i].GetValString(cumulus.TempFormat)}\","); - json.Append($"\"{m}-lowDailyTempRangeTimeLogfile\":\"{lowTempRange[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highHumidityValLogfile\":\"{highHum[i].GetValString(cumulus.HumFormat)}\","); - json.Append($"\"{m}-highHumidityTimeLogfile\":\"{highHum[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowHumidityValLogfile\":\"{lowHum[i].GetValString(cumulus.HumFormat)}\","); - json.Append($"\"{m}-lowHumidityTimeLogfile\":\"{lowHum[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highBarometerValLogfile\":\"{highBaro[i].GetValString(cumulus.PressFormat)}\","); - json.Append($"\"{m}-highBarometerTimeLogfile\":\"{highBaro[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-lowBarometerValLogfile\":\"{lowBaro[i].GetValString(cumulus.PressFormat)}\","); - json.Append($"\"{m}-lowBarometerTimeLogfile\":\"{lowBaro[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highGustValLogfile\":\"{highGust[i].GetValString(cumulus.WindFormat)}\","); - json.Append($"\"{m}-highGustTimeLogfile\":\"{highGust[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highWindValLogfile\":\"{highWind[i].GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"{m}-highWindTimeLogfile\":\"{highWind[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highWindRunValLogfile\":\"{highWindRun[i].GetValString(cumulus.WindRunFormat)}\","); - json.Append($"\"{m}-highWindRunTimeLogfile\":\"{highWindRun[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highRainRateValLogfile\":\"{highRainRate[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highRainRateTimeLogfile\":\"{highRainRate[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highHourlyRainValLogfile\":\"{highRainHour[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highHourlyRainTimeLogfile\":\"{highRainHour[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highDailyRainValLogfile\":\"{highRainDay[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highDailyRainTimeLogfile\":\"{highRainDay[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-highRain24hValLogfile\":\"{highRain24h[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highRain24hTimeLogfile\":\"{highRain24h[i].GetTsString(timeStampFormat)}\","); - json.Append($"\"{m}-highMonthlyRainValLogfile\":\"{highRainMonth[i].GetValString(cumulus.RainFormat)}\","); - json.Append($"\"{m}-highMonthlyRainTimeLogfile\":\"{highRainMonth[i].GetTsString(monthFormat)}\","); - json.Append($"\"{m}-longestDryPeriodValLogfile\":\"{dryPeriod[i].GetValString()}\","); - json.Append($"\"{m}-longestDryPeriodTimeLogfile\":\"{dryPeriod[i].GetTsString(dateStampFormat)}\","); - json.Append($"\"{m}-longestWetPeriodValLogfile\":\"{wetPeriod[i].GetValString()}\","); - json.Append($"\"{m}-longestWetPeriodTimeLogfile\":\"{wetPeriod[i].GetTsString(dateStampFormat)}\","); + jsonObj.Add($"{m}-highTempValLogfile", highTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highTempTimeLogfile", highTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowTempValLogfile", lowTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowTempTimeLogfile", lowTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDewPointValLogfile", highDewPt[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highDewPointTimeLogfile", highDewPt[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowDewPointValLogfile", lowDewPt[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowDewPointTimeLogfile", lowDewPt[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highApparentTempValLogfile", highAppTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highApparentTempTimeLogfile", highAppTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowApparentTempValLogfile", lowAppTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowApparentTempTimeLogfile", lowAppTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highFeelsLikeValLogfile", highFeelsLike[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highFeelsLikeTimeLogfile", highFeelsLike[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowFeelsLikeValLogfile", lowFeelsLike[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowFeelsLikeTimeLogfile", lowFeelsLike[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHumidexValLogfile", highHumidex[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highHumidexTimeLogfile", highHumidex[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowWindChillValLogfile", lowWindChill[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowWindChillTimeLogfile", lowWindChill[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHeatIndexValLogfile", highHeatInd[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highHeatIndexTimeLogfile", highHeatInd[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highMinTempValLogfile", highMinTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highMinTempTimeLogfile", highMinTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowMaxTempValLogfile", lowMaxTemp[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowMaxTempTimeLogfile", lowMaxTemp[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDailyTempRangeValLogfile", highTempRange[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-highDailyTempRangeTimeLogfile", highTempRange[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-lowDailyTempRangeValLogfile", lowTempRange[i].GetValString(cumulus.TempFormat)); + jsonObj.Add($"{m}-lowDailyTempRangeTimeLogfile", lowTempRange[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highHumidityValLogfile", highHum[i].GetValString(cumulus.HumFormat)); + jsonObj.Add($"{m}-highHumidityTimeLogfile", highHum[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowHumidityValLogfile", lowHum[i].GetValString(cumulus.HumFormat)); + jsonObj.Add($"{m}-lowHumidityTimeLogfile", lowHum[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highBarometerValLogfile", highBaro[i].GetValString(cumulus.PressFormat)); + jsonObj.Add($"{m}-highBarometerTimeLogfile", highBaro[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-lowBarometerValLogfile", lowBaro[i].GetValString(cumulus.PressFormat)); + jsonObj.Add($"{m}-lowBarometerTimeLogfile", lowBaro[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highGustValLogfile", highGust[i].GetValString(cumulus.WindFormat)); + jsonObj.Add($"{m}-highGustTimeLogfile", highGust[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highWindValLogfile", highWind[i].GetValString(cumulus.WindAvgFormat)); + jsonObj.Add($"{m}-highWindTimeLogfile", highWind[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highWindRunValLogfile", highWindRun[i].GetValString(cumulus.WindRunFormat)); + jsonObj.Add($"{m}-highWindRunTimeLogfile", highWindRun[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highRainRateValLogfile", highRainRate[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highRainRateTimeLogfile", highRainRate[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highHourlyRainValLogfile", highRainHour[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highHourlyRainTimeLogfile", highRainHour[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highDailyRainValLogfile", highRainDay[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highDailyRainTimeLogfile", highRainDay[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-highRain24hValLogfile", highRain24h[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highRain24hTimeLogfile", highRain24h[i].GetTsString(timeStampFormat)); + jsonObj.Add($"{m}-highMonthlyRainValLogfile", highRainMonth[i].GetValString(cumulus.RainFormat)); + jsonObj.Add($"{m}-highMonthlyRainTimeLogfile", highRainMonth[i].GetTsString(monthFormat)); + jsonObj.Add($"{m}-longestDryPeriodValLogfile", dryPeriod[i].GetValString()); + jsonObj.Add($"{m}-longestDryPeriodTimeLogfile", dryPeriod[i].GetTsString(dateStampFormat)); + jsonObj.Add($"{m}-longestWetPeriodValLogfile", wetPeriod[i].GetValString()); + jsonObj.Add($"{m}-longestWetPeriodTimeLogfile", wetPeriod[i].GetTsString(dateStampFormat)); } - json.Length--; - json.Append('}'); - watch.Stop(); var elapsed = watch.ElapsedMilliseconds; cumulus.LogDebugMessage($"Monthly recs editor Logfiles load = {elapsed} ms"); - return json.ToString(); + return jsonObj.ToJson(); } internal string GetThisMonthRecData() @@ -2705,72 +2686,70 @@ internal string GetThisMonthRecData() const string timeStampFormat = "g"; const string dateStampFormat = "d"; - var json = new StringBuilder("{", 1700); - // Records - Temperature - json.Append($"\"highTempVal\":\"{station.ThisMonth.HighTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highTempTime\":\"{station.ThisMonth.HighTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowTempVal\":\"{station.ThisMonth.LowTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowTempTime\":\"{station.ThisMonth.LowTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDewPointVal\":\"{station.ThisMonth.HighDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDewPointTime\":\"{station.ThisMonth.HighDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowDewPointVal\":\"{station.ThisMonth.LowDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDewPointTime\":\"{station.ThisMonth.LowDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"highApparentTempVal\":\"{station.ThisMonth.HighAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highApparentTempTime\":\"{station.ThisMonth.HighAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowApparentTempVal\":\"{station.ThisMonth.LowAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowApparentTempTime\":\"{station.ThisMonth.LowAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highFeelsLikeVal\":\"{station.ThisMonth.HighFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highFeelsLikeTime\":\"{station.ThisMonth.HighFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowFeelsLikeVal\":\"{station.ThisMonth.LowFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowFeelsLikeTime\":\"{station.ThisMonth.LowFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHumidexVal\":\"{station.ThisMonth.HighHumidex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHumidexTime\":\"{station.ThisMonth.HighHumidex.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowWindChillVal\":\"{station.ThisMonth.LowChill.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowWindChillTime\":\"{station.ThisMonth.LowChill.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHeatIndexVal\":\"{station.ThisMonth.HighHeatIndex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHeatIndexTime\":\"{station.ThisMonth.HighHeatIndex.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMinTempVal\":\"{station.ThisMonth.HighMinTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highMinTempTime\":\"{station.ThisMonth.HighMinTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowMaxTempVal\":\"{station.ThisMonth.LowMaxTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowMaxTempTime\":\"{station.ThisMonth.LowMaxTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyTempRangeVal\":\"{station.ThisMonth.HighDailyTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDailyTempRangeTime\":\"{station.ThisMonth.HighDailyTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"lowDailyTempRangeVal\":\"{station.ThisMonth.LowDailyTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDailyTempRangeTime\":\"{station.ThisMonth.LowDailyTempRange.GetTsString(dateStampFormat)}\","); - // Records - Humidity - json.Append($"\"highHumidityVal\":\"{station.ThisMonth.HighHumidity.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"highHumidityTime\":\"{station.ThisMonth.HighHumidity.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowHumidityVal\":\"{station.ThisMonth.LowHumidity.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"lowHumidityTime\":\"{station.ThisMonth.LowHumidity.GetTsString(timeStampFormat)}\","); - // Records - Pressure - json.Append($"\"highBarometerVal\":\"{station.ThisMonth.HighPress.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"highBarometerTime\":\"{station.ThisMonth.HighPress.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowBarometerVal\":\"{station.ThisMonth.LowPress.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"lowBarometerTime\":\"{station.ThisMonth.LowPress.GetTsString(timeStampFormat)}\","); - // Records - Wind - json.Append($"\"highGustVal\":\"{station.ThisMonth.HighGust.GetValString(cumulus.WindFormat)}\","); - json.Append($"\"highGustTime\":\"{station.ThisMonth.HighGust.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindVal\":\"{station.ThisMonth.HighWind.GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"highWindTime\":\"{station.ThisMonth.HighWind.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindRunVal\":\"{station.ThisMonth.HighWindRun.GetValString(cumulus.WindRunFormat)}\","); - json.Append($"\"highWindRunTime\":\"{station.ThisMonth.HighWindRun.GetTsString(dateStampFormat)}\","); - // Records - Rain - json.Append($"\"highRainRateVal\":\"{station.ThisMonth.HighRainRate.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRainRateTime\":\"{station.ThisMonth.HighRainRate.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHourlyRainVal\":\"{station.ThisMonth.HourlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highHourlyRainTime\":\"{station.ThisMonth.HourlyRain.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyRainVal\":\"{station.ThisMonth.DailyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highDailyRainTime\":\"{station.ThisMonth.DailyRain.GetTsString(dateStampFormat)}\","); - json.Append($"\"highRain24hVal\":\"{station.ThisMonth.HighRain24Hours.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRain24hTime\":\"{station.ThisMonth.HighRain24Hours.GetTsString(timeStampFormat)}\","); - json.Append($"\"longestDryPeriodVal\":\"{station.ThisMonth.LongestDryPeriod.GetValString("F0")}\","); - json.Append($"\"longestDryPeriodTime\":\"{station.ThisMonth.LongestDryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"longestWetPeriodVal\":\"{station.ThisMonth.LongestWetPeriod.GetValString("F0")}\","); - json.Append($"\"longestWetPeriodTime\":\"{station.ThisMonth.LongestWetPeriod.GetTsString(dateStampFormat)}\""); - - json.Append('}'); - - return json.ToString(); + return new JsonObject + { + // Records - Temperature + ["highTempVal"] = station.ThisMonth.HighTemp.GetValString(cumulus.TempFormat), + ["highTempTime"] = station.ThisMonth.HighTemp.GetTsString(timeStampFormat), + ["lowTempVal"] = station.ThisMonth.LowTemp.GetValString(cumulus.TempFormat), + ["lowTempTime"] = station.ThisMonth.LowTemp.GetTsString(timeStampFormat), + ["highDewPointVal"] = station.ThisMonth.HighDewPoint.GetValString(cumulus.TempFormat), + ["highDewPointTime"] = station.ThisMonth.HighDewPoint.GetTsString(timeStampFormat), + ["lowDewPointVal"] = station.ThisMonth.LowDewPoint.GetValString(cumulus.TempFormat), + ["lowDewPointTime"] = station.ThisMonth.LowDewPoint.GetTsString(timeStampFormat), + ["highApparentTempVal"] = station.ThisMonth.HighAppTemp.GetValString(cumulus.TempFormat), + ["highApparentTempTime"] = station.ThisMonth.HighAppTemp.GetTsString(timeStampFormat), + ["lowApparentTempVal"] = station.ThisMonth.LowAppTemp.GetValString(cumulus.TempFormat), + ["lowApparentTempTime"] = station.ThisMonth.LowAppTemp.GetTsString(timeStampFormat), + ["highFeelsLikeVal"] = station.ThisMonth.HighFeelsLike.GetValString(cumulus.TempFormat), + ["highFeelsLikeTime"] = station.ThisMonth.HighFeelsLike.GetTsString(timeStampFormat), + ["lowFeelsLikeVal"] = station.ThisMonth.LowFeelsLike.GetValString(cumulus.TempFormat), + ["lowFeelsLikeTime"] = station.ThisMonth.LowFeelsLike.GetTsString(timeStampFormat), + ["highHumidexVal"] = station.ThisMonth.HighHumidex.GetValString(cumulus.TempFormat), + ["highHumidexTime"] = station.ThisMonth.HighHumidex.GetTsString(timeStampFormat), + ["lowWindChillVal"] = station.ThisMonth.LowChill.GetValString(cumulus.TempFormat), + ["lowWindChillTime"] = station.ThisMonth.LowChill.GetTsString(timeStampFormat), + ["highHeatIndexVal"] = station.ThisMonth.HighHeatIndex.GetValString(cumulus.TempFormat), + ["highHeatIndexTime"] = station.ThisMonth.HighHeatIndex.GetTsString(timeStampFormat), + ["highMinTempVal"] = station.ThisMonth.HighMinTemp.GetValString(cumulus.TempFormat), + ["highMinTempTime"] = station.ThisMonth.HighMinTemp.GetTsString(timeStampFormat), + ["lowMaxTempVal"] = station.ThisMonth.LowMaxTemp.GetValString(cumulus.TempFormat), + ["lowMaxTempTime"] = station.ThisMonth.LowMaxTemp.GetTsString(timeStampFormat), + ["highDailyTempRangeVal"] = station.ThisMonth.HighDailyTempRange.GetValString(cumulus.TempFormat), + ["highDailyTempRangeTime"] = station.ThisMonth.HighDailyTempRange.GetTsString(dateStampFormat), + ["lowDailyTempRangeVal"] = station.ThisMonth.LowDailyTempRange.GetValString(cumulus.TempFormat), + ["lowDailyTempRangeTime"] = station.ThisMonth.LowDailyTempRange.GetTsString(dateStampFormat), + // Records - Humidity + ["highHumidityVal"] = station.ThisMonth.HighHumidity.GetValString(cumulus.HumFormat), + ["highHumidityTime"] = station.ThisMonth.HighHumidity.GetTsString(timeStampFormat), + ["lowHumidityVal"] = station.ThisMonth.LowHumidity.GetValString(cumulus.HumFormat), + ["lowHumidityTime"] = station.ThisMonth.LowHumidity.GetTsString(timeStampFormat), + // Records - Pressure + ["highBarometerVal"] = station.ThisMonth.HighPress.GetValString(cumulus.PressFormat), + ["highBarometerTime"] = station.ThisMonth.HighPress.GetTsString(timeStampFormat), + ["lowBarometerVal"] = station.ThisMonth.LowPress.GetValString(cumulus.PressFormat), + ["lowBarometerTime"] = station.ThisMonth.LowPress.GetTsString(timeStampFormat), + // Records - Wind + ["highGustVal"] = station.ThisMonth.HighGust.GetValString(cumulus.WindFormat), + ["highGustTime"] = station.ThisMonth.HighGust.GetTsString(timeStampFormat), + ["highWindVal"] = station.ThisMonth.HighWind.GetValString(cumulus.WindAvgFormat), + ["highWindTime"] = station.ThisMonth.HighWind.GetTsString(timeStampFormat), + ["highWindRunVal"] = station.ThisMonth.HighWindRun.GetValString(cumulus.WindRunFormat), + ["highWindRunTime"] = station.ThisMonth.HighWindRun.GetTsString(dateStampFormat), + // Records - Rain + ["highRainRateVal"] = station.ThisMonth.HighRainRate.GetValString(cumulus.RainFormat), + ["highRainRateTime"] = station.ThisMonth.HighRainRate.GetTsString(timeStampFormat), + ["highHourlyRainVal"] = station.ThisMonth.HourlyRain.GetValString(cumulus.RainFormat), + ["highHourlyRainTime"] = station.ThisMonth.HourlyRain.GetTsString(timeStampFormat), + ["highDailyRainVal"] = station.ThisMonth.DailyRain.GetValString(cumulus.RainFormat), + ["highDailyRainTime"] = station.ThisMonth.DailyRain.GetTsString(dateStampFormat), + ["highRain24hVal"] = station.ThisMonth.HighRain24Hours.GetValString(cumulus.RainFormat), + ["highRain24hTime"] = station.ThisMonth.HighRain24Hours.GetTsString(timeStampFormat), + ["longestDryPeriodVal"] = station.ThisMonth.LongestDryPeriod.GetValString("F0"), + ["longestDryPeriodTime"] = station.ThisMonth.LongestDryPeriod.GetTsString(dateStampFormat), + ["longestWetPeriodVal"] = station.ThisMonth.LongestWetPeriod.GetValString("F0"), + ["longestWetPeriodTime"] = station.ThisMonth.LongestWetPeriod.GetTsString(dateStampFormat) + }.ToJson(); } internal string EditThisMonthRecs(IHttpContext context) @@ -2976,74 +2955,71 @@ internal string GetThisYearRecData() const string dateStampFormat = "d"; const string monthFormat = "MMM yyyy"; - var json = new StringBuilder("{", 1800); - // Records - Temperature - json.Append($"\"highTempVal\":\"{station.ThisYear.HighTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highTempTime\":\"{station.ThisYear.HighTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowTempVal\":\"{station.ThisYear.LowTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowTempTime\":\"{station.ThisYear.LowTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDewPointVal\":\"{station.ThisYear.HighDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDewPointTime\":\"{station.ThisYear.HighDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowDewPointVal\":\"{station.ThisYear.LowDewPoint.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDewPointTime\":\"{station.ThisYear.LowDewPoint.GetTsString(timeStampFormat)}\","); - json.Append($"\"highApparentTempVal\":\"{station.ThisYear.HighAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highApparentTempTime\":\"{station.ThisYear.HighAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowApparentTempVal\":\"{station.ThisYear.LowAppTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowApparentTempTime\":\"{station.ThisYear.LowAppTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highFeelsLikeVal\":\"{station.ThisYear.HighFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highFeelsLikeTime\":\"{station.ThisYear.HighFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowFeelsLikeVal\":\"{station.ThisYear.LowFeelsLike.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowFeelsLikeTime\":\"{station.ThisYear.LowFeelsLike.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHumidexVal\":\"{station.ThisYear.HighHumidex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHumidexTime\":\"{station.ThisYear.HighHumidex.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowWindChillVal\":\"{station.ThisYear.LowChill.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowWindChillTime\":\"{station.ThisYear.LowChill.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHeatIndexVal\":\"{station.ThisYear.HighHeatIndex.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highHeatIndexTime\":\"{station.ThisYear.HighHeatIndex.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMinTempVal\":\"{station.ThisYear.HighMinTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highMinTempTime\":\"{station.ThisYear.HighMinTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowMaxTempVal\":\"{station.ThisYear.LowMaxTemp.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowMaxTempTime\":\"{station.ThisYear.LowMaxTemp.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyTempRangeVal\":\"{station.ThisYear.HighDailyTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"highDailyTempRangeTime\":\"{station.ThisYear.HighDailyTempRange.GetTsString(dateStampFormat)}\","); - json.Append($"\"lowDailyTempRangeVal\":\"{station.ThisYear.LowDailyTempRange.GetValString(cumulus.TempFormat)}\","); - json.Append($"\"lowDailyTempRangeTime\":\"{station.ThisYear.LowDailyTempRange.GetTsString(dateStampFormat)}\","); - // Records - Humidity - json.Append($"\"highHumidityVal\":\"{station.ThisYear.HighHumidity.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"highHumidityTime\":\"{station.ThisYear.HighHumidity.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowHumidityVal\":\"{station.ThisYear.LowHumidity.GetValString(cumulus.HumFormat)}\","); - json.Append($"\"lowHumidityTime\":\"{station.ThisYear.LowHumidity.GetTsString(timeStampFormat)}\","); - // Records - Pressure - json.Append($"\"highBarometerVal\":\"{station.ThisYear.HighPress.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"highBarometerTime\":\"{station.ThisYear.HighPress.GetTsString(timeStampFormat)}\","); - json.Append($"\"lowBarometerVal\":\"{station.ThisYear.LowPress.GetValString(cumulus.PressFormat)}\","); - json.Append($"\"lowBarometerTime\":\"{station.ThisYear.LowPress.GetTsString(timeStampFormat)}\","); - // Records - Wind - json.Append($"\"highGustVal\":\"{station.ThisYear.HighGust.GetValString(cumulus.WindFormat)}\","); - json.Append($"\"highGustTime\":\"{station.ThisYear.HighGust.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindVal\":\"{station.ThisYear.HighWind.GetValString(cumulus.WindAvgFormat)}\","); - json.Append($"\"highWindTime\":\"{station.ThisYear.HighWind.GetTsString(timeStampFormat)}\","); - json.Append($"\"highWindRunVal\":\"{station.ThisYear.HighWindRun.GetValString(cumulus.WindRunFormat)}\","); - json.Append($"\"highWindRunTime\":\"{station.ThisYear.HighWindRun.GetTsString(dateStampFormat)}\","); - // Records - Rain - json.Append($"\"highRainRateVal\":\"{station.ThisYear.HighRainRate.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRainRateTime\":\"{station.ThisYear.HighRainRate.GetTsString(timeStampFormat)}\","); - json.Append($"\"highHourlyRainVal\":\"{station.ThisYear.HourlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highHourlyRainTime\":\"{station.ThisYear.HourlyRain.GetTsString(timeStampFormat)}\","); - json.Append($"\"highDailyRainVal\":\"{station.ThisYear.DailyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highDailyRainTime\":\"{station.ThisYear.DailyRain.GetTsString(dateStampFormat)}\","); - json.Append($"\"highRain24hVal\":\"{station.ThisYear.HighRain24Hours.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highRain24hTime\":\"{station.ThisYear.HighRain24Hours.GetTsString(timeStampFormat)}\","); - json.Append($"\"highMonthlyRainVal\":\"{station.ThisYear.MonthlyRain.GetValString(cumulus.RainFormat)}\","); - json.Append($"\"highMonthlyRainTime\":\"{station.ThisYear.MonthlyRain.GetTsString(monthFormat)}\","); - json.Append($"\"longestDryPeriodVal\":\"{station.ThisYear.LongestDryPeriod.GetValString("F0")}\","); - json.Append($"\"longestDryPeriodTime\":\"{station.ThisYear.LongestDryPeriod.GetTsString(dateStampFormat)}\","); - json.Append($"\"longestWetPeriodVal\":\"{station.ThisYear.LongestWetPeriod.GetValString("F0")}\","); - json.Append($"\"longestWetPeriodTime\":\"{station.ThisYear.LongestWetPeriod.GetTsString(dateStampFormat)}\""); - - json.Append('}'); - - return json.ToString(); + return new JsonObject + { + ["highTempVal"] = station.ThisYear.HighTemp.GetValString(cumulus.TempFormat), + ["highTempTime"] = station.ThisYear.HighTemp.GetTsString(timeStampFormat), + ["lowTempVal"] = station.ThisYear.LowTemp.GetValString(cumulus.TempFormat), + ["lowTempTime"] = station.ThisYear.LowTemp.GetTsString(timeStampFormat), + ["highDewPointVal"] = station.ThisYear.HighDewPoint.GetValString(cumulus.TempFormat), + ["highDewPointTime"] = station.ThisYear.HighDewPoint.GetTsString(timeStampFormat), + ["lowDewPointVal"] = station.ThisYear.LowDewPoint.GetValString(cumulus.TempFormat), + ["lowDewPointTime"] = station.ThisYear.LowDewPoint.GetTsString(timeStampFormat), + ["highApparentTempVal"] = station.ThisYear.HighAppTemp.GetValString(cumulus.TempFormat), + ["highApparentTempTime"] = station.ThisYear.HighAppTemp.GetTsString(timeStampFormat), + ["lowApparentTempVal"] = station.ThisYear.LowAppTemp.GetValString(cumulus.TempFormat), + ["lowApparentTempTime"] = station.ThisYear.LowAppTemp.GetTsString(timeStampFormat), + ["highFeelsLikeVal"] = station.ThisYear.HighFeelsLike.GetValString(cumulus.TempFormat), + ["highFeelsLikeTime"] = station.ThisYear.HighFeelsLike.GetTsString(timeStampFormat), + ["lowFeelsLikeVal"] = station.ThisYear.LowFeelsLike.GetValString(cumulus.TempFormat), + ["lowFeelsLikeTime"] = station.ThisYear.LowFeelsLike.GetTsString(timeStampFormat), + ["highHumidexVal"] = station.ThisYear.HighHumidex.GetValString(cumulus.TempFormat), + ["highHumidexTime"] = station.ThisYear.HighHumidex.GetTsString(timeStampFormat), + ["lowWindChillVal"] = station.ThisYear.LowChill.GetValString(cumulus.TempFormat), + ["lowWindChillTime"] = station.ThisYear.LowChill.GetTsString(timeStampFormat), + ["highHeatIndexVal"] = station.ThisYear.HighHeatIndex.GetValString(cumulus.TempFormat), + ["highHeatIndexTime"] = station.ThisYear.HighHeatIndex.GetTsString(timeStampFormat), + ["highMinTempVal"] = station.ThisYear.HighMinTemp.GetValString(cumulus.TempFormat), + ["highMinTempTime"] = station.ThisYear.HighMinTemp.GetTsString(timeStampFormat), + ["lowMaxTempVal"] = station.ThisYear.LowMaxTemp.GetValString(cumulus.TempFormat), + ["lowMaxTempTime"] = station.ThisYear.LowMaxTemp.GetTsString(timeStampFormat), + ["highDailyTempRangeVal"] = station.ThisYear.HighDailyTempRange.GetValString(cumulus.TempFormat), + ["highDailyTempRangeTime"] = station.ThisYear.HighDailyTempRange.GetTsString(dateStampFormat), + ["lowDailyTempRangeVal"] = station.ThisYear.LowDailyTempRange.GetValString(cumulus.TempFormat), + ["lowDailyTempRangeTime"] = station.ThisYear.LowDailyTempRange.GetTsString(dateStampFormat), + // Records - Humidity + ["highHumidityVal"] = station.ThisYear.HighHumidity.GetValString(cumulus.HumFormat), + ["highHumidityTime"] = station.ThisYear.HighHumidity.GetTsString(timeStampFormat), + ["lowHumidityVal"] = station.ThisYear.LowHumidity.GetValString(cumulus.HumFormat), + ["lowHumidityTime"] = station.ThisYear.LowHumidity.GetTsString(timeStampFormat), + // Records - Pressure + ["highBarometerVal"] = station.ThisYear.HighPress.GetValString(cumulus.PressFormat), + ["highBarometerTime"] = station.ThisYear.HighPress.GetTsString(timeStampFormat), + ["lowBarometerVal"] = station.ThisYear.LowPress.GetValString(cumulus.PressFormat), + ["lowBarometerTime"] = station.ThisYear.LowPress.GetTsString(timeStampFormat), + // Records - Wind + ["highGustVal"] = station.ThisYear.HighGust.GetValString(cumulus.WindFormat), + ["highGustTime"] = station.ThisYear.HighGust.GetTsString(timeStampFormat), + ["highWindVal"] = station.ThisYear.HighWind.GetValString(cumulus.WindAvgFormat), + ["highWindTime"] = station.ThisYear.HighWind.GetTsString(timeStampFormat), + ["highWindRunVal"] = station.ThisYear.HighWindRun.GetValString(cumulus.WindRunFormat), + ["highWindRunTime"] = station.ThisYear.HighWindRun.GetTsString(dateStampFormat), + // Records - Rain + ["highRainRateVal"] = station.ThisYear.HighRainRate.GetValString(cumulus.RainFormat), + ["highRainRateTime"] = station.ThisYear.HighRainRate.GetTsString(timeStampFormat), + ["highHourlyRainVal"] = station.ThisYear.HourlyRain.GetValString(cumulus.RainFormat), + ["highHourlyRainTime"] = station.ThisYear.HourlyRain.GetTsString(timeStampFormat), + ["highDailyRainVal"] = station.ThisYear.DailyRain.GetValString(cumulus.RainFormat), + ["highDailyRainTime"] = station.ThisYear.DailyRain.GetTsString(dateStampFormat), + ["highRain24hVal"] = station.ThisYear.HighRain24Hours.GetValString(cumulus.RainFormat), + ["highRain24hTime"] = station.ThisYear.HighRain24Hours.GetTsString(timeStampFormat), + ["highMonthlyRainVal"] = station.ThisYear.MonthlyRain.GetValString(cumulus.RainFormat), + ["highMonthlyRainTime"] = station.ThisYear.MonthlyRain.GetTsString(monthFormat), + ["longestDryPeriodVal"] = station.ThisYear.LongestDryPeriod.GetValString("F0"), + ["longestDryPeriodTime"] = station.ThisYear.LongestDryPeriod.GetTsString(dateStampFormat), + ["longestWetPeriodVal"] = station.ThisYear.LongestWetPeriod.GetValString("F0"), + ["longestWetPeriodTime"] = station.ThisYear.LongestWetPeriod.GetTsString(dateStampFormat) + }.ToJson(); } internal string EditThisYearRecs(IHttpContext context) diff --git a/CumulusMX/NOAA.cs b/CumulusMX/NOAA.cs index 95ad4f62..e7f17da3 100644 --- a/CumulusMX/NOAA.cs +++ b/CumulusMX/NOAA.cs @@ -161,6 +161,11 @@ private double GetAverageWindSpeed(int month, int year, out int domdir) totalwinddirY += (windspeed * Math.Cos((winddir * (Math.PI / 180)))); } } + catch (IOException ex) + { + cumulus.LogErrorMessage($"Error reading log file {logFile}: {ex.Message}"); + cumulus.LogMessage("Please check the file for errors"); + } catch (Exception e) { cumulus.LogErrorMessage($"Error at line {linenum}, column {idx}, value '{(st.Count >= idx ? st[idx] : "")}' of {logFile} : {e}"); From f24d3c1cbe6af3e87c806a5c80b5cdba6e2f9efe Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 2 Nov 2023 22:19:29 +0000 Subject: [PATCH 21/45] Add wind rose data to AddValuesToRecentWind() --- CumulusMX/DavisStation.cs | 11 ++++------- CumulusMX/WeatherStation.cs | 33 +++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index ce12d255..2074f072 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -2505,13 +2505,10 @@ private void GetArchiveData() if (archiveData.HiWindSpeed < 250 && archiveData.AvgWindSpeed < 250) { int bearing = archiveData.WindDirection; - if (bearing == 255) - { - bearing = 0; - } + bearing = bearing == 255 ? 0 : (int) (bearing * 22.5); - AddValuesToRecentWind(avgwind, avgwind, timestamp.AddMinutes(-interval), timestamp); - DoWind(wind, (int) (bearing * 22.5), avgwind, timestamp); + AddValuesToRecentWind(avgwind, avgwind, bearing, timestamp.AddMinutes(-interval), timestamp); + DoWind(wind, bearing, avgwind, timestamp); if (ConvertUserWindToMS(WindAverage) < 1.5) { @@ -2524,7 +2521,7 @@ private void GetArchiveData() } // update dominant wind bearing - CalculateDominantWindBearing((int) (bearing * 22.5), WindAverage, interval); + CalculateDominantWindBearing(bearing, WindAverage, interval); } DoApparentTemp(timestamp); diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 110e7cce..01f2a23a 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -6166,7 +6166,7 @@ private string TimeToStrHHMM(DateTime timestamp) public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime timestamp) { cumulus.LogDebugMessage($"DoWind: latest={gustpar:F1}, speed={speedpar:F1} - Current: gust={RecentMaxGust:F1}, speed={WindAverage:F1}"); - + // if we have a spike in wind speed or gust, ignore the reading // Spike removal is in m/s var windGustMS = ConvertUserWindToMS(gustpar); var windAvgMS = speedpar == -1 ? previousWind : ConvertUserWindToMS(speedpar); @@ -6449,16 +6449,41 @@ public void InitialiseWind() cumulus.LogDebugMessage($"InitialiseWind: gust={RecentMaxGust:F1}, speed={WindAverage:F1}"); } - public void AddValuesToRecentWind(double gust, double speed, DateTime start, DateTime end) + public void AddValuesToRecentWind(double gust, double speed, int bearing, DateTime start, DateTime end) { - for (DateTime ts = start; ts <= end; ts = ts.AddSeconds(3)) + var calGust = cumulus.Calib.WindGust.Calibrate(gust); + int calBearing; + + // use bearing of zero when calm + if ((Math.Abs(gust) < 0.001) && cumulus.StationOptions.UseZeroBearing) { - nextwindvalue = (nextwindvalue + 1) % maxwindvalues; + calBearing = 0; + } + else + { + calBearing = (bearing + (int) cumulus.Calib.WindDir.Offset) % 360; + if (calBearing < 0) + { + calBearing = 360 + calBearing; + } + if (calBearing == 0) + { + calBearing = 360; + } + } + + + for (DateTime ts = start; ts <= end; ts = ts.AddSeconds(3)) + { WindRecent[nextwind].Gust = gust; WindRecent[nextwind].Speed = speed; WindRecent[nextwind].Timestamp = ts; nextwind = (nextwind + 1) % MaxWindRecent; + + windspeeds[nextwindvalue] = calibratedgust; + windbears[nextwindvalue] = calBearing; + nextwindvalue = (nextwindvalue + 1) % maxwindvalues; } } From 687c9c34b192b7e6d15f5389cdd1cdb0db1c23dd Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 2 Nov 2023 22:29:44 +0000 Subject: [PATCH 22/45] Bits'n'bobs tidied --- CumulusMX/Api.cs | 6 +++--- CumulusMX/Cumulus.cs | 8 ++++---- CumulusMX/DavisCloudStation.cs | 2 +- CumulusMX/DavisStation.cs | 2 +- CumulusMX/DavisWllStation.cs | 2 +- CumulusMX/EcowittCloudStation.cs | 2 +- CumulusMX/ProgramSettings.cs | 2 +- CumulusMX/WeatherStation.cs | 10 +++++----- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/CumulusMX/Api.cs b/CumulusMX/Api.cs index 5900203d..51874967 100644 --- a/CumulusMX/Api.cs +++ b/CumulusMX/Api.cs @@ -488,13 +488,13 @@ public async Task GetGraphData(string req) await writer.WriteAsync(Station.GetIntervalTempGraphData(true, start, end)); break; case "intvwind.json": - await writer.WriteAsync(Station.GetIntervalWindGraphData(true, start, end)); + await writer.WriteAsync(Station.GetIntervalWindGraphData(start, end)); break; case "intvrain.json": - await writer.WriteAsync(Station.GetIntervalRainGraphData(true, start, end)); + await writer.WriteAsync(Station.GetIntervalRainGraphData(start, end)); break; case "intvpress.json": - await writer.WriteAsync(Station.GetIntervaPressGraphData(true, start, end)); + await writer.WriteAsync(Station.GetIntervalPressGraphData(start, end)); break; case "intvhum.json": await writer.WriteAsync(Station.GetIntervalHumGraphData(true, start, end)); diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 69b25482..def5a15d 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -1066,7 +1066,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) if (FtpOptions.Logging) { - CreateFtpLogFile(ftpLogfile); + CreateFtpLogFile(); } ListSeparator = CultureInfo.CurrentCulture.TextInfo.ListSeparator; @@ -4203,13 +4203,13 @@ public void RotateLogFiles() var oldfile = ftpLogfile; ftpLogfile = RemoveOldDiagsFiles("FTP"); LogFtpMessage("Rotating FTP log file, new log file will be: " + ftpLogfile.Split(DirectorySeparator).Last()); - CreateFtpLogFile(ftpLogfile); + CreateFtpLogFile(); LogFtpMessage("Rotated FTP log file, old log file was: " + oldfile.Split(DirectorySeparator).Last()); } } } - public void CreateFtpLogFile(string filename) + public void CreateFtpLogFile() { if (FtpTraceListener != null) { @@ -11782,7 +11782,7 @@ public void LogFluentFtpMessage(FtpTraceLevel level, string message) // Let's try closing and the existing log file and reopening LogDebugMessage($"LogFluentFtpMessage: Error = {ex.Message}"); ftpLogfile = RemoveOldDiagsFiles("FTP"); - CreateFtpLogFile(ftpLogfile); + CreateFtpLogFile(); } } } diff --git a/CumulusMX/DavisCloudStation.cs b/CumulusMX/DavisCloudStation.cs index 532847af..a139f972 100644 --- a/CumulusMX/DavisCloudStation.cs +++ b/CumulusMX/DavisCloudStation.cs @@ -2624,7 +2624,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json, bool curr var dir = data.wind_speed_hi_dir ?? 0; cumulus.LogDebugMessage($"WL.com historic: using wind data from TxId {data.tx_id}"); DoWind(gust, dir, spd, lastRecordTime); - AddValuesToRecentWind(spd, spd, lastRecordTime.AddSeconds(-data.arch_int), lastRecordTime); + AddValuesToRecentWind(spd, spd, dir, lastRecordTime.AddSeconds(-data.arch_int), lastRecordTime); } else { diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index 2074f072..c988092c 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -2912,7 +2912,7 @@ private bool WakeVP(TcpClient thePort, bool force = false) int retryCount = 0; // Check if we haven't sent a command within the last two minutes - use 1:50 () to be safe - if (awakeStopWatch.IsRunning && awakeStopWatch.ElapsedMilliseconds < 110000) + if (awakeStopWatch.IsRunning && awakeStopWatch.ElapsedMilliseconds < 110000 && !force) { cumulus.LogDebugMessage("WakeVP: Not required"); awakeStopWatch.Restart(); diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs index 082b1ac6..27aaf6c5 100644 --- a/CumulusMX/DavisWllStation.cs +++ b/CumulusMX/DavisWllStation.cs @@ -2142,7 +2142,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) var dir = data11.wind_speed_hi_dir ?? 0; cumulus.LogDebugMessage($"WL.com historic: using wind data from TxId {data11.tx_id}"); DoWind(gust, dir, spd, recordTs); - AddValuesToRecentWind(spd, spd, recordTs.AddSeconds(-data11.arch_int), recordTs); + AddValuesToRecentWind(spd, spd, dir, recordTs.AddSeconds(-data11.arch_int), recordTs); } else { diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs index 595b38d5..a5a8867b 100644 --- a/CumulusMX/EcowittCloudStation.cs +++ b/CumulusMX/EcowittCloudStation.cs @@ -8,7 +8,7 @@ namespace CumulusMX internal class EcowittCloudStation : WeatherStation { private readonly WeatherStation station; - private EcowittApi ecowittApi; + private readonly EcowittApi ecowittApi; private int maxArchiveRuns = 1; private Task liveTask; private readonly bool main; diff --git a/CumulusMX/ProgramSettings.cs b/CumulusMX/ProgramSettings.cs index 3559614d..21b4ef1a 100644 --- a/CumulusMX/ProgramSettings.cs +++ b/CumulusMX/ProgramSettings.cs @@ -196,7 +196,7 @@ public string UpdateConfig(IHttpContext context) if (settings.logging.ftplogging) { cumulus.ftpLogfile = cumulus.RemoveOldDiagsFiles("FTP"); - cumulus.CreateFtpLogFile(cumulus.ftpLogfile); + cumulus.CreateFtpLogFile(); } cumulus.FtpOptions.Logging = settings.logging.ftplogging; } diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 01f2a23a..e854dc20 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -502,7 +502,7 @@ private void CheckSqliteDatabase(bool giveup) public void ReloadFailedMySQLCommands() { - while (cumulus.MySqlFailedList.TryDequeue(out var tmp)) + while (cumulus.MySqlFailedList.TryDequeue(out var _)) { // do nothing }; @@ -4479,7 +4479,7 @@ public string GetIntervalSolarGraphData(bool local, DateTime? start = null, Date } - public string GetIntervaPressGraphData(bool local, DateTime? start = null, DateTime? end = null) + public string GetIntervalPressGraphData(DateTime? start = null, DateTime? end = null) { var InvC = new CultureInfo(""); StringBuilder sb = new StringBuilder("{\"press\":["); @@ -4556,7 +4556,7 @@ public string GetIntervaPressGraphData(bool local, DateTime? start = null, DateT } - public string GetIntervalWindGraphData(bool local, DateTime? start = null, DateTime? end = null) + public string GetIntervalWindGraphData(DateTime? start = null, DateTime? end = null) { var InvC = new CultureInfo(""); var sb = new StringBuilder("{\"wgust\":["); @@ -4656,7 +4656,7 @@ public string GetIntervalWindGraphData(bool local, DateTime? start = null, DateT return sb.ToString(); } - public string GetIntervalRainGraphData(bool local, DateTime? start = null, DateTime? end = null) + public string GetIntervalRainGraphData(DateTime? start = null, DateTime? end = null) { var InvC = new CultureInfo(""); var sb = new StringBuilder("{"); @@ -6481,7 +6481,7 @@ public void AddValuesToRecentWind(double gust, double speed, int bearing, DateTi WindRecent[nextwind].Timestamp = ts; nextwind = (nextwind + 1) % MaxWindRecent; - windspeeds[nextwindvalue] = calibratedgust; + windspeeds[nextwindvalue] = calGust; windbears[nextwindvalue] = calBearing; nextwindvalue = (nextwindvalue + 1) % maxwindvalues; } From 8bd61d293dfe99a9d1424be649458f28f8c94a77 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Fri, 3 Nov 2023 16:31:30 +0000 Subject: [PATCH 23/45] New web tag for monthly wind run total <#windrunmonth> --- CumulusMX/WeatherStation.cs | 7 +++++++ CumulusMX/webtags.cs | 15 +++++++++++++++ Updates.txt | 6 +++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index e854dc20..25c630e4 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -1370,6 +1370,13 @@ public void UpdateDegreeDays(int interval) /// public double WindRunToday { get; set; } = 0; + public double GetWindRunMonth(int year, int month) + { + var startDate = new DateTime(year, month, 1); + var enddate = startDate.AddMonths(1); + return DayFile.Where(r => r.Date >= startDate && r.Date < enddate).Sum(r => r.WindRun); + } + /// /// Extra Temps /// diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 855c2122..96abc74d 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -517,6 +517,20 @@ private string Tagwindrun(Dictionary tagParams) return CheckRcDp(CheckWindRunUnit(station.WindRunToday, tagParams), tagParams, cumulus.WindRunDPlaces); } + private string Tagwindrunmonth(Dictionary tagParams) + { + if (!int.TryParse(tagParams.Get("year"), out int year)) + { + year = DateTime.Now.Year; + } + if (!int.TryParse(tagParams.Get("month"), out int month)) + { + month = DateTime.Now.Month; + } + + return CheckRcDp(CheckWindRunUnit(station.GetWindRunMonth(year, month), tagParams), tagParams, cumulus.WindRunDPlaces); + } + private string Tagwspeed(Dictionary tagParams) { return CheckRcDp(CheckWindUnit(station.WindAverage, tagParams), tagParams, cumulus.WindAvgDPlaces); @@ -5685,6 +5699,7 @@ public void InitialiseWebtags() { "domwinddirY", TagdomwinddirY }, { "tomorrowdaylength", Tagtomorrowdaylength }, { "windrun", Tagwindrun }, + { "windrunmonth", Tagwindrunmonth }, { "domwindbearing", Tagdomwindbearing }, { "domwinddir", Tagdomwinddir }, { "heatdegdays", Tagheatdegdays }, diff --git a/Updates.txt b/Updates.txt index caf392f6..c208353f 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,7 +1,11 @@ 3.27.1 - b3259 —————————————— New -- Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: #LatestErrorEnc, #LatestErrorJsEnc +- Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: <#LatestErrorEnc>, <#LatestErrorJsEnc> +- New web tag for monthly wind run total <#windrunmonth> + - Without parameters it returns the total for the current month so far (excluding today) + - You can add parameters "year" and "month" to return the value for a particular month. Eg. <#windrunmonth year=2022 month=3> + - Like the existing <#windrun> it also accepts the "unit" parameter to return the value in miles, kilometers, or nautical miles Changed - All third-party upload issues are now classified as Warnings From f1cd64e5e349e07b981dd808c0a1ae28fb7e3d35 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sat, 4 Nov 2023 09:38:31 +0000 Subject: [PATCH 24/45] Fix for error 500 in Wizrd with Davis cloud stations --- CumulusMX/Wizard.cs | 50 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/CumulusMX/Wizard.cs b/CumulusMX/Wizard.cs index cd8646fc..e4995ba7 100644 --- a/CumulusMX/Wizard.cs +++ b/CumulusMX/Wizard.cs @@ -82,6 +82,25 @@ public string GetAlpacaFormData() } }; + var daviscloud = new JsonWizardDavisWll() + { + api = new JsonStationSettingsWLLApi() + { + apiKey = cumulus.WllApiKey, + apiSecret = cumulus.WllApiSecret, + apiStationId = cumulus.WllStationId + }, + primary = new JsonStationSettingsWllPrimary() + { + wind = cumulus.WllPrimaryWind, + temphum = cumulus.WllPrimaryTempHum, + rain = cumulus.WllPrimaryRain, + solar = cumulus.WllPrimarySolar, + uv = cumulus.WllPrimaryUV + } + }; + + var weatherflow = new JsonStationSettingsWeatherFlow() { deviceid = cumulus.WeatherFlowOptions.WFDeviceId, tcpport = cumulus.WeatherFlowOptions.WFTcpPort, token = cumulus.WeatherFlowOptions.WFToken, dayshistory = cumulus.WeatherFlowOptions.WFDaysHist }; @@ -128,6 +147,7 @@ public string GetAlpacaFormData() stationmodel = cumulus.StationModel, davisvp2 = davisvp, daviswll = daviswll, + daviscloud = daviscloud, gw1000 = gw1000, fineoffset = fineoffset, easyw = easyweather, @@ -501,7 +521,6 @@ public string UpdateConfig(IHttpContext context) cumulus.WllPrimaryTempHum = settings.station.daviswll.primary.temphum; cumulus.WllPrimaryUV = settings.station.daviswll.primary.uv; cumulus.WllPrimaryWind = settings.station.daviswll.primary.wind; - } } catch (Exception ex) @@ -512,6 +531,34 @@ public string UpdateConfig(IHttpContext context) context.Response.StatusCode = 500; } + // Davis Cloud + try + { + if (settings.station.daviscloud != null) + { + cumulus.WllApiKey = string.IsNullOrWhiteSpace(settings.station.daviscloud.api.apiKey) ? string.Empty : settings.station.daviscloud.api.apiKey.Trim(); + cumulus.WllApiSecret = string.IsNullOrWhiteSpace(settings.station.daviscloud.api.apiSecret) ? string.Empty : settings.station.daviscloud.api.apiSecret.Trim(); + cumulus.WllStationId = settings.station.daviscloud.api.apiStationId; + + if (settings.station.daviscloud.primary != null) + { + cumulus.WllPrimaryRain = settings.station.daviscloud.primary.rain; + cumulus.WllPrimarySolar = settings.station.daviscloud.primary.solar; + cumulus.WllPrimaryTempHum = settings.station.daviscloud.primary.temphum; + cumulus.WllPrimaryUV = settings.station.daviscloud.primary.uv; + cumulus.WllPrimaryWind = settings.station.daviscloud.primary.wind; + } + } + } + catch (Exception ex) + { + var msg = "Error processing davis cloud settings: " + ex.Message; + cumulus.LogErrorMessage(msg); + errorMsg += msg + "\n\n"; + context.Response.StatusCode = 500; + } + + // GW1000 connection details try { @@ -707,6 +754,7 @@ internal class JsonWizardStation public string stationmodel { get; set; } public JsonWizardDavisVp davisvp2 { get; set; } public JsonWizardDavisWll daviswll { get; set; } + public JsonWizardDavisWll daviscloud { get; set; } public JsonStationSettingsGw1000Conn gw1000 { get; set; } public JsonWizardFineOffset fineoffset { get; set; } public JsonWizardEasyWeather easyw { get; set; } From 9abc671cb8cd255f1f38ecf9d62695ebff9697f8 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sat, 4 Nov 2023 09:39:49 +0000 Subject: [PATCH 25/45] Updates.txt --- Updates.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Updates.txt b/Updates.txt index c208353f..7f2f6155 100644 --- a/Updates.txt +++ b/Updates.txt @@ -23,6 +23,7 @@ Fixed - Some new record alarms not setting the email message correctly - Fix for wind spike testing applying unit conversion multiple times in some circumstances - Davis VP2, if requested changing the logger interval is now done after reading catch-up data so the archive is not cleared before catch-up +- Fix error 500 in the config Wizard for davis Cloud stations 3.27.0 - b3257 From 575ed933240bf461cd080364646ce62f5fd58183 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sat, 4 Nov 2023 10:51:05 +0000 Subject: [PATCH 26/45] Updates.txt --- Updates.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Updates.txt b/Updates.txt index 7f2f6155..c15c8184 100644 --- a/Updates.txt +++ b/Updates.txt @@ -24,6 +24,7 @@ Fixed - Fix for wind spike testing applying unit conversion multiple times in some circumstances - Davis VP2, if requested changing the logger interval is now done after reading catch-up data so the archive is not cleared before catch-up - Fix error 500 in the config Wizard for davis Cloud stations +- Fix Wizard and Settings screens did not allow the entry of an IMEI number for the Ecowitt Cloud API device ID 3.27.0 - b3257 From 9568799399b8f91f5d9bd75934bdf03493c9c2c1 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sat, 4 Nov 2023 20:34:19 +0000 Subject: [PATCH 27/45] Improvement to LogExceptionMessage() Improve SendEmail() exception message --- CumulusMX/Cumulus.cs | 2 +- CumulusMX/CumulusMX.csproj | 4 ++-- CumulusMX/EmailSender.cs | 4 ++-- Updates.txt | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index def5a15d..357b7f9f 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -11813,7 +11813,7 @@ public void LogExceptionMessage(Exception ex, string message) { _ = ErrorList.Dequeue(); } - ErrorList.Enqueue((DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss - ") + message)); + ErrorList.Enqueue((DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss - ") + message + " - " + ex.Message)); } /* diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index d0f5cb67..74ef09f5 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -52,13 +52,13 @@ - + - + diff --git a/CumulusMX/EmailSender.cs b/CumulusMX/EmailSender.cs index 1edf627b..a3d999c6 100644 --- a/CumulusMX/EmailSender.cs +++ b/CumulusMX/EmailSender.cs @@ -88,9 +88,9 @@ public async Task SendEmail(string[] to, string from, string subject, stri } retVal = true; } - catch (Exception e) + catch (Exception ex) { - cumulus.LogErrorMessage("SendEmail: Error - " + e); + cumulus.LogExceptionMessage(ex, "SendEmail: Error"); } finally { diff --git a/Updates.txt b/Updates.txt index c15c8184..65dfed32 100644 --- a/Updates.txt +++ b/Updates.txt @@ -9,6 +9,7 @@ New Changed - All third-party upload issues are now classified as Warnings +- Third party components for FTP(S) and SFTP updated Fixed - "Object is not intialized" error from the API when the station is not yet ready From b839df5c63615c9819566df5d181310ddf3d9965 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sun, 5 Nov 2023 22:42:25 +0000 Subject: [PATCH 28/45] Ecowitt Cloud $G stations use different API calls with imei= rather than mac= --- CumulusMX/CumulusMX.csproj | 2 +- CumulusMX/EcowittApi.cs | 22 ++++++++++++++++++---- CumulusMX/EcowittCloudStation.cs | 8 ++------ 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index 74ef09f5..788761a8 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -77,7 +77,7 @@ CumulusMX.Program AnyCPU Copyright © 2015-2023 Cumulus MX - 3.27.1.3259 + 3.27.1.3260 diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index b1ea15dc..1757b181 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -76,7 +76,14 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation sb.Append($"application_key={cumulus.EcowittApplicationKey}"); sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); - sb.Append($"&mac={cumulus.EcowittMacAddress}"); + if (ulong.TryParse(cumulus.EcowittMacAddress, out _)) + { + sb.Append($"&imei={cumulus.EcowittMacAddress}"); + } + else + { + sb.Append($"&mac={cumulus.EcowittMacAddress}"); + } sb.Append($"&start_date={apiStartDate:yyyy-MM-dd'%20'HH:mm:ss}"); sb.Append($"&end_date={apiEndDate:yyyy-MM-dd'%20'HH:mm:ss}"); @@ -1639,7 +1646,14 @@ internal CurrentDataData GetCurrentData(CancellationToken token, ref int delay) sb.Append($"application_key={cumulus.EcowittApplicationKey}"); sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); - sb.Append($"&mac={cumulus.EcowittMacAddress}"); + if (ulong.TryParse(cumulus.EcowittMacAddress, out _)) + { + sb.Append($"&imei={cumulus.EcowittMacAddress}"); + } + else + { + sb.Append($"&mac={cumulus.EcowittMacAddress}"); + } // Request the data in the correct units sb.Append($"&temp_unitid={cumulus.Units.Temp + 1}"); // 1=C, 2=F @@ -1765,8 +1779,8 @@ internal CurrentDataData GetCurrentData(CancellationToken token, ref int delay) if (!token.IsCancellationRequested) { - // indoor values should always be present, so use them for teh data timestamp - var dataTime = Utils.FromUnixTime(currObj.data.indoor.temperature.time); + // pressure values should always be present, so use them for the data timestamp, if not try the outdoor temp + var dataTime = Utils.FromUnixTime(currObj.data.pressure == null ? currObj.data.outdoor.temperature.time : currObj.data.pressure.absolute.time); cumulus.LogDebugMessage($"EcowittCloud: Last data update {dataTime:s}"); if (dataTime != LastCurrentDataTime) diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs index a5a8867b..32c39fcc 100644 --- a/CumulusMX/EcowittCloudStation.cs +++ b/CumulusMX/EcowittCloudStation.cs @@ -22,7 +22,7 @@ public EcowittCloudStation(Cumulus cumulus, WeatherStation station = null) : bas if (main) { - cumulus.LogMessage("Creating Ecowitt Cloud"); + cumulus.LogMessage("Creating Ecowitt Cloud Station"); } else { @@ -102,10 +102,6 @@ public EcowittCloudStation(Cumulus cumulus, WeatherStation station = null) : bas { Task.Run(getAndProcessHistoryData); } - else - { - cumulus.LogMessage("Extra Sensors - Ecowitt Cloud"); - } } public override void Start() @@ -491,7 +487,7 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok cumulus.BatteryLowAlarm.Triggered = batteryLow; - var updateTime = Utils.FromUnixTime(data.outdoor == null ? data.indoor.temperature.time : data.indoor.temperature.time); + var updateTime = Utils.FromUnixTime(data.pressure == null ? data.outdoor.temperature.time : data.pressure.absolute.time); thisStation.UpdateStatusPanel(updateTime); thisStation.UpdateMQTT(); From 1914391ea214a6f7fcbdc37fe54f2cb19fc40656 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sun, 5 Nov 2023 22:43:10 +0000 Subject: [PATCH 29/45] Updates.txt --- Updates.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Updates.txt b/Updates.txt index 65dfed32..c692c395 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,4 @@ -3.27.1 - b3259 +3.27.1 - b3260 —————————————— New - Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: <#LatestErrorEnc>, <#LatestErrorJsEnc> @@ -24,7 +24,7 @@ Fixed - Some new record alarms not setting the email message correctly - Fix for wind spike testing applying unit conversion multiple times in some circumstances - Davis VP2, if requested changing the logger interval is now done after reading catch-up data so the archive is not cleared before catch-up -- Fix error 500 in the config Wizard for davis Cloud stations +- Fix error 500 in the config Wizard for Davis Cloud stations - Fix Wizard and Settings screens did not allow the entry of an IMEI number for the Ecowitt Cloud API device ID From 654cda235af4ca928b3c72694e2aa4ffe49edc4e Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Tue, 7 Nov 2023 12:25:25 +0000 Subject: [PATCH 30/45] - The records editors now set both value and date/time when you click on the record from the dayfile for monthly logs - The records editors now accept y/n key presses to update records or cancel --- CumulusMX/Cumulus.cs | 32 +- CumulusMX/DataEditor.cs | 906 ++++++++++++++-------------------------- Updates.txt | 4 + 3 files changed, 349 insertions(+), 593 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 357b7f9f..c24d36bc 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -4225,6 +4225,7 @@ private void ReadIniFile() var DavisBaudRates = new List { 1200, 2400, 4800, 9600, 14400, 19200 }; ImetOptions.BaudRates = new List { 19200, 115200 }; var rewriteRequired = false; // Do we need to re-save the ini file after migration processing or resetting options? + var recreateRequired = false; // Do we need to wipe the file to remove old entries? LogMessage("Reading Cumulus.ini file"); //DateTimeToString(LongDate, "ddddd", Now); @@ -4542,7 +4543,7 @@ private void ReadIniFile() try { RecordsBeganDateTime = DateTime.Parse(RecordsBeganDate); - rewriteRequired = true; + recreateRequired = true; } catch (Exception ex) { @@ -4756,7 +4757,7 @@ private void ReadIniFile() else { AirLinkIsNode = ini.GetValue("AirLink", "In-IsNode", false) || ini.GetValue("AirLink", "Out-IsNode", false); - rewriteRequired = true; + recreateRequired = true; } AirLinkApiKey = ini.GetValue("AirLink", "WLv2ApiKey", ""); AirLinkApiSecret = ini.GetValue("AirLink", "WLv2ApiSecret", ""); @@ -5553,14 +5554,14 @@ private void ReadIniFile() { SolarOptions.RStransfactorJun = ini.GetValue("Solar", "RStransfactor", 0.8); SolarOptions.RStransfactorDec = SolarOptions.RStransfactorJun; - rewriteRequired = true; + recreateRequired = true; } else { if (ini.ValueExists("Solar", "RStransfactorJul")) { SolarOptions.RStransfactorJun = ini.GetValue("Solar", "RStransfactorJul", 0.8); - rewriteRequired = true; + recreateRequired = true; } else { @@ -5572,14 +5573,14 @@ private void ReadIniFile() { SolarOptions.BrasTurbidityJun = ini.GetValue("Solar", "BrasTurbidity", 2.0); SolarOptions.BrasTurbidityDec = SolarOptions.BrasTurbidityJun; - rewriteRequired = true; + recreateRequired = true; } else { if (ini.ValueExists("Solar", "BrasTurbidityJul")) { SolarOptions.BrasTurbidityJun = ini.GetValue("Solar", "BrasTurbidityJul", 2.0); - rewriteRequired = true; + recreateRequired = true; } else { @@ -5940,12 +5941,25 @@ private void ReadIniFile() LogMessage("Reading Cumulus.ini file completed"); - if (rewriteRequired && File.Exists("Cumulus.ini")) + if ((rewriteRequired || recreateRequired) && File.Exists("Cumulus.ini")) { LogMessage("Some values in Cumulus.ini had invalid values, or new required entries have been created."); LogMessage("Recreating Cumulus.ini to reflect the new configuration."); - LogDebugMessage("Deleting existing Cumulus.ini"); - File.Delete("Cumulus.ini"); + if (recreateRequired) + { + LogDebugMessage("Clearing existing Cumulus.ini"); + try + { + using (var fs = new FileStream("Cumulus.ini", FileMode.Truncate)) + { + // Truncate the file to zero bytes. + } + } + catch (Exception ex) + { + LogErrorMessage("Error clearing Cumulus.ini: " + ex.Message); + } + } WriteIniFile(); } } diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs index 5a4c5710..87713d16 100644 --- a/CumulusMX/DataEditor.cs +++ b/CumulusMX/DataEditor.cs @@ -632,7 +632,7 @@ internal string GetRecordsDayFile(string recordType, DateTime? start = null, Dat ["highGustValDayfile"] = highGust.GetValString(cumulus.WindFormat), ["highGustTimeDayfile"] = highGust.GetTsString(timeStampFormat), ["highWindValDayfile"] = highWindRun.GetValString(cumulus.WindRunFormat), - ["highWindTimeDayfile"] = highWindRun.GetTsString(dateStampFormat), + ["highWindTimeDayfile"] = highWindRun.GetTsString(timeStampFormat), ["highWindRunValDayfile"] = highWindRun.GetValString(cumulus.WindRunFormat), ["highWindRunTimeDayfile"] = highWindRun.GetTsString(dateStampFormat), ["highRainRateValDayfile"] = highRainRate.GetValString(cumulus.RainFormat), @@ -1220,185 +1220,104 @@ internal string EditAllTimeRecs(IHttpContext context) // Eg "name=highTempValvalue=134.6&pk=1" var newData = text.Split('&'); var field = newData[0].Split('=')[1]; - var value = newData[1].Split('=')[1]; + + var txtValue = newData[1].Split('=')[1]; + var value = double.Parse(txtValue); + + var txtTime = newData[2].Split('=')[1]; + var time = localeDateTimeStrToDate(txtTime); + try { switch (field) { - case "highTempVal": - station.SetAlltime(station.AllTime.HighTemp, double.Parse(value), station.AllTime.HighTemp.Ts); - break; - case "highTempTime": - station.SetAlltime(station.AllTime.HighTemp, station.AllTime.HighTemp.Val, localeDateTimeStrToDate(value)); - break; - case "lowTempVal": - station.SetAlltime(station.AllTime.LowTemp, double.Parse(value), station.AllTime.LowTemp.Ts); - break; - case "lowTempTime": - station.SetAlltime(station.AllTime.LowTemp, station.AllTime.LowTemp.Val, localeDateTimeStrToDate(value)); - break; - case "highDewPointVal": - station.SetAlltime(station.AllTime.HighDewPoint, double.Parse(value), station.AllTime.HighDewPoint.Ts); - break; - case "highDewPointTime": - station.SetAlltime(station.AllTime.HighDewPoint, station.AllTime.HighDewPoint.Val, localeDateTimeStrToDate(value)); - break; - case "lowDewPointVal": - station.SetAlltime(station.AllTime.LowDewPoint, double.Parse(value), station.AllTime.LowDewPoint.Ts); - break; - case "lowDewPointTime": - station.SetAlltime(station.AllTime.LowDewPoint, station.AllTime.LowDewPoint.Val, localeDateTimeStrToDate(value)); - break; - case "highApparentTempVal": - station.SetAlltime(station.AllTime.HighAppTemp, double.Parse(value), station.AllTime.HighAppTemp.Ts); - break; - case "highApparentTempTime": - station.SetAlltime(station.AllTime.HighAppTemp, station.AllTime.HighAppTemp.Val, localeDateTimeStrToDate(value)); - break; - case "lowApparentTempVal": - station.SetAlltime(station.AllTime.LowAppTemp, double.Parse(value), station.AllTime.LowAppTemp.Ts); - break; - case "lowApparentTempTime": - station.SetAlltime(station.AllTime.LowAppTemp, station.AllTime.LowAppTemp.Val, localeDateTimeStrToDate(value)); - break; - case "highFeelsLikeVal": - station.SetAlltime(station.AllTime.HighFeelsLike, double.Parse(value), station.AllTime.HighFeelsLike.Ts); - break; - case "highFeelsLikeTime": - station.SetAlltime(station.AllTime.HighFeelsLike, station.AllTime.HighFeelsLike.Val, localeDateTimeStrToDate(value)); - break; - case "lowFeelsLikeVal": - station.SetAlltime(station.AllTime.LowFeelsLike, double.Parse(value), station.AllTime.LowFeelsLike.Ts); + case "highTemp": + station.SetAlltime(station.AllTime.HighTemp, value, time); break; - case "lowFeelsLikeTime": - station.SetAlltime(station.AllTime.LowFeelsLike, station.AllTime.LowFeelsLike.Val, localeDateTimeStrToDate(value)); + case "lowTemp": + station.SetAlltime(station.AllTime.LowTemp, value, time); break; - case "highHumidexVal": - station.SetAlltime(station.AllTime.HighHumidex, double.Parse(value), station.AllTime.HighHumidex.Ts); + case "highDewPoint": + station.SetAlltime(station.AllTime.HighDewPoint, value, time); break; - case "highHumidexTime": - station.SetAlltime(station.AllTime.HighHumidex, station.AllTime.HighHumidex.Val, localeDateTimeStrToDate(value)); + case "lowDewPoint": + station.SetAlltime(station.AllTime.LowDewPoint, value, time); break; - case "lowWindChillVal": - station.SetAlltime(station.AllTime.LowChill, double.Parse(value), station.AllTime.LowChill.Ts); + case "highApparentTemp": + station.SetAlltime(station.AllTime.HighAppTemp, value, time); break; - case "lowWindChillTime": - station.SetAlltime(station.AllTime.LowChill, station.AllTime.LowChill.Val, localeDateTimeStrToDate(value)); + case "lowApparentTemp": + station.SetAlltime(station.AllTime.LowAppTemp, value, time); break; - case "highHeatIndexVal": - station.SetAlltime(station.AllTime.HighHeatIndex, double.Parse(value), station.AllTime.HighHeatIndex.Ts); + case "highFeelsLike": + station.SetAlltime(station.AllTime.HighFeelsLike, value, time); break; - case "highHeatIndexTime": - station.SetAlltime(station.AllTime.HighHeatIndex, station.AllTime.HighHeatIndex.Val, localeDateTimeStrToDate(value)); + case "lowFeelsLike": + station.SetAlltime(station.AllTime.LowFeelsLike, value, time); break; - case "highMinTempVal": - station.SetAlltime(station.AllTime.HighMinTemp, double.Parse(value), station.AllTime.HighMinTemp.Ts); + case "highHumidex": + station.SetAlltime(station.AllTime.HighHumidex, value, time); break; - case "highMinTempTime": - station.SetAlltime(station.AllTime.HighMinTemp, station.AllTime.HighMinTemp.Val, localeDateTimeStrToDate(value)); + case "lowWindChill": + station.SetAlltime(station.AllTime.LowChill, value, time); break; - case "lowMaxTempVal": - station.SetAlltime(station.AllTime.LowMaxTemp, double.Parse(value), station.AllTime.LowMaxTemp.Ts); + case "highHeatIndex": + station.SetAlltime(station.AllTime.HighHeatIndex, value, time); break; - case "lowMaxTempTime": - station.SetAlltime(station.AllTime.LowMaxTemp, station.AllTime.LowMaxTemp.Val, localeDateTimeStrToDate(value)); + case "highMinTemp": + station.SetAlltime(station.AllTime.HighMinTemp, value, time); break; - case "highDailyTempRangeVal": - station.SetAlltime(station.AllTime.HighDailyTempRange, double.Parse(value), station.AllTime.HighDailyTempRange.Ts); + case "lowMaxTemp": + station.SetAlltime(station.AllTime.LowMaxTemp, value, time); break; - case "highDailyTempRangeTime": - station.SetAlltime(station.AllTime.HighDailyTempRange, station.AllTime.HighDailyTempRange.Val, localeDateTimeStrToDate(value)); + case "highDailyTempRange": + station.SetAlltime(station.AllTime.HighDailyTempRange, value, time); break; - case "lowDailyTempRangeVal": - station.SetAlltime(station.AllTime.LowDailyTempRange, double.Parse(value), station.AllTime.LowDailyTempRange.Ts); + case "lowDailyTempRange": + station.SetAlltime(station.AllTime.LowDailyTempRange, value, time); break; - case "lowDailyTempRangeTime": - station.SetAlltime(station.AllTime.LowDailyTempRange, station.AllTime.LowDailyTempRange.Val, localeDateTimeStrToDate(value)); + case "highHumidity": + station.SetAlltime(station.AllTime.HighHumidity, int.Parse(txtValue), time); break; - case "highHumidityVal": - station.SetAlltime(station.AllTime.HighHumidity, double.Parse(value), station.AllTime.HighHumidity.Ts); + case "lowHumidity": + station.SetAlltime(station.AllTime.LowHumidity, int.Parse(txtValue), time); break; - case "highHumidityTime": - station.SetAlltime(station.AllTime.HighHumidity, station.AllTime.HighHumidity.Val, localeDateTimeStrToDate(value)); + case "highBarometer": + station.SetAlltime(station.AllTime.HighPress, value, time); break; - case "lowHumidityVal": - station.SetAlltime(station.AllTime.LowHumidity, double.Parse(value), station.AllTime.LowHumidity.Ts); + case "lowBarometer": + station.SetAlltime(station.AllTime.LowPress, value, time); break; - case "lowHumidityTime": - station.SetAlltime(station.AllTime.LowHumidity, station.AllTime.LowHumidity.Val, localeDateTimeStrToDate(value)); + case "highGust": + station.SetAlltime(station.AllTime.HighGust, value, time); break; - case "highBarometerVal": - station.SetAlltime(station.AllTime.HighPress, double.Parse(value), station.AllTime.HighPress.Ts); + case "highWind": + station.SetAlltime(station.AllTime.HighWind, value, time); break; - case "highBarometerTime": - station.SetAlltime(station.AllTime.HighPress, station.AllTime.HighPress.Val, localeDateTimeStrToDate(value)); + case "highWindRun": + station.SetAlltime(station.AllTime.HighWindRun, value, time); break; - case "lowBarometerVal": - station.SetAlltime(station.AllTime.LowPress, double.Parse(value), station.AllTime.LowPress.Ts); + case "highRainRate": + station.SetAlltime(station.AllTime.HighRainRate, value, time); break; - case "lowBarometerTime": - station.SetAlltime(station.AllTime.LowPress, station.AllTime.LowPress.Val, localeDateTimeStrToDate(value)); + case "highHourlyRain": + station.SetAlltime(station.AllTime.HourlyRain, value, time); break; - case "highGustVal": - station.SetAlltime(station.AllTime.HighGust, double.Parse(value), station.AllTime.HighGust.Ts); + case "highDailyRain": + station.SetAlltime(station.AllTime.DailyRain, value, time); break; - case "highGustTime": - station.SetAlltime(station.AllTime.HighGust, station.AllTime.HighGust.Val, localeDateTimeStrToDate(value)); + case "highRain24h": + station.SetAlltime(station.AllTime.HighRain24Hours, value, time); break; - case "highWindVal": - station.SetAlltime(station.AllTime.HighWind, double.Parse(value), station.AllTime.HighWind.Ts); - break; - case "highWindTime": - station.SetAlltime(station.AllTime.HighWind, station.AllTime.HighWind.Val, localeDateTimeStrToDate(value)); - break; - case "highWindRunVal": - station.SetAlltime(station.AllTime.HighWindRun, double.Parse(value), station.AllTime.HighWindRun.Ts); - break; - case "highWindRunTime": - station.SetAlltime(station.AllTime.HighWindRun, station.AllTime.HighWindRun.Val, localeDateTimeStrToDate(value)); - break; - case "highRainRateVal": - station.SetAlltime(station.AllTime.HighRainRate, double.Parse(value), station.AllTime.HighRainRate.Ts); - break; - case "highRainRateTime": - station.SetAlltime(station.AllTime.HighRainRate, station.AllTime.HighRainRate.Val, localeDateTimeStrToDate(value)); - break; - case "highHourlyRainVal": - station.SetAlltime(station.AllTime.HourlyRain, double.Parse(value), station.AllTime.HourlyRain.Ts); - break; - case "highHourlyRainTime": - station.SetAlltime(station.AllTime.HourlyRain, station.AllTime.HourlyRain.Val, localeDateTimeStrToDate(value)); - break; - case "highDailyRainVal": - station.SetAlltime(station.AllTime.DailyRain, double.Parse(value), station.AllTime.DailyRain.Ts); - break; - case "highDailyRainTime": - station.SetAlltime(station.AllTime.DailyRain, station.AllTime.DailyRain.Val, localeDateTimeStrToDate(value)); - break; - case "highRain24hVal": - station.SetAlltime(station.AllTime.HighRain24Hours, double.Parse(value), station.AllTime.HighRain24Hours.Ts); - break; - case "highRain24hTime": - station.SetAlltime(station.AllTime.HighRain24Hours, station.AllTime.HighRain24Hours.Val, localeDateTimeStrToDate(value)); - break; - case "highMonthlyRainVal": - station.SetAlltime(station.AllTime.MonthlyRain, double.Parse(value), station.AllTime.MonthlyRain.Ts); - break; - case "highMonthlyRainTime": + case "highMonthlyRain": // MM/yyyy - station.SetAlltime(station.AllTime.MonthlyRain, station.AllTime.MonthlyRain.Val, localeMonthYearStrToDate(value)); - break; - case "longestDryPeriodVal": - station.SetAlltime(station.AllTime.LongestDryPeriod, double.Parse(value), station.AllTime.LongestDryPeriod.Ts); + station.SetAlltime(station.AllTime.MonthlyRain, value, localeMonthYearStrToDate(txtTime)); break; - case "longestDryPeriodTime": - station.SetAlltime(station.AllTime.LongestDryPeriod, station.AllTime.LongestDryPeriod.Val, localeDateTimeStrToDate(value)); + case "longestDryPeriod": + station.SetAlltime(station.AllTime.LongestDryPeriod, int.Parse(txtValue), time); break; - case "longestWetPeriodVal": - station.SetAlltime(station.AllTime.LongestWetPeriod, double.Parse(value), station.AllTime.LongestWetPeriod.Ts); - break; - case "longestWetPeriodTime": - station.SetAlltime(station.AllTime.LongestWetPeriod, station.AllTime.LongestWetPeriod.Val, localeDateTimeStrToDate(value)); + case "longestWetPeriod": + station.SetAlltime(station.AllTime.LongestWetPeriod, int.Parse(txtValue), time); break; default: return "Data index not recognised"; @@ -1420,193 +1339,111 @@ internal string EditMonthlyRecs(IHttpContext context) { text = Uri.UnescapeDataString(reader.ReadToEnd()); } - // Eg "name=2-highTempValvalue=134.6&pk=1" + // Eg "name=2-highTemp&value=134.6&time=29/01/23 08:07" var newData = text.Split('&'); + var monthField = newData[0].Split('=')[1].Split('-'); var month = int.Parse(monthField[0]); var field = monthField[1]; - var value = newData[1].Split('=')[1]; + + var txtValue = newData[1].Split('=')[1]; + var value = double.Parse(txtValue); + + var txtTime = newData[2].Split('=')[1]; + var time = localeDateTimeStrToDate(txtTime); + try { lock (station.monthlyalltimeIniThreadLock) { - string[] dt; switch (field) { - case "highTempVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighTemp, double.Parse(value), station.MonthlyRecs[month].HighTemp.Ts); - break; - case "highTempTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighTemp, station.MonthlyRecs[month].HighTemp.Val, localeDateTimeStrToDate(value)); - break; - case "lowTempVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowTemp, double.Parse(value), station.MonthlyRecs[month].LowTemp.Ts); - break; - case "lowTempTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowTemp, station.MonthlyRecs[month].LowTemp.Val, localeDateTimeStrToDate(value)); - break; - case "highDewPointVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighDewPoint, double.Parse(value), station.MonthlyRecs[month].HighDewPoint.Ts); - break; - case "highDewPointTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighDewPoint, station.MonthlyRecs[month].HighDewPoint.Val, localeDateTimeStrToDate(value)); - break; - case "lowDewPointVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowDewPoint, double.Parse(value), station.MonthlyRecs[month].LowDewPoint.Ts); - break; - case "lowDewPointTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowDewPoint, station.MonthlyRecs[month].LowDewPoint.Val, localeDateTimeStrToDate(value)); - break; - case "highApparentTempVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighAppTemp, double.Parse(value), station.MonthlyRecs[month].HighAppTemp.Ts); - break; - case "highApparentTempTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighAppTemp, station.MonthlyRecs[month].HighAppTemp.Val, localeDateTimeStrToDate(value)); - break; - case "lowApparentTempVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowAppTemp, double.Parse(value), station.MonthlyRecs[month].LowAppTemp.Ts); - break; - case "lowApparentTempTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowAppTemp, station.MonthlyRecs[month].LowAppTemp.Val, localeDateTimeStrToDate(value)); - break; - case "highFeelsLikeVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighFeelsLike, double.Parse(value), station.MonthlyRecs[month].HighFeelsLike.Ts); - break; - case "highFeelsLikeTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighFeelsLike, station.MonthlyRecs[month].HighFeelsLike.Val, localeDateTimeStrToDate(value)); - break; - case "lowFeelsLikeVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowFeelsLike, double.Parse(value), station.MonthlyRecs[month].LowFeelsLike.Ts); - break; - case "lowFeelsLikeTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowFeelsLike, station.MonthlyRecs[month].LowFeelsLike.Val, localeDateTimeStrToDate(value)); - break; - case "highHumidexVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHumidex, double.Parse(value), station.MonthlyRecs[month].HighHumidex.Ts); - break; - case "highHumidexTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHumidex, station.MonthlyRecs[month].HighHumidex.Val, localeDateTimeStrToDate(value)); - break; - case "lowWindChillVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowChill, double.Parse(value), station.MonthlyRecs[month].LowChill.Ts); - break; - case "lowWindChillTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowChill, station.MonthlyRecs[month].LowChill.Val, localeDateTimeStrToDate(value)); - break; - case "highHeatIndexVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHeatIndex, double.Parse(value), station.MonthlyRecs[month].HighHeatIndex.Ts); - break; - case "highHeatIndexTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHeatIndex, station.MonthlyRecs[month].HighHeatIndex.Val, localeDateTimeStrToDate(value)); - break; - case "highMinTempVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighMinTemp, double.Parse(value), station.MonthlyRecs[month].HighMinTemp.Ts); - break; - case "highMinTempTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighMinTemp, station.MonthlyRecs[month].HighMinTemp.Val, localeDateTimeStrToDate(value)); - break; - case "lowMaxTempVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowMaxTemp, double.Parse(value), station.MonthlyRecs[month].LowMaxTemp.Ts); - break; - case "lowMaxTempTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowMaxTemp, station.MonthlyRecs[month].LowMaxTemp.Val, localeDateTimeStrToDate(value)); - break; - case "highDailyTempRangeVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighDailyTempRange, double.Parse(value), station.MonthlyRecs[month].HighDailyTempRange.Ts); - break; - case "highDailyTempRangeTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighDailyTempRange, station.MonthlyRecs[month].HighDailyTempRange.Val, localeDateTimeStrToDate(value)); - break; - case "lowDailyTempRangeVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowDailyTempRange, double.Parse(value), station.MonthlyRecs[month].LowDailyTempRange.Ts); - break; - case "lowDailyTempRangeTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowDailyTempRange, station.MonthlyRecs[month].LowDailyTempRange.Val, localeDateTimeStrToDate(value)); + case "highTemp": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighTemp, value, time); break; - case "highHumidityVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHumidity, double.Parse(value), station.MonthlyRecs[month].HighHumidity.Ts); + case "lowTemp": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowTemp, value, time); break; - case "highHumidityTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHumidity, station.MonthlyRecs[month].HighHumidity.Val, localeDateTimeStrToDate(value)); + case "highDewPoint": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighDewPoint, value, time); break; - case "lowHumidityVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowHumidity, double.Parse(value), station.MonthlyRecs[month].LowHumidity.Ts); + case "lowDewPoint": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowDewPoint, value, time); break; - case "lowHumidityTime": - dt = value.Split('+'); - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowHumidity, station.MonthlyRecs[month].LowHumidity.Val, localeDateTimeStrToDate(value)); + case "highApparentTemp": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighAppTemp, value, time); break; - case "highBarometerVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighPress, double.Parse(value), station.MonthlyRecs[month].HighPress.Ts); + case "lowApparentTemp": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowAppTemp, value, time); break; - case "highBarometerTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighPress, station.MonthlyRecs[month].HighPress.Val, localeDateTimeStrToDate(value)); + case "highFeelsLike": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighFeelsLike, value, time); break; - case "lowBarometerVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowPress, double.Parse(value), station.MonthlyRecs[month].LowPress.Ts); + case "lowFeelsLike": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowFeelsLike, value, time); break; - case "lowBarometerTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LowPress, station.MonthlyRecs[month].LowPress.Val, localeDateTimeStrToDate(value)); + case "highHumidex": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHumidex, value, time); break; - case "highGustVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighGust, double.Parse(value), station.MonthlyRecs[month].HighGust.Ts); + case "lowWindChill": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowChill, value, time); break; - case "highGustTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighGust, station.MonthlyRecs[month].HighGust.Val, localeDateTimeStrToDate(value)); + case "highHeatIndex": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHeatIndex, value, time); break; - case "highWindVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighWind, double.Parse(value), station.MonthlyRecs[month].HighWind.Ts); + case "highMinTemp": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighMinTemp, value, time); break; - case "highWindTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighWind, station.MonthlyRecs[month].HighWind.Val, localeDateTimeStrToDate(value)); + case "lowMaxTemp": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowMaxTemp, value, time); break; - case "highWindRunVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighWindRun, double.Parse(value), station.MonthlyRecs[month].HighWindRun.Ts); + case "highDailyTempRange": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighDailyTempRange, value, time); break; - case "highWindRunTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighWindRun, station.MonthlyRecs[month].HighWindRun.Val, localeDateTimeStrToDate(value)); + case "lowDailyTempRange": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowDailyTempRange, value, time); break; - case "highRainRateVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighRainRate, double.Parse(value), station.MonthlyRecs[month].HighRainRate.Ts); + case "highHumidity": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighHumidity, int.Parse(txtValue), time); break; - case "highRainRateTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighRainRate, station.MonthlyRecs[month].HighRainRate.Val, localeDateTimeStrToDate(value)); + case "lowHumidity": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowHumidity, int.Parse(txtValue), time); break; - case "highHourlyRainVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HourlyRain, double.Parse(value), station.MonthlyRecs[month].HourlyRain.Ts); + case "highBarometer": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighPress, value, time); break; - case "highHourlyRainTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HourlyRain, station.MonthlyRecs[month].HourlyRain.Val, localeDateTimeStrToDate(value)); + case "lowBarometer": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LowPress, value, time); break; - case "highDailyRainVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].DailyRain, double.Parse(value), station.MonthlyRecs[month].DailyRain.Ts); + case "highGust": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighGust, value, time); break; - case "highDailyRainTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].DailyRain, station.MonthlyRecs[month].DailyRain.Val, localeDateTimeStrToDate(value)); + case "highWind": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighWind, value, time); break; - case "highRain24hVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighRain24Hours, double.Parse(value), station.MonthlyRecs[month].HighRain24Hours.Ts); + case "highWindRun": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighWindRun, value, time); break; - case "highRain24hTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].HighRain24Hours, station.MonthlyRecs[month].HighRain24Hours.Val, localeDateTimeStrToDate(value)); + case "highRainRate": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighRainRate, value, time); break; - case "highMonthlyRainVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].MonthlyRain, double.Parse(value), station.MonthlyRecs[month].MonthlyRain.Ts); + case "highHourlyRain": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HourlyRain, value, time); break; - case "highMonthlyRainTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].MonthlyRain, station.MonthlyRecs[month].MonthlyRain.Val, localeMonthYearStrToDate(value)); + case "highDailyRain": + station.SetMonthlyAlltime(station.MonthlyRecs[month].DailyRain, value, time); break; - case "longestDryPeriodVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LongestDryPeriod, double.Parse(value), station.MonthlyRecs[month].LongestDryPeriod.Ts); + case "highRain24h": + station.SetMonthlyAlltime(station.MonthlyRecs[month].HighRain24Hours, value, time); break; - case "longestDryPeriodTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LongestDryPeriod, station.MonthlyRecs[month].LongestDryPeriod.Val, localeDateTimeStrToDate(value)); + case "highMonthlyRain": + station.SetMonthlyAlltime(station.MonthlyRecs[month].MonthlyRain, value, localeMonthYearStrToDate(txtTime)); break; - case "longestWetPeriodVal": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LongestWetPeriod, double.Parse(value), station.MonthlyRecs[month].LongestWetPeriod.Ts); + case "longestDryPeriod": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LongestDryPeriod, int.Parse(txtValue), time); break; - case "longestWetPeriodTime": - station.SetMonthlyAlltime(station.MonthlyRecs[month].LongestWetPeriod, station.MonthlyRecs[month].LongestWetPeriod.Val, localeDateTimeStrToDate(value)); + case "longestWetPeriod": + station.SetMonthlyAlltime(station.MonthlyRecs[month].LongestWetPeriod, int.Parse(txtValue), time); break; default: return "Data index not recognised"; @@ -2761,181 +2598,134 @@ internal string EditThisMonthRecs(IHttpContext context) { text = Uri.UnescapeDataString(reader.ReadToEnd()); } - // Eg "name=highTempValvalue=134.6&pk=1" + // Eg name=highTempVal&value=134.6&pk=1 - From direct editing + // Eg name=highTempTime&value="04/11/2023 06:58"&pk=1 - From direct editing + // Eg name=highTemp&value=134.6&time="04/11/2023 06:58" - From recorder "clicker" + var newData = text.Split('&'); var field = newData[0].Split('=')[1]; - var value = newData[1].Split('=')[1]; + + var txtValue = newData[1].Split('=')[1]; + var value = double.Parse(txtValue); + + var txtTime = newData[2].Split('=')[1]; + var time = localeDateTimeStrToDate(txtTime); + try { switch (field) { - case "highTempVal": - station.ThisMonth.HighTemp.Val = double.Parse(value); - break; - case "highTempTime": - station.ThisMonth.HighTemp.Ts = localeDateTimeStrToDate(value); - break; - case "lowTempVal": - station.ThisMonth.LowTemp.Val = double.Parse(value); - break; - case "lowTempTime": - station.ThisMonth.LowTemp.Ts = localeDateTimeStrToDate(value); - break; - case "highDewPointVal": - station.ThisMonth.HighDewPoint.Val = double.Parse(value); - break; - case "highDewPointTime": - station.ThisMonth.HighDewPoint.Ts = localeDateTimeStrToDate(value); - break; - case "lowDewPointVal": - station.ThisMonth.LowDewPoint.Val = double.Parse(value); - break; - case "lowDewPointTime": - station.ThisMonth.LowDewPoint.Ts = localeDateTimeStrToDate(value); - break; - case "highApparentTempVal": - station.ThisMonth.HighAppTemp.Val = double.Parse(value); - break; - case "highApparentTempTime": - station.ThisMonth.HighAppTemp.Ts = localeDateTimeStrToDate(value); - break; - case "lowApparentTempVal": - station.ThisMonth.LowAppTemp.Val = double.Parse(value); - break; - case "lowApparentTempTime": - station.ThisMonth.LowAppTemp.Ts = localeDateTimeStrToDate(value); - break; - case "highFeelsLikeVal": - station.ThisMonth.HighFeelsLike.Val = double.Parse(value); - break; - case "highFeelsLikeTime": - station.ThisMonth.HighFeelsLike.Ts = localeDateTimeStrToDate(value); - break; - case "lowFeelsLikeVal": - station.ThisMonth.LowFeelsLike.Val = double.Parse(value); - break; - case "lowFeelsLikeTime": - station.ThisMonth.LowFeelsLike.Ts = localeDateTimeStrToDate(value); - break; - case "highHumidexVal": - station.ThisMonth.HighHumidex.Val = double.Parse(value); - break; - case "highHumidexTime": - station.ThisMonth.HighHumidex.Ts = localeDateTimeStrToDate(value); - break; - case "lowWindChillVal": - station.ThisMonth.LowChill.Val = double.Parse(value); - break; - case "lowWindChillTime": - station.ThisMonth.LowChill.Ts = localeDateTimeStrToDate(value); - break; - case "highHeatIndexVal": - station.ThisMonth.HighHeatIndex.Val = double.Parse(value); - break; - case "highHeatIndexTime": - station.ThisMonth.HighHeatIndex.Ts = localeDateTimeStrToDate(value); - break; - case "highMinTempVal": - station.ThisMonth.HighMinTemp.Val = double.Parse(value); - break; - case "highMinTempTime": - station.ThisMonth.HighMinTemp.Ts = localeDateTimeStrToDate(value); - break; - case "lowMaxTempVal": - station.ThisMonth.LowMaxTemp.Val = double.Parse(value); - break; - case "lowMaxTempTime": - station.ThisMonth.LowMaxTemp.Ts = localeDateTimeStrToDate(value); - break; - case "highDailyTempRangeVal": - station.ThisMonth.HighDailyTempRange.Val = double.Parse(value); - break; - case "highDailyTempRangeTime": - station.ThisMonth.HighDailyTempRange.Ts = localeDateTimeStrToDate(value); - break; - case "lowDailyTempRangeVal": - station.ThisMonth.LowDailyTempRange.Val = double.Parse(value); + case "highTemp": + station.ThisMonth.HighTemp.Val = value; + station.ThisMonth.HighTemp.Ts = time; break; - case "lowDailyTempRangeTime": - station.ThisMonth.LowDailyTempRange.Ts = localeDateTimeStrToDate(value); + case "lowTemp": + station.ThisMonth.LowTemp.Val = value; + station.ThisMonth.LowTemp.Ts = time; break; - case "highHumidityVal": - station.ThisMonth.HighHumidity.Val = int.Parse(value); + case "highDewPoint": + station.ThisMonth.HighDewPoint.Val =value; + station.ThisMonth.HighDewPoint.Ts = time; break; - case "highHumidityTime": - station.ThisMonth.HighHumidity.Ts = localeDateTimeStrToDate(value); + case "lowDewPoint": + station.ThisMonth.LowDewPoint.Val = value; + station.ThisMonth.LowDewPoint.Ts = time; break; - case "lowHumidityVal": - station.ThisMonth.LowHumidity.Val = int.Parse(value); + case "highApparentTemp": + station.ThisMonth.HighAppTemp.Val = value; + station.ThisMonth.HighAppTemp.Ts = time; break; - case "lowHumidityTime": - station.ThisMonth.LowHumidity.Ts = localeDateTimeStrToDate(value); + case "lowApparentTemp": + station.ThisMonth.LowAppTemp.Val = value; + station.ThisMonth.LowAppTemp.Ts = time; break; - case "highBarometerVal": - station.ThisMonth.HighPress.Val = double.Parse(value); + case "highFeelsLike": + station.ThisMonth.HighFeelsLike.Val = value; + station.ThisMonth.HighFeelsLike.Ts = time; break; - case "highBarometerTime": - station.ThisMonth.HighPress.Ts = localeDateTimeStrToDate(value); + case "lowFeelsLike": + station.ThisMonth.LowFeelsLike.Val = value; + station.ThisMonth.LowFeelsLike.Ts = time; break; - case "lowBarometerVal": - station.ThisMonth.LowPress.Val = double.Parse(value); + case "highHumidex": + station.ThisMonth.HighHumidex.Val = value; + station.ThisMonth.HighHumidex.Ts = time; break; - case "lowBarometerTime": - station.ThisMonth.LowPress.Ts = localeDateTimeStrToDate(value); + case "lowWindChill": + station.ThisMonth.LowChill.Val =value; + station.ThisMonth.LowChill.Ts = time; break; - case "highGustVal": - station.ThisMonth.HighGust.Val = double.Parse(value); + case "highHeatIndex": + station.ThisMonth.HighHeatIndex.Val = value; + station.ThisMonth.HighHeatIndex.Ts = time; break; - case "highGustTime": - station.ThisMonth.HighGust.Ts = localeDateTimeStrToDate(value); + case "highMinTemp": + station.ThisMonth.HighMinTemp.Val = value; + station.ThisMonth.HighMinTemp.Ts = time; break; - case "highWindVal": - station.ThisMonth.HighWind.Val = double.Parse(value); + case "lowMaxTemp": + station.ThisMonth.LowMaxTemp.Val =value; + station.ThisMonth.LowMaxTemp.Ts = time; break; - case "highWindTime": - station.ThisMonth.HighWind.Ts = localeDateTimeStrToDate(value); + case "highDailyTempRange": + station.ThisMonth.HighDailyTempRange.Val = value; + station.ThisMonth.HighDailyTempRange.Ts = time; break; - case "highWindRunVal": - station.ThisMonth.HighWindRun.Val = double.Parse(value); + case "lowDailyTempRange": + station.ThisMonth.LowDailyTempRange.Val = value; + station.ThisMonth.LowDailyTempRange.Ts = time; break; - case "highWindRunTime": - station.ThisMonth.HighWindRun.Ts = localeDateTimeStrToDate(value); + case "highHumidity": + station.ThisMonth.HighHumidity.Val = int.Parse(txtValue); + station.ThisMonth.HighHumidity.Ts = time; break; - case "highRainRateVal": - station.ThisMonth.HighRainRate.Val = double.Parse(value); + case "lowHumidity": + station.ThisMonth.LowHumidity.Val = int.Parse(txtValue); + station.ThisMonth.LowHumidity.Ts = time; break; - case "highRainRateTime": - station.ThisMonth.HighRainRate.Ts = localeDateTimeStrToDate(value); + case "highBarometer": + station.ThisMonth.HighPress.Val = value; + station.ThisMonth.HighPress.Ts = time; break; - case "highHourlyRainVal": - station.ThisMonth.HourlyRain.Val = double.Parse(value); + case "lowBarometer": + station.ThisMonth.LowPress.Val = value; + station.ThisMonth.LowPress.Ts = time; break; - case "highHourlyRainTime": - station.ThisMonth.HourlyRain.Ts = localeDateTimeStrToDate(value); + case "highGust": + station.ThisMonth.HighGust.Val = value; + station.ThisMonth.HighGust.Ts = time; break; - case "highDailyRainVal": - station.ThisMonth.DailyRain.Val = double.Parse(value); + case "highWind": + station.ThisMonth.HighWind.Val = value; + station.ThisMonth.HighWind.Ts = time; break; - case "highDailyRainTime": - station.ThisMonth.DailyRain.Ts = localeDateTimeStrToDate(value); + case "highWindRun": + station.ThisMonth.HighWindRun.Val = value; + station.ThisMonth.HighWindRun.Ts = time; break; - case "highRain24hVal": - station.ThisMonth.HighRain24Hours.Val = double.Parse(value); + case "highRainRate": + station.ThisMonth.HighRainRate.Val = value; + station.ThisMonth.HighRainRate.Ts = time; break; - case "highRain24hTime": - station.ThisMonth.HighRain24Hours.Ts = localeDateTimeStrToDate(value); + case "highHourlyRain": + station.ThisMonth.HourlyRain.Val = value; + station.ThisMonth.HourlyRain.Ts = time; break; - case "longestDryPeriodVal": - station.ThisMonth.LongestDryPeriod.Val = int.Parse(value); + case "highDailyRain": + station.ThisMonth.DailyRain.Val = value; + station.ThisMonth.DailyRain.Ts = time; break; - case "longestDryPeriodTime": - station.ThisMonth.LongestDryPeriod.Ts = localeDateTimeStrToDate(value); + case "highRain24h": + station.ThisMonth.HighRain24Hours.Val = value; + station.ThisMonth.HighRain24Hours.Ts = time; break; - case "longestWetPeriodVal": - station.ThisMonth.LongestWetPeriod.Val = int.Parse(value); + case "longestDryPeriod": + station.ThisMonth.LongestDryPeriod.Val = int.Parse(txtValue); + station.ThisMonth.LongestDryPeriod.Ts = time; break; - case "longestWetPeriodTime": - station.ThisMonth.LongestWetPeriod.Ts = localeDateTimeStrToDate(value); + case "longestWetPeriod": + station.ThisMonth.LongestWetPeriod.Val = int.Parse(txtValue); + station.ThisMonth.LongestWetPeriod.Ts = time; break; default: return "Data index not recognised"; @@ -3034,185 +2824,133 @@ internal string EditThisYearRecs(IHttpContext context) // Eg "name=highTempValvalue=134.6&pk=1" var newData = text.Split('&'); var field = newData[0].Split('=')[1]; - var value = newData[1].Split('=')[1]; + + var txtValue = newData[1].Split('=')[1]; + var value = double.Parse(txtValue); + + var txtTime = newData[2].Split('=')[1]; + var time = localeDateTimeStrToDate(txtTime); + try { switch (field) { - case "highTempVal": - station.ThisYear.HighTemp.Val = double.Parse(value); - break; - case "highTempTime": - station.ThisYear.HighTemp.Ts = localeDateTimeStrToDate(value); - break; - case "lowTempVal": - station.ThisYear.LowTemp.Val = double.Parse(value); - break; - case "lowTempTime": - station.ThisYear.LowTemp.Ts = localeDateTimeStrToDate(value); - break; - case "highDewPointVal": - station.ThisYear.HighDewPoint.Val = double.Parse(value); - break; - case "highDewPointTime": - station.ThisYear.HighDewPoint.Ts = localeDateTimeStrToDate(value); - break; - case "lowDewPointVal": - station.ThisYear.LowDewPoint.Val = double.Parse(value); - break; - case "lowDewPointTime": - station.ThisYear.LowDewPoint.Ts = localeDateTimeStrToDate(value); - break; - case "highApparentTempVal": - station.ThisYear.HighAppTemp.Val = double.Parse(value); - break; - case "highApparentTempTime": - station.ThisYear.HighAppTemp.Ts = localeDateTimeStrToDate(value); - break; - case "lowApparentTempVal": - station.ThisYear.LowAppTemp.Val = double.Parse(value); - break; - case "lowApparentTempTime": - station.ThisYear.LowAppTemp.Ts = localeDateTimeStrToDate(value); - break; - case "highFeelsLikeVal": - station.ThisYear.HighFeelsLike.Val = double.Parse(value); - break; - case "highFeelsLikeTime": - station.ThisYear.HighFeelsLike.Ts = localeDateTimeStrToDate(value); + case "highTemp": + station.ThisYear.HighTemp.Val = value; + station.ThisYear.HighTemp.Ts = time; break; - case "lowFeelsLikeVal": - station.ThisYear.LowFeelsLike.Val = double.Parse(value); + case "lowTemp": + station.ThisYear.LowTemp.Val = value; + station.ThisYear.LowTemp.Ts = time; break; - case "lowFeelsLikeTime": - station.ThisYear.LowFeelsLike.Ts = localeDateTimeStrToDate(value); + case "highDewPoint": + station.ThisYear.HighDewPoint.Val = value; + station.ThisYear.HighDewPoint.Ts = time; break; - case "highHumidexVal": - station.ThisYear.HighHumidex.Val = double.Parse(value); + case "lowDewPoint": + station.ThisYear.LowDewPoint.Val = value; + station.ThisYear.LowDewPoint.Ts = time; break; - case "highHumidexTime": - station.ThisYear.HighHumidex.Ts = localeDateTimeStrToDate(value); + case "highApparentTemp": + station.ThisYear.HighAppTemp.Val = value; + station.ThisYear.HighAppTemp.Ts = time; break; - case "lowWindChillVal": - station.ThisYear.LowChill.Val = double.Parse(value); + case "lowApparentTemp": + station.ThisYear.LowAppTemp.Val = value; + station.ThisYear.LowAppTemp.Ts = time; break; - case "lowWindChillTime": - station.ThisYear.LowChill.Ts = localeDateTimeStrToDate(value); + case "highFeelsLike": + station.ThisYear.HighFeelsLike.Val = value; + station.ThisYear.HighFeelsLike.Ts = time; break; - case "highHeatIndexVal": - station.ThisYear.HighHeatIndex.Val = double.Parse(value); + case "lowFeelsLike": + station.ThisYear.LowFeelsLike.Val = value; + station.ThisYear.LowFeelsLike.Ts = time; break; - case "highHeatIndexTime": - station.ThisYear.HighHeatIndex.Ts = localeDateTimeStrToDate(value); + case "highHumidex": + station.ThisYear.HighHumidex.Val = value; + station.ThisYear.HighHumidex.Ts = time; break; - case "highMinTempVal": - station.ThisYear.HighMinTemp.Val = double.Parse(value); + case "lowWindChill": + station.ThisYear.LowChill.Val = value; + station.ThisYear.LowChill.Ts = time; break; - case "highMinTempTime": - station.ThisYear.HighMinTemp.Ts = localeDateTimeStrToDate(value); + case "highHeatIndex": + station.ThisYear.HighHeatIndex.Val = value; + station.ThisYear.HighHeatIndex.Ts = time; break; - case "lowMaxTempVal": - station.ThisYear.LowMaxTemp.Val = double.Parse(value); + case "highMinTemp": + station.ThisYear.HighMinTemp.Val = value; + station.ThisYear.HighMinTemp.Ts = time; break; - case "lowMaxTempTime": - station.ThisYear.LowMaxTemp.Ts = localeDateTimeStrToDate(value); + case "lowMaxTemp": + station.ThisYear.LowMaxTemp.Val = value; + station.ThisYear.LowMaxTemp.Ts = time; break; - case "highDailyTempRangeVal": - station.ThisYear.HighDailyTempRange.Val = double.Parse(value); + case "highDailyTempRange": + station.ThisYear.HighDailyTempRange.Val = value; + station.ThisYear.HighDailyTempRange.Ts = time; break; - case "highDailyTempRangeTime": - station.ThisYear.HighDailyTempRange.Ts = localeDateTimeStrToDate(value); + case "lowDailyTempRange": + station.ThisYear.LowDailyTempRange.Val = value; + station.ThisYear.LowDailyTempRange.Ts = time; break; - case "lowDailyTempRangeVal": - station.ThisYear.LowDailyTempRange.Val = double.Parse(value); + case "highHumidity": + station.ThisYear.HighHumidity.Val = int.Parse(txtValue); + station.ThisYear.HighHumidity.Ts = time; break; - case "lowDailyTempRangeTime": - station.ThisYear.LowDailyTempRange.Ts = localeDateTimeStrToDate(value); + case "lowHumidity": + station.ThisYear.LowHumidity.Val = int.Parse(txtValue); + station.ThisYear.LowHumidity.Ts = time; break; - case "highHumidityVal": - station.ThisYear.HighHumidity.Val = int.Parse(value); + case "highBarometer": + station.ThisYear.HighPress.Val = value; + station.ThisYear.HighPress.Ts = time; break; - case "highHumidityTime": - station.ThisYear.HighHumidity.Ts = localeDateTimeStrToDate(value); + case "lowBarometer": + station.ThisYear.LowPress.Val = value; + station.ThisYear.LowPress.Ts = time; break; - case "lowHumidityVal": - station.ThisYear.LowHumidity.Val = int.Parse(value); + case "highGust": + station.ThisYear.HighGust.Val = value; + station.ThisYear.HighGust.Ts = time; break; - case "lowHumidityTime": - station.ThisYear.LowHumidity.Ts = localeDateTimeStrToDate(value); + case "highWind": + station.ThisYear.HighWind.Val = value; + station.ThisYear.HighWind.Ts = time; break; - case "highBarometerVal": - station.ThisYear.HighPress.Val = double.Parse(value); + case "highWindRun": + station.ThisYear.HighWindRun.Val = value; + station.ThisYear.HighWindRun.Ts = time; break; - case "highBarometerTime": - station.ThisYear.HighPress.Ts = localeDateTimeStrToDate(value); + case "highRainRate": + station.ThisYear.HighRainRate.Val = value; + station.ThisYear.HighRainRate.Ts = time; break; - case "lowBarometerVal": - station.ThisYear.LowPress.Val = double.Parse(value); + case "highHourlyRain": + station.ThisYear.HourlyRain.Val = value; + station.ThisYear.HourlyRain.Ts = time; break; - case "lowBarometerTime": - station.ThisYear.LowPress.Ts = localeDateTimeStrToDate(value); + case "highDailyRain": + station.ThisYear.DailyRain.Val = value; + station.ThisYear.DailyRain.Ts = time; break; - case "highGustVal": - station.ThisYear.HighGust.Val = double.Parse(value); + case "highRain24h": + station.ThisYear.HighRain24Hours.Val = value; + station.ThisYear.HighRain24Hours.Ts = time; break; - case "highGustTime": - station.ThisYear.HighGust.Ts = localeDateTimeStrToDate(value); - break; - case "highWindVal": - station.ThisYear.HighWind.Val = double.Parse(value); - break; - case "highWindTime": - station.ThisYear.HighWind.Ts = localeDateTimeStrToDate(value); - break; - case "highWindRunVal": - station.ThisYear.HighWindRun.Val = double.Parse(value); - break; - case "highWindRunTime": - station.ThisYear.HighWindRun.Ts = localeDateTimeStrToDate(value); - break; - case "highRainRateVal": - station.ThisYear.HighRainRate.Val = double.Parse(value); - break; - case "highRainRateTime": - station.ThisYear.HighRainRate.Ts = localeDateTimeStrToDate(value); - break; - case "highHourlyRainVal": - station.ThisYear.HourlyRain.Val = double.Parse(value); - break; - case "highHourlyRainTime": - station.ThisYear.HourlyRain.Ts = localeDateTimeStrToDate(value); - break; - case "highDailyRainVal": - station.ThisYear.DailyRain.Val = double.Parse(value); - break; - case "highDailyRainTime": - station.ThisYear.DailyRain.Ts = localeDateTimeStrToDate(value); - break; - case "highRain24hVal": - station.ThisYear.HighRain24Hours.Val = double.Parse(value); - break; - case "highRain24hTime": - station.ThisYear.HighRain24Hours.Ts = localeDateTimeStrToDate(value); - break; - case "highMonthlyRainVal": - station.ThisYear.MonthlyRain.Val = double.Parse(value); - break; - case "highMonthlyRainTime": + case "highMonthlyRain": + station.ThisYear.MonthlyRain.Val = value; // MM/yyyy - station.ThisYear.MonthlyRain.Ts = localeMonthYearStrToDate(value); - break; - case "longestDryPeriodVal": - station.ThisYear.LongestDryPeriod.Val = int.Parse(value); - break; - case "longestDryPeriodTime": - station.ThisYear.LongestDryPeriod.Ts = localeDateTimeStrToDate(value); + station.ThisYear.MonthlyRain.Ts = localeMonthYearStrToDate(txtTime); break; - case "longestWetPeriodVal": - station.ThisYear.LongestWetPeriod.Val = int.Parse(value); + case "longestDryPeriod": + station.ThisYear.LongestDryPeriod.Val = int.Parse(txtValue); + station.ThisYear.LongestDryPeriod.Ts = time; break; - case "longestWetPeriodTime": - station.ThisYear.LongestWetPeriod.Ts = localeDateTimeStrToDate(value); + case "longestWetPeriod": + station.ThisYear.LongestWetPeriod.Val = int.Parse(txtValue); + station.ThisYear.LongestWetPeriod.Ts = time; break; default: return "Data index not recognised"; diff --git a/Updates.txt b/Updates.txt index c692c395..17c2857a 100644 --- a/Updates.txt +++ b/Updates.txt @@ -10,6 +10,10 @@ New Changed - All third-party upload issues are now classified as Warnings - Third party components for FTP(S) and SFTP updated +- If Cumulus.ini needs rewriting it is now overwritten, if it needs recreating it is first truncated to zero length. Previously the file was deleted and recreated in both instances which + made linking the file for example in Docker images impossible. +- The records editors now set both value and date/time when you click on the record from the dayfile for monthly logs +- The records editors now accept y/n key presses to update records or cancel Fixed - "Object is not intialized" error from the API when the station is not yet ready From e94a1156b850f67df5c903ba4424cc91c04a58f0 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 8 Nov 2023 16:02:41 +0000 Subject: [PATCH 31/45] Updates.txt --- Updates.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Updates.txt b/Updates.txt index 17c2857a..7dc19231 100644 --- a/Updates.txt +++ b/Updates.txt @@ -6,6 +6,7 @@ New - Without parameters it returns the total for the current month so far (excluding today) - You can add parameters "year" and "month" to return the value for a particular month. Eg. <#windrunmonth year=2022 month=3> - Like the existing <#windrun> it also accepts the "unit" parameter to return the value in miles, kilometers, or nautical miles +- PHP upload advanced option to disable the use of GET for small files and fallback to using POST for all files Changed - All third-party upload issues are now classified as Warnings From 1a863f011013f3d06845e8fe5346f76ab727342e Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sat, 11 Nov 2023 15:20:56 +0000 Subject: [PATCH 32/45] - Fix some potential errors in the dewpoint calculations on Ecowitt stations when using a mapped primary temp/hum sensor - Fix for zero length string error when parsing web tokens - Ecowitt stations can now map the indoor temp/hum values to the primary outdoor sensor (primarily of use for 4G stations that do not allow extra T/H sensors to be added) --- CumulusMX/Api.cs | 4 ++ CumulusMX/ApiTagProcessor.cs | 80 +++++++++++---------- CumulusMX/Cumulus.cs | 41 ++++++----- CumulusMX/DataEditor.cs | 21 ++++-- CumulusMX/DavisStation.cs | 24 +------ CumulusMX/DavisWllStation.cs | 11 +-- CumulusMX/EcowittApi.cs | 89 +++++++++++++++--------- CumulusMX/EcowittCloudStation.cs | 22 +++++- CumulusMX/FOStation.cs | 33 +++------ CumulusMX/GW1000Station.cs | 36 ++++++---- CumulusMX/HttpStationEcowitt.cs | 115 ++++++++++++++++++------------- CumulusMX/HttpStationWund.cs | 2 +- CumulusMX/ImetStation.cs | 6 +- CumulusMX/Simulator.cs | 4 +- CumulusMX/TempestStation.cs | 33 +++------ CumulusMX/WMR100Station.cs | 3 +- CumulusMX/WMR200Station.cs | 4 +- CumulusMX/WS2300Station.cs | 32 ++------- CumulusMX/WeatherStation.cs | 44 +++++++----- CumulusMX/webtags.cs | 4 +- Updates.txt | 3 + 21 files changed, 321 insertions(+), 290 deletions(-) diff --git a/CumulusMX/Api.cs b/CumulusMX/Api.cs index 51874967..a48b40e2 100644 --- a/CumulusMX/Api.cs +++ b/CumulusMX/Api.cs @@ -340,6 +340,8 @@ public async Task PostTags(string req) { cumulus.LogErrorMessage($"api/tags: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"{ex.Message}\"}}"); } } @@ -375,6 +377,8 @@ public async Task GetTags(string req) { cumulus.LogErrorMessage($"api/tags: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) + await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"500\",\"Description\":\"{ex.Message}\"}}"); } } } diff --git a/CumulusMX/ApiTagProcessor.cs b/CumulusMX/ApiTagProcessor.cs index dc7d5a68..42914b01 100644 --- a/CumulusMX/ApiTagProcessor.cs +++ b/CumulusMX/ApiTagProcessor.cs @@ -10,71 +10,69 @@ namespace CumulusMX public class ApiTagProcessor { private readonly Cumulus cumulus; - private WebTags webtags; + private readonly object lockObject = new object(); internal ApiTagProcessor(Cumulus cumulus) { this.cumulus = cumulus; } - internal void SetWebTags(WebTags webtags) - { - this.webtags = webtags; - } - // Output the processed response as a JSON string public string ProcessJson(IHttpRequest request) { - var rc = false; + lock (lockObject) + { + var rc = false; - var query = request.Url.Query; + var query = request.Url.Query; - cumulus.LogDebugMessage("API tag: Processing API JSON tag request"); - cumulus.LogDataMessage($"API tag: Source = {request.RemoteEndPoint} Input string = {query}"); + cumulus.LogDebugMessage("API tag: Processing API JSON tag request"); + cumulus.LogDataMessage($"API tag: Source = {request.RemoteEndPoint} Input string = {query}"); - var output = new StringBuilder("{", query.Length * 2); + var output = new StringBuilder("{", query.Length * 2); - try - { - // remove leading "?" and split on "&" - var input = new List(query.Substring(1).Split('&')); - var parms = new Dictionary(); - if (input[0] == "rc") + try { - input.RemoveAt(0); - rc = true; - } + // remove leading "?" and split on "&" + var input = new List(query.Substring(1).Split('&')); + var parms = new Dictionary(); + if (input[0] == "rc") + { + input.RemoveAt(0); + rc = true; + } - foreach (var tag in input) - { - if (rc) + foreach (var tag in input) { - parms.Add("webtag", tag); - parms.Add("rc", "y"); + if (rc) + { + parms.Add("webtag", tag); + parms.Add("rc", "y"); + } + var val = cumulus.WebTags.GetWebTagText(tag, parms); + output.Append($"\"{tag}\":\"{val}\","); + if (rc) + { + parms.Clear(); + } } - var val = webtags.GetWebTagText(tag, parms); - output.Append($"\"{tag}\":\"{val}\","); - if (rc) + if (output.Length > 1) { - parms.Clear(); + // remove trailing "," + output.Remove(output.Length - 1, 1); } + output.Append('}'); + + cumulus.LogDataMessage("API tag: Output string = " + output); } - if (output.Length > 1) + catch (Exception ex) { - // remove trailing "," - output.Remove(output.Length - 1, 1); + cumulus.LogErrorMessage($"API tag: Error - {ex.Message}"); + output.Append($"\"ERROR\":\"{ex.Message}\"}}"); } - output.Append('}'); - cumulus.LogDataMessage("API tag: Output string = " + output); - } - catch (Exception ex) - { - cumulus.LogErrorMessage($"API tag: Error - {ex.Message}"); - output.Append($"\"ERROR\":\"{ex.Message}\"}}"); + return output.ToString(); } - - return output.ToString(); } // Just return the processed text as-is diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index c24d36bc..8ea7c6dd 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -243,7 +243,7 @@ public struct TExtraFiles public PerformanceCounter UpTime; - private readonly WebTags webtags; + internal readonly WebTags WebTags; internal Lang Trans = new Lang(); @@ -1643,13 +1643,11 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) ecowittCloudExtra = new EcowittCloudStation(this, station); } - webtags = new WebTags(this, station); - webtags.InitialiseWebtags(); + WebTags = new WebTags(this, station); + WebTags.InitialiseWebtags(); httpFiles = new HttpFiles(this, station); - Api.dataEditor.SetWebTags(webtags); - Api.tagProcessor.SetWebTags(webtags); Api.httpFiles = httpFiles; RealtimeTimer.Interval = RealtimeInterval; @@ -3546,23 +3544,28 @@ private void CreateRealtimeHTMLfiles(int cycle) } } + private readonly object tokenParserLockObj = new object(); + public void TokenParserOnToken(string strToken, ref string strReplacement) { - var tagParams = new Dictionary(); - var paramList = ParseParams(strToken); - var webTag = paramList[0]; - - tagParams.Add("webtag", webTag); - for (int i = 1; i < paramList.Count; i += 2) + lock (tokenParserLockObj) { - // odd numbered entries are keys with "=" on the end - remove that - string key = paramList[i].Remove(paramList[i].Length - 1); - // even numbered entries are values - string value = paramList[i + 1]; - tagParams.Add(key, value); - } + var tagParams = new Dictionary(); + var paramList = ParseParams(strToken); + var webTag = paramList[0]; - strReplacement = webtags.GetWebTagText(webTag, tagParams); + tagParams.Add("webtag", webTag); + for (int i = 1; i < paramList.Count; i += 2) + { + // odd numbered entries are keys with "=" on the end - remove that + string key = paramList[i].Remove(paramList[i].Length - 1); + // even numbered entries are values + string value = paramList[i + 1]; + tagParams.Add(key, value); + } + + strReplacement = WebTags.GetWebTagText(webTag, tagParams); + } } private List ParseParams(string line) @@ -4683,7 +4686,7 @@ private void ReadIniFile() Gw1000IpAddress = ini.GetValue("GW1000", "IPAddress", "0.0.0.0"); Gw1000MacAddress = ini.GetValue("GW1000", "MACAddress", ""); Gw1000AutoUpdateIpAddress = ini.GetValue("GW1000", "AutoUpdateIpAddress", true); - Gw1000PrimaryTHSensor = ini.GetValue("GW1000", "PrimaryTHSensor", 0); // 0=default, 1-8=extra t/h sensor number + Gw1000PrimaryTHSensor = ini.GetValue("GW1000", "PrimaryTHSensor", 0); // 0=default, 1-8=extra t/h sensor number, 99=use indoor sensor Gw1000PrimaryRainSensor = ini.GetValue("GW1000", "PrimaryRainSensor", 0); //0=main station (tipping bucket) 1=piezo EcowittExtraEnabled = ini.GetValue("GW1000", "ExtraSensorDataEnabled", false); EcowittCloudExtraEnabled = ini.GetValue("GW1000", "ExtraCloudSensorDataEnabled", false); diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs index 87713d16..9a0d46ba 100644 --- a/CumulusMX/DataEditor.cs +++ b/CumulusMX/DataEditor.cs @@ -16,7 +16,6 @@ internal class DataEditor { private WeatherStation station; private readonly Cumulus cumulus; - private WebTags webtags; internal DataEditor(Cumulus cumulus) @@ -29,10 +28,6 @@ internal void SetStation(WeatherStation station) this.station = station; } - internal void SetWebTags(WebTags webtags) - { - this.webtags = webtags; - } //internal string EditRainToday(HttpListenerContext context) internal string EditRainToday(IHttpContext context) { @@ -2966,7 +2961,21 @@ internal string EditThisYearRecs(IHttpContext context) internal string GetCurrentCond() { - return $"{{\"data\":\"{webtags.GetCurrCondText()}\"}}"; + string res; + string fileName = cumulus.AppDir + "currentconditions.txt"; + if (File.Exists(fileName)) + { + using (StreamReader streamReader = new StreamReader(fileName)) + { + res = streamReader.ReadToEnd(); + } + } + else + { + res = string.Empty; + } + return res; + } internal string EditCurrentCond(IHttpContext context) diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index c988092c..7eefa3cb 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -1631,16 +1631,7 @@ private void GetAndProcessLoopData(int number) DoET(ConvertRainINToUser(loopData.AnnualET), now); } - if (ConvertUserWindToMS(WindAverage) < 1.5) - { - DoWindChill(OutdoorTemperature, now); - } - else - { - // calculate wind chill from calibrated C temp and calibrated win in KPH - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), now); - } - + DoWindChill(OutdoorTemperature, now); DoApparentTemp(now); DoFeelsLike(now); DoHumidex(now); @@ -2509,16 +2500,7 @@ private void GetArchiveData() AddValuesToRecentWind(avgwind, avgwind, bearing, timestamp.AddMinutes(-interval), timestamp); DoWind(wind, bearing, avgwind, timestamp); - - if (ConvertUserWindToMS(WindAverage) < 1.5) - { - DoWindChill(OutdoorTemperature, timestamp); - } - else - { - // calculate wind chill from calibrated C temp and calibrated win in KPH - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), timestamp); - } + DoWindChill(OutdoorTemperature, timestamp); // update dominant wind bearing CalculateDominantWindBearing(bearing, WindAverage, interval); @@ -2668,7 +2650,7 @@ private void GetArchiveData() cumulus.LogMessage("GetArchiveData: Page=" + p + " Record=" + r + " Timestamp=" + archiveData.Timestamp); - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), timestamp); + DoWindChill(-999, timestamp); DoApparentTemp(timestamp); DoFeelsLike(timestamp); diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs index 27aaf6c5..03da438d 100644 --- a/CumulusMX/DavisWllStation.cs +++ b/CumulusMX/DavisWllStation.cs @@ -1280,16 +1280,7 @@ private void DecodeCurrent(string currentJson) // Now we have the primary data, calculate the derived data if (cumulus.StationOptions.CalculatedWC) { - if (ConvertUserWindToMS(WindAverage) < 1.5) - { - // wind speed too low, use the temperature - DoWindChill(OutdoorTemperature, dateTime); - } - else - { - // calculate wind chill from calibrated C temp and calibrated wind in KPH - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), dateTime); - } + DoWindChill(OutdoorTemperature, dateTime); } DoApparentTemp(dateTime); diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index 1757b181..1de96486 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -1290,6 +1290,12 @@ private void ApplyHistoricData(KeyValuePair rec) { if (rec.Value.IndoorHum.HasValue) { + // user has mapped indoor humidity to outdoor + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + station.DoOutdoorHumidity(rec.Value.IndoorHum.Value, rec.Key); + } + station.DoIndoorHumidity(rec.Value.IndoorHum.Value); } else @@ -1297,9 +1303,12 @@ private void ApplyHistoricData(KeyValuePair rec) cumulus.LogWarningMessage("ApplyHistoricData: Missing indoor humidity data"); } - if (rec.Value.Humidity.HasValue && cumulus.Gw1000PrimaryTHSensor == 0) + if (rec.Value.Humidity.HasValue) { - station.DoOutdoorHumidity(rec.Value.Humidity.Value, rec.Key); + if (cumulus.Gw1000PrimaryTHSensor == 0) + { + station.DoOutdoorHumidity(rec.Value.Humidity.Value, rec.Key); + } } else { @@ -1339,6 +1348,12 @@ private void ApplyHistoricData(KeyValuePair rec) if (rec.Value.IndoorTemp.HasValue) { var tempVal = (double) rec.Value.IndoorTemp; + // user has mapped indoor temperature to outdoor + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + station.DoOutdoorTemp(tempVal, rec.Key); + } + station.DoIndoorTemp(tempVal); } else @@ -1355,10 +1370,13 @@ private void ApplyHistoricData(KeyValuePair rec) // = avg for period try { - if (rec.Value.Temp.HasValue && cumulus.Gw1000PrimaryTHSensor == 0) + if (rec.Value.Temp.HasValue) { - var tempVal = (double) rec.Value.Temp; - station.DoOutdoorTemp(tempVal, rec.Key); + if (cumulus.Gw1000PrimaryTHSensor == 0) + { + var tempVal = (double) rec.Value.Temp; + station.DoOutdoorTemp(tempVal, rec.Key); + } } else { @@ -1439,11 +1457,16 @@ private void ApplyHistoricData(KeyValuePair rec) if (rec.Value.ExtraTemp[i].HasValue) { var tempVal = (double) rec.Value.ExtraTemp[i]; + station.DoExtraTemp(tempVal, i); + if (i == cumulus.Gw1000PrimaryTHSensor) { station.DoOutdoorTemp(tempVal, rec.Key); } - station.DoExtraTemp(tempVal, i); + } + else if (i == cumulus.Gw1000PrimaryTHSensor) + { + cumulus.LogErrorMessage($"ApplyHistoricData: Missing Extra temperature #{i} mapped to outdoor temperature data"); } } catch (Exception ex) @@ -1455,11 +1478,16 @@ private void ApplyHistoricData(KeyValuePair rec) { if (rec.Value.ExtraHumidity[i].HasValue) { + station.DoExtraHum(rec.Value.ExtraHumidity[i].Value, i); + if (i == cumulus.Gw1000PrimaryTHSensor) { station.DoOutdoorHumidity(rec.Value.ExtraHumidity[i].Value, rec.Key); } - station.DoExtraHum(rec.Value.ExtraHumidity[i].Value, i); + } + else if (i == cumulus.Gw1000PrimaryTHSensor) + { + cumulus.LogErrorMessage($"ApplyHistoricData: Missing Extra humidity #{i} mapped to outdoor humidity data"); } } catch (Exception ex) @@ -1576,15 +1604,15 @@ private void ApplyHistoricData(KeyValuePair rec) // === Dewpoint === try { - if (cumulus.StationOptions.CalculatedDP) - { - station.DoOutdoorDewpoint(0, rec.Key); - } - else if (rec.Value.DewPoint.HasValue) + if (rec.Value.DewPoint.HasValue) { var val = (double) rec.Value.DewPoint; station.DoOutdoorDewpoint(val, rec.Key); } + else if (rec.Value.Temp.HasValue && rec.Value.Humidity.HasValue) + { + station.DoOutdoorDewpoint(-999, rec.Key); + } } catch (Exception ex) { @@ -1594,16 +1622,10 @@ private void ApplyHistoricData(KeyValuePair rec) // === Wind Chill === try { - if (cumulus.StationOptions.CalculatedWC && rec.Value.WindSpd.HasValue) - { - station.DoWindChill(0, rec.Key); - } - else + if (rec.Value.Temp.HasValue && rec.Value.WindSpd.HasValue) { // historic API does not provide Wind Chill so force calculation - cumulus.StationOptions.CalculatedWC = true; - station.DoWindChill(0, rec.Key); - cumulus.StationOptions.CalculatedWC = false; + station.DoWindChill(-999, rec.Key); } } catch (Exception ex) @@ -1611,21 +1633,24 @@ private void ApplyHistoricData(KeyValuePair rec) cumulus.LogErrorMessage("ApplyHistoricData: Error in Wind chill data - " + ex.Message); } - // === Humidex === + // === Humidex etc === try { - station.DoHumidex(rec.Key); - station.DoCloudBaseHeatIndex(rec.Key); - - // === Apparent & Feels Like === - requires temp, hum, and windspeed - if (rec.Value.WindSpd.HasValue) + if (rec.Value.Temp.HasValue && rec.Value.Humidity.HasValue) { - station.DoApparentTemp(rec.Key); - station.DoFeelsLike(rec.Key); - } - else - { - cumulus.LogWarningMessage("ApplyHistoricData: Insufficient data to calculate Apparent/Feels Like temps"); + station.DoHumidex(rec.Key); + station.DoCloudBaseHeatIndex(rec.Key); + + // === Apparent & Feels Like === - requires temp, hum, and windspeed + if (rec.Value.WindSpd.HasValue) + { + station.DoApparentTemp(rec.Key); + station.DoFeelsLike(rec.Key); + } + else + { + cumulus.LogWarningMessage("ApplyHistoricData: Insufficient data to calculate Humidex/Apparent/Feels Like temps"); + } } } catch (Exception ex) diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs index 32c39fcc..6a040f66 100644 --- a/CumulusMX/EcowittCloudStation.cs +++ b/CumulusMX/EcowittCloudStation.cs @@ -63,6 +63,11 @@ public EcowittCloudStation(Cumulus cumulus, WeatherStation station = null) : bas // We are using the primary T/H sensor cumulus.LogMessage("Using the default outdoor temp/hum sensor data"); } + else if (cumulus.Gw1000PrimaryTHSensor == 99) + { + cumulus.LogMessage("Overriding the default outdoor temp/hum data with the internal sensor"); + cumulus.StationOptions.CalculatedDP = true; + } else { // We are not using the primary T/H sensor @@ -228,10 +233,10 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok { var time = Utils.FromUnixTime(data.outdoor.temperature.time); DoOutdoorTemp(data.outdoor.temperature.value, time); + DoOutdoorHumidity(data.outdoor.humidity.value, time); DoOutdoorDewpoint(data.outdoor.dew_point.value, time); DoFeelsLike(time); DoApparentTemp(time); - DoOutdoorHumidity(data.outdoor.humidity.value, time); DoHumidex(time); DoCloudBaseHeatIndex(time); } @@ -251,6 +256,19 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok { try { + // user has mapped the indoor sensor to the outdoor sensor + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + var time = Utils.FromUnixTime(data.outdoor.temperature.time); + DoOutdoorTemp(data.indoor.temperature.value, time); + DoOutdoorHumidity(data.indoor.humidity.value, time); + DoOutdoorDewpoint(data.outdoor.dew_point.value, time); + DoFeelsLike(time); + DoApparentTemp(time); + DoHumidex(time); + DoCloudBaseHeatIndex(time); + } + DoIndoorTemp(data.indoor.temperature.value); DoIndoorHumidity(data.indoor.humidity.value); } @@ -288,7 +306,7 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok try { DoWind(data.wind.wind_gust.value, data.wind.wind_direction.value, data.wind.wind_speed.value, Utils.FromUnixTime(data.wind.wind_gust.time)); - DoWindChill(0, Utils.FromUnixTime(data.wind.wind_gust.time)); + DoWindChill(-999, Utils.FromUnixTime(data.wind.wind_gust.time)); } catch (Exception ex) { diff --git a/CumulusMX/FOStation.cs b/CumulusMX/FOStation.cs index e3be9f7e..c4d60fc1 100644 --- a/CumulusMX/FOStation.cs +++ b/CumulusMX/FOStation.cs @@ -61,6 +61,9 @@ internal FOStation(Cumulus cumulus) : base(cumulus) hasSolar = cumulus.StationType == StationTypes.FineOffsetSolar; + cumulus.StationOptions.CalculatedDP = true; + cumulus.StationOptions.CalculatedWC = true; + cumulus.LogMessage("FO synchronise reads: " + cumulus.FineOffsetOptions.SyncReads); if (cumulus.FineOffsetOptions.SyncReads) { @@ -546,21 +549,12 @@ private void ProcessHistoryData() prevraintotal = historydata.rainCounter; - OutdoorDewpoint = ConvertTempCToUser(MeteoLib.DewPoint(ConvertUserTempToC(OutdoorTemperature), OutdoorHumidity)); - - CheckForDewpointHighLow(timestamp); + // calculate dp + DoOutdoorDewpoint(-999, timestamp); // calculate wind chill - if (ConvertUserWindToMS(WindAverage) < 1.5) - { - DoWindChill(OutdoorTemperature, timestamp); - } - else - { - // calculate wind chill from calibrated C temp and calibrated win in KPH - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), timestamp); - } + DoWindChill(-999, timestamp); DoApparentTemp(timestamp); DoFeelsLike(timestamp); @@ -1316,21 +1310,12 @@ private void GetAndProcessData() // Use current humidity for dewpoint if (OutdoorHumidity > 0) { - OutdoorDewpoint = ConvertTempCToUser(MeteoLib.DewPoint(ConvertUserTempToC(OutdoorTemperature), OutdoorHumidity)); - - CheckForDewpointHighLow(now); + // calculate dp + DoOutdoorDewpoint(-999, now); } // calculate wind chill - // The 'global average speed will have been determined by the call of DoWind - // so use that in the wind chill calculation - double avgspeedKPH = ConvertUserWindToKPH(WindAverage); - - // windinMPH = calibwind * 2.23693629; - // calculate wind chill from calibrated C temp and calibrated win in KPH - double val = MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), avgspeedKPH); - - DoWindChill(ConvertTempCToUser(val), now); + DoWindChill(-999, now); DoApparentTemp(now); DoFeelsLike(now); diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index e4f0e0b1..f85fc7e7 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -72,9 +72,17 @@ public GW1000Station(Cumulus cumulus) : base(cumulus) // We are using the primary T/H sensor cumulus.LogMessage("Using the default outdoor temp/hum sensor data"); } + else if (cumulus.Gw1000PrimaryTHSensor == 99) + { + // We are overriding the outdoor with the indoor T/H sensor + cumulus.LogMessage("Overriding the default outdoor temp/hum data with Indoor temp/hum sensor"); + cumulus.StationOptions.CalculatedDP = true; + cumulus.StationOptions.CalculatedWC = true; + } else { // We are not using the primary T/H sensor so MX must calculate the wind chill as well + cumulus.StationOptions.CalculatedDP = true; cumulus.StationOptions.CalculatedWC = true; cumulus.LogMessage("Overriding the default outdoor temp/hum data with Extra temp/hum sensor #" + cumulus.Gw1000PrimaryTHSensor); } @@ -787,6 +795,8 @@ private void GetLiveData() double windSpeedLast = -999, rainRateLast = -999, rainLast = -999, gustLast = -999; int windDirLast = -999; double outdoortemp = -999; + double dewpoint = -999; + double windchill = -999; bool batteryLow = false; @@ -801,6 +811,12 @@ private void GetLiveData() { case 0x01: //Indoor Temperature (℃) tempInt16 = GW1000Api.ConvertBigEndianInt16(data, idx); + // user has mapped indoor temp to outdoor temp + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + // do not process temperature here as if "MX calculates DP" is enabled, we have not yet read the humidity value. Have to do it at the end. + outdoortemp = tempInt16 / 10.0; + } DoIndoorTemp(ConvertTempCToUser(tempInt16 / 10.0)); idx += 2; break; @@ -815,14 +831,14 @@ private void GetLiveData() break; case 0x03: //Dew point (℃) tempInt16 = GW1000Api.ConvertBigEndianInt16(data, idx); - DoOutdoorDewpoint(ConvertTempCToUser(tempInt16 / 10.0), dateTime); + dewpoint = tempInt16 / 10.0; idx += 2; break; case 0x04: //Wind chill (℃) if (cumulus.Gw1000PrimaryTHSensor == 0) { tempInt16 = GW1000Api.ConvertBigEndianInt16(data, idx); - DoWindChill(ConvertTempCToUser(tempInt16 / 10.0), dateTime); + windchill = tempInt16 / 10.0; } idx += 2; break; @@ -831,6 +847,11 @@ private void GetLiveData() idx += 2; break; case 0x06: //Indoor Humidity(%) + // user has mapped indoor hum to outdoor hum + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + DoOutdoorHumidity(data[idx], dateTime); + } DoIndoorHumidity(data[idx]); idx += 1; break; @@ -1256,16 +1277,7 @@ private void GetLiveData() if (outdoortemp > -999) { - if (ConvertUserWindToMS(WindAverage) < 1.5) - { - DoWindChill(OutdoorTemperature, dateTime); - } - else - { - // calculate wind chill from calibrated C temp and calibrated wind in KPH - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), dateTime); - } - + DoWindChill(windchill, dateTime); DoApparentTemp(dateTime); DoFeelsLike(dateTime); DoHumidex(dateTime); diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index c459c7e1..e6908ff2 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -66,6 +66,11 @@ public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base // We are using the primary T/H sensor cumulus.LogMessage("Using the default outdoor temp/hum sensor data"); } + else if (cumulus.Gw1000PrimaryTHSensor == 99) + { + // We are not using the primary T/H sensor + cumulus.LogMessage("Overriding the default outdoor temp/hum data with Indoor temp/hum sensor"); + } else { // We are not using the primary T/H sensor @@ -275,8 +280,8 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) { var procName = main ? "ApplyData" : "ApplyExtraData"; var thisStation = main ? this : station; - string thisTemp = null; - string thisHum = null; + var haveTemp = false; + var haveHum = false; try { @@ -316,9 +321,6 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) // Only do the primary sensors if running as the main station if (main) { - thisTemp = cumulus.Gw1000PrimaryTHSensor == 0 ? data["tempf"] : data["temp" + cumulus.Gw1000PrimaryTHSensor + "f"]; - thisHum = cumulus.Gw1000PrimaryTHSensor == 0 ? data["humidity"] : data["humidity" + cumulus.Gw1000PrimaryTHSensor]; - // === Wind == try { @@ -363,28 +365,34 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) // humidity // humidityin - var humIn = data["humidityin"]; - - if (humIn == null) + if (data["humidityin"] == null) { cumulus.LogWarningMessage($"{procName}: Error, missing indoor humidity"); } else { - var humVal = Convert.ToInt32(humIn, invNum); + var humVal = Convert.ToInt32(data["humidityin"], invNum); DoIndoorHumidity(humVal); + + // user has mapped indoor humidity to outdoor + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + DoOutdoorHumidity(humVal, recDate); + haveHum = true; + } } if (cumulus.Gw1000PrimaryTHSensor == 0) { - if (thisHum == null) + if (data["humidity"] == null) { cumulus.LogDebugMessage($"{procName}: Error, missing outdoor humidity"); } else { - var humVal = Convert.ToInt32(thisHum, invNum); + var humVal = Convert.ToInt32(data["humidity"], invNum); DoOutdoorHumidity(humVal, recDate); + haveHum = true; } } } @@ -435,17 +443,21 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) try { // tempinf - - var temp = data["tempinf"]; - - if (temp == null) + if (data["tempinf"] == null) { cumulus.LogDebugMessage($"{procName}: Error, missing indoor temp"); } else { - var tempVal = ConvertTempFToUser(Convert.ToDouble(temp, invNum)); + var tempVal = ConvertTempFToUser(Convert.ToDouble(data["tempinf"], invNum)); DoIndoorTemp(tempVal); + + // user has mapped indoor humidity to outdoor + if (cumulus.Gw1000PrimaryTHSensor == 99) + { + DoOutdoorTemp(tempVal, recDate); + haveTemp = true; + } } } catch (Exception ex) @@ -461,14 +473,15 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) // tempf if (cumulus.Gw1000PrimaryTHSensor == 0) { - if (thisTemp == null) + if (data["tempf"] == null) { cumulus.LogDebugMessage($"{procName}: Error, missing outdoor temp"); } else { - var tempVal = ConvertTempFToUser(Convert.ToDouble(thisTemp, invNum)); + var tempVal = ConvertTempFToUser(Convert.ToDouble(data["tempf"], invNum)); DoOutdoorTemp(tempVal, recDate); + haveTemp = true; } } } @@ -550,36 +563,36 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } } - // === Extra Temperature === + + // === Extra Humidity === if (main || cumulus.EcowittExtraUseTempHum) { try { - // temp[1-10]f - ProcessExtraTemps(data, thisStation, recDate); + // humidity[1-10] + haveHum = ProcessExtraHumidity(data, thisStation, recDate, haveHum); } catch (Exception ex) { - cumulus.LogErrorMessage($"{procName}: Error in extra temperature data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in extra humidity data - {ex.Message}"); } } - // === Extra Humidity === + // === Extra Temperature === if (main || cumulus.EcowittExtraUseTempHum) { try { - // humidity[1-10] - ProcessExtraHumidity(data, thisStation, recDate); + // temp[1-10]f + haveTemp = ProcessExtraTemps(data, thisStation, recDate, haveTemp); } catch (Exception ex) { - cumulus.LogErrorMessage($"{procName}: Error in extra humidity data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in extra temperature data - {ex.Message}"); } } - // === Solar === if (main || cumulus.EcowittExtraUseSolar) { @@ -824,22 +837,19 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) try { // dewptf - - var dewpnt = data["dewptf"]; - - if (cumulus.StationOptions.CalculatedDP) - { - DoOutdoorDewpoint(0, recDate); - } - else if (dewpnt == null) - { - cumulus.LogWarningMessage($"{procName}: Error, missing dew point"); - } - else + if (!cumulus.StationOptions.CalculatedDP) { - var val = ConvertTempFToUser(Convert.ToDouble(dewpnt, invNum)); - DoOutdoorDewpoint(val, recDate); + if (data["dewptf"] == null) + { + cumulus.LogWarningMessage($"{procName}: Error, missing dew point"); + } + else + { + var val = ConvertTempFToUser(Convert.ToDouble(data["dewptf"], invNum)); + DoOutdoorDewpoint(val, recDate); + } } + } catch (Exception ex) { @@ -854,9 +864,9 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) // windchillf if (cumulus.StationOptions.CalculatedWC) { - if (thisTemp != null && data["windspeedmph"] != null) + if (haveTemp && data["windspeedmph"] != null) { - DoWindChill(0, recDate); + DoWindChill(-999, recDate); } else { @@ -885,7 +895,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) // === Humidex === - if (thisTemp != null && thisHum != null) + if (haveTemp && haveHum) { DoHumidex(recDate); DoCloudBaseHeatIndex(recDate); @@ -923,7 +933,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) return ""; } - private void ProcessExtraTemps(NameValueCollection data, WeatherStation station, DateTime ts) + private bool ProcessExtraTemps(NameValueCollection data, WeatherStation station, DateTime ts, bool alreadyHaveTemp) { for (var i = 1; i <= 10; i++) { @@ -932,13 +942,19 @@ private void ProcessExtraTemps(NameValueCollection data, WeatherStation station, if (i == cumulus.Gw1000PrimaryTHSensor) { station.DoOutdoorTemp(ConvertTempFToUser(Convert.ToDouble(data["temp" + i + "f"], invNum)), ts); + alreadyHaveTemp = true; } station.DoExtraTemp(ConvertTempFToUser(Convert.ToDouble(data["temp" + i + "f"], invNum)), i); } + else if (i == cumulus.Gw1000PrimaryTHSensor) + { + cumulus.LogDebugMessage($"ProcessExtraTemps: Error, missing Extra temperature #{i} which is mapped to outdoor temperature"); + } } + return alreadyHaveTemp; } - private void ProcessExtraHumidity(NameValueCollection data, WeatherStation station, DateTime ts) + private bool ProcessExtraHumidity(NameValueCollection data, WeatherStation station, DateTime ts, bool alreadyHaveHum) { for (var i = 1; i <= 10; i++) { @@ -947,10 +963,17 @@ private void ProcessExtraHumidity(NameValueCollection data, WeatherStation stati if (i == cumulus.Gw1000PrimaryTHSensor) { station.DoOutdoorHumidity(Convert.ToInt32(data["humidity" + i], invNum), ts); + alreadyHaveHum = true; } station.DoExtraHum(Convert.ToDouble(data["humidity" + i], invNum), i); } + else if (i == cumulus.Gw1000PrimaryTHSensor) + { + cumulus.LogDebugMessage($"ProcessExtraHumidity: Error, missing Extra humidity #{i} which is mapped to outdoor humidity"); + } + } + return alreadyHaveHum; } private void ProcessSolar(NameValueCollection data, WeatherStation station, DateTime recDate) diff --git a/CumulusMX/HttpStationWund.cs b/CumulusMX/HttpStationWund.cs index 2bacc06b..ce4e4f4a 100644 --- a/CumulusMX/HttpStationWund.cs +++ b/CumulusMX/HttpStationWund.cs @@ -295,7 +295,7 @@ GET Parameters - all fields are URL escaped // - no w/c in wunderground data, so it must be set to CMX calculated if (data["windspeedmph"] != null && data["tempf"] != null && data["windspeedmph"] != "-9999" && data["tempf"] != "-9999") { - DoWindChill(0, recDate); + DoWindChill(-999, recDate); // === Apparent/Feels Like === if (data["humidity"] != null && data["humidity"] != "-9999") diff --git a/CumulusMX/ImetStation.cs b/CumulusMX/ImetStation.cs index 5876f509..3af91adb 100644 --- a/CumulusMX/ImetStation.cs +++ b/CumulusMX/ImetStation.cs @@ -762,9 +762,9 @@ public override void getAndProcessHistoryData() } // Cause wind chill calc - DoWindChill(0, timestamp); + DoWindChill(-999, timestamp); - DoOutdoorDewpoint(0, timestamp); + DoOutdoorDewpoint(-999, timestamp); DoApparentTemp(timestamp); DoFeelsLike(timestamp); DoHumidex(timestamp); @@ -1002,7 +1002,7 @@ private void ImetGetData() if (temp1 > -999 && humidity > -999) { - DoOutdoorDewpoint(0, now); + DoOutdoorDewpoint(-999, now); DoHumidex(now); DoCloudBaseHeatIndex(now); diff --git a/CumulusMX/Simulator.cs b/CumulusMX/Simulator.cs index 9d507878..c05de664 100644 --- a/CumulusMX/Simulator.cs +++ b/CumulusMX/Simulator.cs @@ -117,8 +117,8 @@ private void applyData(DateTime recDate) doSolar(recDate); - DoOutdoorDewpoint(0, recDate); - DoWindChill(0, recDate); + DoOutdoorDewpoint(-999, recDate); + DoWindChill(-999, recDate); DoHumidex(recDate); DoApparentTemp(recDate); DoFeelsLike(recDate); diff --git a/CumulusMX/TempestStation.cs b/CumulusMX/TempestStation.cs index a8e97acc..5b930354 100644 --- a/CumulusMX/TempestStation.cs +++ b/CumulusMX/TempestStation.cs @@ -31,6 +31,7 @@ public TempestStation(Cumulus cumulus) : base(cumulus) // Tempest does not provide wind chill cumulus.StationOptions.CalculatedWC = true; + cumulus.StationOptions.CalculatedDP = true; // Tempest does not provide average wind speeds cumulus.StationOptions.CalcuateAverageWindSpeed = true; @@ -161,22 +162,10 @@ private void ProcessHistoryData(List datalist) cumulus.LogMessage( $"TempestDoRainHist: Total Precip for Day: {Raincounter}"); - OutdoorDewpoint = - ConvertTempCToUser(MeteoLib.DewPoint(ConvertUserTempToC(OutdoorTemperature), - OutdoorHumidity)); - - CheckForDewpointHighLow(timestamp); - + // calculate dp + DoOutdoorDewpoint(-999, timestamp); // calculate wind chill - - if (ConvertUserWindToMS(WindAverage) < 1.5) - DoWindChill(OutdoorTemperature, timestamp); - else - // calculate wind chill from calibrated C temp and calibrated win in KPH - DoWindChill( - ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), - ConvertUserWindToKPH(WindAverage))), timestamp); - + DoWindChill(-999, timestamp); DoApparentTemp(timestamp); DoFeelsLike(timestamp); DoHumidex(timestamp); @@ -286,6 +275,9 @@ private void WeatherPacketReceived(WeatherPacket wp) ); ts = wp.Observation.Timestamp; + + DoOutdoorHumidity((int) wp.Observation.Humidity, ts); + var userTemp = ConvertTempCToUser(Convert.ToDouble(wp.Observation.Temperature)); DoOutdoorTemp(userTemp, ts); @@ -314,17 +306,10 @@ private void WeatherPacketReceived(WeatherPacket wp) cumulus.LogDebugMessage( $"TempestDoRain: Total Precip for Day: {Raincounter}"); - DoOutdoorHumidity((int) wp.Observation.Humidity, ts); - - OutdoorDewpoint = - ConvertTempCToUser(MeteoLib.DewPoint(ConvertUserTempToC(OutdoorTemperature), - OutdoorHumidity)); - - CheckForDewpointHighLow(ts); - + DoOutdoorDewpoint(-999, ts); DoApparentTemp(ts); DoFeelsLike(ts); - DoWindChill(userTemp, ts); + DoWindChill(-999, ts); DoHumidex(ts); DoCloudBaseHeatIndex(ts); diff --git a/CumulusMX/WMR100Station.cs b/CumulusMX/WMR100Station.cs index ea65070d..34dcb282 100644 --- a/CumulusMX/WMR100Station.cs +++ b/CumulusMX/WMR100Station.cs @@ -396,8 +396,7 @@ private void ProcessWindPacket() if (TempReadyToPlot) { - wc = OutdoorTemperature; - DoWindChill(wc, now); + DoWindChill(-999, now); } } else diff --git a/CumulusMX/WMR200Station.cs b/CumulusMX/WMR200Station.cs index 49efa755..90f99a48 100644 --- a/CumulusMX/WMR200Station.cs +++ b/CumulusMX/WMR200Station.cs @@ -866,7 +866,7 @@ private void ProcessWindPacket() // it can't/won't do it if temp isn't available, so don't // bother calling anyway - DoWindChill(OutdoorTemperature, now); + DoWindChill(-999, now); } else { @@ -1564,7 +1564,7 @@ private void ProcessHistoryDataPacket() // bother calling anyway if (TempReadyToPlot) { - DoWindChill(OutdoorTemperature, timestamp); + DoWindChill(-999, timestamp); } } else diff --git a/CumulusMX/WS2300Station.cs b/CumulusMX/WS2300Station.cs index c4cb3efe..bccc30b6 100644 --- a/CumulusMX/WS2300Station.cs +++ b/CumulusMX/WS2300Station.cs @@ -333,24 +333,13 @@ private void ProcessHistoryData() } // Wind chill ================================================================== - if (cumulus.StationOptions.CalculatedWC) + if (historydata.windchill < ConvertTempCToUser(60)) { - if (ConvertUserWindToMS(WindAverage) < 1.5) - { - DoWindChill(OutdoorTemperature, timestamp); - } - else - { - // calculate wind chill from calibrated C temp and calibrated win in KPH - DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), timestamp); - } + DoWindChill(historydata.windchill, timestamp); } else { - if (historydata.windchill < ConvertTempCToUser(60)) - { - DoWindChill(historydata.windchill, timestamp); - } + DoWindChill(-999, timestamp); } // Wind run ====================================================================== @@ -699,19 +688,12 @@ private void GetAndProcessData() } // wind chill - if (cumulus.StationOptions.CalculatedWC) + double wc = Ws2300WindChill(); + if (wc > -100 && wc < 60) { - DoWindChill(OutdoorTemperature, now); + DoWindChill(ConvertTempCToUser(wc), now); } - else - { - double wc = Ws2300WindChill(); - if (wc > -100 && wc < 60) - { - DoWindChill(ConvertTempCToUser(wc), now); - } - } - } + } // Rain =========================================================================== if (!stop) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 25c630e4..4441b613 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -5399,14 +5399,22 @@ public void DoWindChill(double chillpar, DateTime timestamp) { bool chillvalid = true; - if (cumulus.StationOptions.CalculatedWC) + if (cumulus.StationOptions.CalculatedWC || chillpar < -500) { // don"t try to calculate wind chill if we haven"t yet had wind and temp readings if (TempReadyToPlot && WindReadyToPlot) { double TempinC = ConvertUserTempToC(OutdoorTemperature); double windinKPH = ConvertUserWindToKPH(WindAverage); - WindChill = ConvertTempCToUser(MeteoLib.WindChill(TempinC, windinKPH)); + // no wind chill below 1.5 m/s = 5.4 km + if (windinKPH >= 5.4) + { + WindChill = ConvertTempCToUser(MeteoLib.WindChill(TempinC, windinKPH)); + } + else + { + WindChill = OutdoorTemperature; + } } else { @@ -5994,21 +6002,25 @@ public void DoRain(double total, double rate, DateTime timestamp) public void DoOutdoorDewpoint(double dp, DateTime timestamp) { - if (!cumulus.StationOptions.CalculatedDP) + + if (cumulus.StationOptions.CalculatedDP || dp < -500) { - if (ConvertUserTempToC(dp) <= cumulus.Limit.DewHigh) - { - OutdoorDewpoint = dp; - CheckForDewpointHighLow(timestamp); - } - else - { - var msg = $"Dew point greater than limit ({cumulus.Limit.DewHigh.ToString(cumulus.TempFormat)}); reading ignored: {dp.ToString(cumulus.TempFormat)}"; - lastSpikeRemoval = DateTime.Now; - cumulus.SpikeAlarm.LastMessage = msg; - cumulus.SpikeAlarm.Triggered = true; - cumulus.LogSpikeRemoval(msg); - } + dp = ConvertTempCToUser(MeteoLib.DewPoint(ConvertUserTempToC(OutdoorTemperature), OutdoorHumidity)); + + } + + if (ConvertUserTempToC(dp) <= cumulus.Limit.DewHigh) + { + OutdoorDewpoint = dp; + CheckForDewpointHighLow(timestamp); + } + else + { + var msg = $"Dew point greater than limit ({cumulus.Limit.DewHigh.ToString(cumulus.TempFormat)}); reading ignored: {dp.ToString(cumulus.TempFormat)}"; + lastSpikeRemoval = DateTime.Now; + cumulus.SpikeAlarm.LastMessage = msg; + cumulus.SpikeAlarm.Triggered = true; + cumulus.LogSpikeRemoval(msg); } } diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 96abc74d..7353a5cd 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -12,14 +12,14 @@ namespace CumulusMX { public delegate string WebTagFunction(Dictionary tagParams); - internal class WebTags + public class WebTags { private Dictionary webTagDictionary; private readonly Cumulus cumulus; private readonly WeatherStation station; - public WebTags(Cumulus cumulus, WeatherStation station) + internal WebTags(Cumulus cumulus, WeatherStation station) { this.cumulus = cumulus; this.station = station; diff --git a/Updates.txt b/Updates.txt index 7dc19231..eab52fa2 100644 --- a/Updates.txt +++ b/Updates.txt @@ -7,6 +7,7 @@ New - You can add parameters "year" and "month" to return the value for a particular month. Eg. <#windrunmonth year=2022 month=3> - Like the existing <#windrun> it also accepts the "unit" parameter to return the value in miles, kilometers, or nautical miles - PHP upload advanced option to disable the use of GET for small files and fallback to using POST for all files +- Ecowitt stations can now map the indoor temp/hum values to the primary outdoor sensor (primarily of use for 4G stations that do not allow extra T/H sensors to be added) Changed - All third-party upload issues are now classified as Warnings @@ -31,6 +32,8 @@ Fixed - Davis VP2, if requested changing the logger interval is now done after reading catch-up data so the archive is not cleared before catch-up - Fix error 500 in the config Wizard for Davis Cloud stations - Fix Wizard and Settings screens did not allow the entry of an IMEI number for the Ecowitt Cloud API device ID +- Fix some potential errors in the dewpoint calculations on Ecowitt stations when using a mapped primary temp/hum sensor +- Fix for zero length string error when parsing web tokens 3.27.0 - b3257 From ee508ac0638f84a2ca2dd4f71654709d90dcac2a Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sun, 12 Nov 2023 18:23:51 +0000 Subject: [PATCH 33/45] Second chance PHP compression test --- CumulusMX/Cumulus.cs | 8 ++++++++ Updates.txt | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 8ea7c6dd..352b479e 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -9879,6 +9879,13 @@ public void DoHttpFiles(DateTime now) LogDebugMessage("ProcessHttpFiles: Uploading http files"); + // do we perform a second chance compresssion test? + if (FtpOptions.PhpCompression == "none" && FtpOptions.PhpCompressionRetries == 0) + { + FtpOptions.PhpCompressionRetries++; + TestPhpUploadCompression(); + } + HttpFilesConfig .Where(x => x.Enabled && x.Url.Length > 0 && x.Remote.Length > 0 && x.NextDownload <= now && x.Upload) .ToList() @@ -14060,6 +14067,7 @@ public class FtpOptionsClass public string PhpSecret { get; set; } public bool PhpIgnoreCertErrors { get; set; } public string PhpCompression { get; set; } = "none"; + public int PhpCompressionRetries { get; set; } public int MaxConcurrentUploads { get; set; } public bool PhpUseGet { get; set; } } diff --git a/Updates.txt b/Updates.txt index eab52fa2..1ff629e0 100644 --- a/Updates.txt +++ b/Updates.txt @@ -18,7 +18,7 @@ Changed - The records editors now accept y/n key presses to update records or cancel Fixed -- "Object is not intialized" error from the API when the station is not yet ready +- "Object is not initialized" error from the API when the station is not yet ready - MX crash on start-up if the SQLite database has certain corruptions - Fix for PHP uploads when no compression is supported by the server (or the network was down when MX started) - VP2 error message "No Ack in response to DMPAFT" on start-up @@ -34,6 +34,7 @@ Fixed - Fix Wizard and Settings screens did not allow the entry of an IMEI number for the Ecowitt Cloud API device ID - Fix some potential errors in the dewpoint calculations on Ecowitt stations when using a mapped primary temp/hum sensor - Fix for zero length string error when parsing web tokens +- Perform a second chance PHP upload compression test at the first upload interval if no compression detected at start-up 3.27.0 - b3257 From b5b75e0803c81a8a5136b576959daaf44938a0df Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Sun, 12 Nov 2023 18:34:29 +0000 Subject: [PATCH 34/45] Add hash file creation to post build script --- CumulusMX/CumulusMX.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index 788761a8..1f3deab3 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -80,6 +80,6 @@ 3.27.1.3260 - + \ No newline at end of file From 89ff9f1d2868280cbda051f7f7c9d6e77e94748e Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Wed, 15 Nov 2023 22:50:40 +0000 Subject: [PATCH 35/45] Ecowitt camera URL finally sorted? Fix Current Conditions editor fetch not working --- CumulusMX/ApiTagProcessor.cs | 6 +- CumulusMX/Cumulus.cs | 6 +- CumulusMX/CumulusMX.csproj | 2 +- CumulusMX/DataEditor.cs | 5 +- CumulusMX/EcowittApi.cs | 289 +++++++++++++++++++++++++++++++ CumulusMX/EcowittCloudStation.cs | 81 ++++++--- CumulusMX/GW1000Station.cs | 27 ++- CumulusMX/HttpStationEcowitt.cs | 22 +++ CumulusMX/WeatherStation.cs | 6 +- CumulusMX/webtags.cs | 12 +- Updates.txt | 2 +- 11 files changed, 419 insertions(+), 39 deletions(-) diff --git a/CumulusMX/ApiTagProcessor.cs b/CumulusMX/ApiTagProcessor.cs index 42914b01..e7fdb555 100644 --- a/CumulusMX/ApiTagProcessor.cs +++ b/CumulusMX/ApiTagProcessor.cs @@ -84,15 +84,17 @@ public string ProcessText(IHttpRequest request) { var data = new StreamReader(request.InputStream).ReadToEnd(); +#if DEBUG cumulus.LogDataMessage($"API tag: Source = {request.RemoteEndPoint} Input string = {data}"); - +#endif var tokenParser = new TokenParser(cumulus.TokenParserOnToken); tokenParser.Encoding = new UTF8Encoding(false); tokenParser.InputText = data; var output = tokenParser.ToStringFromString(); +#if DEBUG cumulus.LogDataMessage("API tag: Output string = " + output); - +#endif return output; } catch (Exception ex) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 352b479e..e60ba943 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -7562,6 +7562,7 @@ public void WriteStringsFile() public string EcowittApplicationKey { get; set; } public string EcowittUserApiKey { get; set; } public string EcowittMacAddress { get; set; } + public string EcowittCameraMacAddress { get; set; } public bool EcowittSetCustomServer { get; set; } public string EcowittGatewayAddr { get; set; } public string EcowittLocalAddr { get; set; } @@ -12195,7 +12196,10 @@ private async Task ProcessTemplateFile2StringAsync(string template, bool public void StartTimersAndSensors() { - LogMessage("Start Extra Sensors"); + if (airLinkOut != null || airLinkIn != null || ecowittExtra != null || ambientExtra != null || ecowittCloudExtra != null) + { + LogMessage("Starting Extra Sensors"); + } airLinkOut?.Start(); airLinkIn?.Start(); ecowittExtra?.Start(); diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index 1f3deab3..fb14192a 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -77,7 +77,7 @@ CumulusMX.Program AnyCPU Copyright © 2015-2023 Cumulus MX - 3.27.1.3260 + 3.27.1.3261 diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs index 9a0d46ba..abb2e4d5 100644 --- a/CumulusMX/DataEditor.cs +++ b/CumulusMX/DataEditor.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Text; +using System.Web; using EmbedIO; @@ -2974,8 +2975,10 @@ internal string GetCurrentCond() { res = string.Empty; } - return res; + var str = HttpUtility.JavaScriptStringEncode(res); + + return $"{{\"data\":\"{str}\"}}"; } internal string EditCurrentCond(IHttpContext context) diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index 1de96486..b6720814 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -18,10 +18,13 @@ internal class EcowittApi private static readonly string historyUrl = "https://api.ecowitt.net/api/v3/device/history?"; private static readonly string currentUrl = "https://api.ecowitt.net/api/v3/device/real_time?"; + private static readonly string stationUrl = "https://api.ecowitt.net/api/v3/device/list?"; private static readonly int EcowittApiFudgeFactor = 5; // Number of minutes that Ecowitt API data is delayed private DateTime LastCurrentDataTime = DateTime.MinValue; + private DateTime LastCameraImageTime = DateTime.MinValue; + private DateTime LastCameraCallTime = DateTime.MinValue; public EcowittApi(Cumulus cuml, WeatherStation stn) { @@ -1848,6 +1851,261 @@ internal CurrentDataData GetCurrentData(CancellationToken token, ref int delay) } + internal string GetCurrentCameraImageUrl(CancellationToken token, string defaultUrl) + { + // Doc: https://doc.ecowitt.net/web/#/apiv3en?page_id=17 + + cumulus.LogMessage("API.GetCurrentCameraImageUrl: Get Ecowitt Current Camera Data"); + + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittCameraMacAddress)) + { + cumulus.LogWarningMessage("API.GetCurrentCameraImageUrl: Missing Ecowitt API data in the configuration, aborting!"); + return defaultUrl; + } + + + // rate limit to one call per minute + if (LastCameraCallTime.AddMinutes(1) > DateTime.Now) + { + cumulus.LogMessage("API.GetCurrentCameraImageUrl: Last call was less than 1 minute ago, using last image URL"); + return defaultUrl; + } + + LastCameraCallTime = DateTime.Now; + + if (LastCameraImageTime.AddMinutes(5) > DateTime.Now) + { + cumulus.LogMessage("API.GetCurrentCameraImageUrl: Last image was less than 5 minutes ago, using last image URL"); + return defaultUrl; + } + + var sb = new StringBuilder(currentUrl); + + sb.Append($"application_key={cumulus.EcowittApplicationKey}"); + sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); + sb.Append($"&mac={cumulus.EcowittCameraMacAddress}"); + sb.Append("&call_back=camera"); + + var url = sb.ToString(); + + var logUrl = url.Replace(cumulus.EcowittApplicationKey, "<>").Replace(cumulus.EcowittUserApiKey, "<>"); + cumulus.LogDebugMessage($"Ecowitt URL = {logUrl}"); + + CurrentData currObj; + + try + { + string responseBody; + int responseCode; + + // we want to do this synchronously, so .Result + using (var response = Cumulus.MyHttpClient.GetAsync(url).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"API.GetCurrentCameraImageUrl: Ecowitt API Current Camera Response code: {responseCode}"); + cumulus.LogDataMessage($"API.GetCurrentCameraImageUrl: Ecowitt API Current Camera Response: {responseBody}"); + } + + if (responseCode != 200) + { + var currentError = responseBody.FromJson(); + cumulus.LogWarningMessage($"API.GetCurrentCameraImageUrl: Ecowitt API Current Camera Error: {currentError.code}, {currentError.msg}"); + cumulus.LogConsoleMessage($" - Error {currentError.code}: {currentError.msg}", ConsoleColor.Red); + return defaultUrl; + } + + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("API.GetCurrentCameraImageUrl: Ecowitt API Current Camera Data: No data was returned."); + cumulus.LogConsoleMessage(" - No current data available"); + return defaultUrl; + } + else if (responseBody.StartsWith("{\"code\":")) // sanity check + { + // get the sensor data + currObj = responseBody.FromJson(); + + if (currObj != null) + { + // success + if (currObj.code == 0) + { + if (currObj.data == null) + { + // There was no data returned. + return defaultUrl; + } + } + else if (currObj.code == -1 || currObj.code == 45001) + { + // -1 = system busy, 45001 = rate limited + + cumulus.LogMessage("API.GetCurrentCameraImageUrl: System Busy or Rate Limited, waiting 5 secs before retry..."); + return defaultUrl; + } + else + { + return defaultUrl; + } + } + else + { + return defaultUrl; + } + + } + else // No idea what we got, dump it to the log + { + cumulus.LogErrorMessage("API.GetCurrentCameraImageUrl: Invalid current message received"); + cumulus.LogDataMessage("API.GetCurrentCameraImageUrl: Received: " + responseBody); + return defaultUrl; + } + + if (!token.IsCancellationRequested) + { + if (currObj.data.camera == null) + { + cumulus.LogWarningMessage("API.GetCurrentCameraImageUrl: Ecowitt API Current Camera Data: No camera data was returned."); + return defaultUrl; + } + + LastCameraImageTime = Utils.FromUnixTime(currObj.data.camera.photo.time); + cumulus.LogDebugMessage($"API.GetCurrentCameraImageUrl: Last image update {LastCameraImageTime:s}"); + return currObj.data.camera.photo.url; + } + + return defaultUrl; + } + catch (Exception ex) + { + cumulus.LogErrorMessage("API.GetCurrentData: Exception: " + ex.Message); + return defaultUrl; + } + } + + internal bool GetStationList(CancellationToken token) + { + cumulus.LogMessage("API.GetStationList: Get Ecowitt Station List"); + + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) ) + { + cumulus.LogWarningMessage("API.GetCurrentCameraImageUrl: Missing Ecowitt API data in the configuration, aborting!"); + return false; + } + + var sb = new StringBuilder(stationUrl); + + sb.Append($"application_key={cumulus.EcowittApplicationKey}"); + sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); + + var url = sb.ToString(); + + var logUrl = url.Replace(cumulus.EcowittApplicationKey, "<>").Replace(cumulus.EcowittUserApiKey, "<>"); + cumulus.LogDebugMessage($"Ecowitt URL = {logUrl}"); + + StationList stnObj; + + try + { + string responseBody; + int responseCode; + + // we want to do this synchronously, so .Result + using (var response = Cumulus.MyHttpClient.GetAsync(url).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"API.GetStationList: Ecowitt API Station List Response code: {responseCode}"); + cumulus.LogDataMessage($"API.GetStationList: Ecowitt API Station List Response: {responseBody}"); + } + + if (responseCode != 200) + { + var currentError = responseBody.FromJson(); + cumulus.LogWarningMessage($"API.GetStationList: Ecowitt API Station List Error: {currentError.code}, {currentError.msg}"); + cumulus.LogConsoleMessage($" - Error {currentError.code}: {currentError.msg}", ConsoleColor.Red); + return false; + } + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("API.GetStationList: Ecowitt API Station List: No data was returned."); + cumulus.LogConsoleMessage(" - No current data available"); + return false; + } + else if (responseBody.StartsWith("{\"code\":")) // sanity check + { + // get the sensor data + stnObj = responseBody.FromJson(); + + if (stnObj != null) + { + // success + if (stnObj.code == 0) + { + if (stnObj.data == null) + { + // There was no data returned. + return false; + } + } + else if (stnObj.code == -1 || stnObj.code == 45001) + { + // -1 = system busy, 45001 = rate limited + + cumulus.LogMessage("API.GetStationList: System Busy or Rate Limited, waiting 5 secs before retry..."); + return false; + } + else + { + return false; + } + } + else + { + return false; + } + + } + else // No idea what we got, dump it to the log + { + cumulus.LogErrorMessage("API.GetStationList: Invalid message received"); + cumulus.LogDataMessage("API.GetStationList: Received: " + responseBody); + return false; + } + + if (!token.IsCancellationRequested) + { + if (stnObj.data.list == null) + { + cumulus.LogWarningMessage("API.GetStationList: Ecowitt API: No station data was returned."); + return false; + } + + foreach( var stn in stnObj.data.list) + { + cumulus.LogDebugMessage($"API.GetStationList: Station: id={stn.id}, mac/imei={stn.mac ?? stn.imei}, name={stn.name}, type={stn.type}"); + if (stn.type == 2) + { + // we have a camera + cumulus.EcowittCameraMacAddress = stn.mac; + } + } + + return cumulus.EcowittCameraMacAddress != null; + } + + return false; + } + catch (Exception ex) + { + cumulus.LogErrorMessage("API.GetStationList: Exception: " + ex.Message); + return false; + } + } + /* private string ErrorCode(int code) { @@ -2362,5 +2620,36 @@ internal class CurrentCameraVal public long time { get; set; } public string url { get; set; } } + + internal class StationList + { + public int code { get; set; } + public string msg { get; set; } + public long time { get; set; } + + public StationListData data { get; set; } + } + + internal class StationListData + { + public int total { get; set; } + public int totalPage { get; set; } + public int pageNum { get; set; } + public StationListDataStations[] list { get; set; } + } + + internal class StationListDataStations + { + public int id { get; set; } + public string name { get; set; } + public string mac { get; set; } + public string imei { get; set; } + public int type { get; set; } + public string date_zone_id { get; set; } + public long createtime { get; set; } + public double longitude { get; set; } + public double latitude { get; set; } + public string stationtype { get; set; } + } } } diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs index 6a040f66..ffb0fccd 100644 --- a/CumulusMX/EcowittCloudStation.cs +++ b/CumulusMX/EcowittCloudStation.cs @@ -107,6 +107,11 @@ public EcowittCloudStation(Cumulus cumulus, WeatherStation station = null) : bas { Task.Run(getAndProcessHistoryData); } + else if (cumulus.EcowittExtraUseCamera) + { + // see if we have a camera attached + ecowittApi.GetStationList(cumulus.cancellationToken); + } } public override void Start() @@ -122,36 +127,39 @@ public override void Start() cumulus.StartTimersAndSensors(); } + // main data task liveTask = Task.Run(() => { - try - { var piezoLastRead = DateTime.MinValue; var dataLastRead = DateTime.MinValue; var delay = 0; var nextFetch = DateTime.MinValue; - while (!cumulus.cancellationToken.IsCancellationRequested) { if (DateTime.Now >= nextFetch) { - var data = ecowittApi.GetCurrentData(cumulus.cancellationToken, ref delay); + try + { + + var data = ecowittApi.GetCurrentData(cumulus.cancellationToken, ref delay); - if (data != null) + if (data != null) + { + ProcessCurrentData(data, cumulus.cancellationToken); + } + cumulus.LogDebugMessage($"EcowittCloud; Waiting {delay} seconds before next update"); + nextFetch = DateTime.Now.AddSeconds(delay); + } + catch (Exception ex) { - ProcessCurrentData(data, cumulus.cancellationToken); + cumulus.LogExceptionMessage(ex, "Error running Ecowitt Cloud station"); + nextFetch = DateTime.Now.AddMinutes(1); } - cumulus.LogDebugMessage($"EcowittCloud; Waiting {delay} seconds before next update"); - nextFetch = DateTime.Now.AddSeconds(delay); } + Thread.Sleep(1000); } - } - catch (Exception ex) - { - cumulus.LogExceptionMessage(ex, "Error ruuning Ecowitt Cloud station"); - } }, cumulus.cancellationToken); } @@ -181,6 +189,8 @@ public override void getAndProcessHistoryData() GetHistoricData(); archiveRun++; } while (archiveRun < maxArchiveRuns); + + ecowittApi.GetStationList(cumulus.cancellationToken); } catch (Exception ex) { @@ -193,25 +203,43 @@ public override void getAndProcessHistoryData() StartLoop(); } - private void GetHistoricData() + public override string GetEcowittCameraUrl() { - cumulus.LogMessage("GetHistoricData: Starting Historic Data Process"); - - // add one minute to avoid duplicating the last log entry - var startTime = cumulus.LastUpdateTime.AddMinutes(1); - var endTime = DateTime.Now; - - // The API call is limited to fetching 24 hours of data - if ((endTime - startTime).TotalHours > 24.0) + if ((cumulus.EcowittExtraUseCamera || main) && cumulus.EcowittCameraMacAddress != null) { - // only fetch 24 hours worth of data, and schedule another run to fetch the rest - endTime = startTime.AddHours(24); - maxArchiveRuns++; + try + { + EcowittCameraUrl = ecowittApi.GetCurrentCameraImageUrl(cumulus.cancellationToken, EcowittCameraUrl); + return EcowittCameraUrl; + } + catch (Exception ex) + { + cumulus.LogExceptionMessage(ex, "Error runing Ecowitt Camera URL"); + } } - ecowittApi.GetHistoricData(startTime, endTime, cumulus.cancellationToken); + return null; + } + + private void GetHistoricData() + { + cumulus.LogMessage("GetHistoricData: Starting Historic Data Process"); + + // add one minute to avoid duplicating the last log entry + var startTime = cumulus.LastUpdateTime.AddMinutes(1); + var endTime = DateTime.Now; + + // The API call is limited to fetching 24 hours of data + if ((endTime - startTime).TotalHours > 24.0) + { + // only fetch 24 hours worth of data, and schedule another run to fetch the rest + endTime = startTime.AddHours(24); + maxArchiveRuns++; } + ecowittApi.GetHistoricData(startTime, endTime, cumulus.cancellationToken); + } + private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationToken token) { bool batteryLow = false; @@ -512,6 +540,7 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok DataStopped = false; cumulus.DataStoppedAlarm.Triggered = false; } + private void ProcessExtraTempHum(EcowittApi.CurrentDataData data, WeatherStation station) { if (data.temp_and_humidity_ch1 != null) diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index f85fc7e7..92dbe1ef 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -20,7 +20,7 @@ internal class GW1000Station : WeatherStation private int lastMinute; private bool tenMinuteChanged = true; - private EcowittApi EcowittApi; + private EcowittApi ecowittApi; private readonly GW1000Api Api; private int maxArchiveRuns = 1; @@ -246,7 +246,7 @@ public override void getAndProcessHistoryData() try { - EcowittApi = new EcowittApi(cumulus, this); + ecowittApi = new EcowittApi(cumulus, this); do { @@ -258,6 +258,9 @@ public override void getAndProcessHistoryData() { cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); } + + // get the station list + ecowittApi.GetStationList(cumulus.cancellationToken); } //cumulus.LogDebugMessage("Lock: Station releasing the lock"); @@ -287,7 +290,25 @@ private void GetHistoricData() maxArchiveRuns++; } - EcowittApi.GetHistoricData(startTime, endTime, cumulus.cancellationToken); + ecowittApi.GetHistoricData(startTime, endTime, cumulus.cancellationToken); + } + + public override string GetEcowittCameraUrl() + { + if (cumulus.EcowittCameraMacAddress != null) + { + try + { + EcowittCameraUrl = ecowittApi.GetCurrentCameraImageUrl(cumulus.cancellationToken, EcowittCameraUrl); + return EcowittCameraUrl; + } + catch (Exception ex) + { + cumulus.LogExceptionMessage(ex, "Error runing Ecowitt Camera URL"); + } + } + + return null; } private Discovery DiscoverGW1000() diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index e6908ff2..f6de90be 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -127,6 +127,8 @@ public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base } else { + // see if we have a camera attached + ecowittApi.GetStationList(cumulus.cancellationToken); cumulus.LogMessage("Extra Sensors - HTTP Station (Ecowitt) - Waiting for data..."); } } @@ -183,6 +185,8 @@ public override void getAndProcessHistoryData() GetHistoricData(); archiveRun++; } while (archiveRun < maxArchiveRuns); + + ecowittApi.GetStationList(cumulus.cancellationToken); } catch (Exception ex) { @@ -216,6 +220,24 @@ private void GetHistoricData() } + public override string GetEcowittCameraUrl() + { + if (cumulus.EcowittCameraMacAddress != null) + { + try + { + EcowittCameraUrl = ecowittApi.GetCurrentCameraImageUrl(cumulus.cancellationToken, EcowittCameraUrl); + return EcowittCameraUrl; + } + catch (Exception ex) + { + cumulus.LogExceptionMessage(ex, "Error runing Ecowitt Camera URL"); + } + } + + return null; + } + public string ProcessData(IHttpContext context, bool main, DateTime? ts = null) { /* diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 4441b613..9947c699 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -16,7 +16,6 @@ using System.Threading.Tasks; using System.Timers; using System.Web; -using System.Web.Compilation; using EmbedIO.Utilities; @@ -379,6 +378,11 @@ public struct DailyHighLow public abstract void Start(); + public virtual string GetEcowittCameraUrl() + { + return string.Empty; + } + public WeatherStation(Cumulus cumulus) { // save the reference to the owner diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 7353a5cd..8ae17ca3 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -398,7 +398,7 @@ public string GetCurrCondText() { res = string.Empty; } - return res; + return EncodeForJs(res); } private string GetFormattedDateTime(DateTime dt, Dictionary tagParams) @@ -3263,10 +3263,16 @@ private string Tagwebcamurl(Dictionary tagParams) private string TagEcowittCameraUrl(Dictionary tagParams) { - return string.IsNullOrEmpty(station.EcowittCameraUrl) ? string.Empty : station.EcowittCameraUrl; + if (cumulus.StationType == StationTypes.GW1000 || cumulus.StationType == StationTypes.HttpEcowitt || cumulus.StationType == StationTypes.EcowittCloud) + { + return string.IsNullOrEmpty(station.GetEcowittCameraUrl()) ? string.Empty : station.EcowittCameraUrl; + } + else + { + return string.Empty; + } } - private string Tagtempunit(Dictionary tagParams) { return EncodeForWeb(cumulus.Units.TempText); diff --git a/Updates.txt b/Updates.txt index 1ff629e0..210aabff 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,4 @@ -3.27.1 - b3260 +3.27.1 - b3261 —————————————— New - Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: <#LatestErrorEnc>, <#LatestErrorJsEnc> From f7be1ec5215d0fca4c1249097cd8963a9a2c9baa Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 16 Nov 2023 15:05:08 +0000 Subject: [PATCH 36/45] Add sorting to recent graph data selects --- CumulusMX/WeatherStation.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 9947c699..e362412d 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -2352,7 +2352,7 @@ public string GetSolarGraphData(bool incremental, bool local, DateTime? start = } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2422,7 +2422,7 @@ public string GetRainGraphData(bool incremental, DateTime? start = null) } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2463,7 +2463,7 @@ public string GetHumGraphData(bool incremental, bool local, DateTime? start = nu } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2522,7 +2522,7 @@ public string GetWindDirGraphData(bool incremental, DateTime? start = null) } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2562,7 +2562,7 @@ public string GetWindGraphData(bool incremental, DateTime? start = null) dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2601,7 +2601,7 @@ public string GetPressGraphData(bool incremental, DateTime? start = null) } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2639,7 +2639,7 @@ public string GetTempGraphData(bool incremental, bool local, DateTime? start = n dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { @@ -2776,7 +2776,7 @@ public string GetAqGraphData(bool incremental, DateTime? start = null) dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var data = RecentDataDb.Query("select * from RecentData where Timestamp > ?", dateFrom); + var data = RecentDataDb.Query("select * from RecentData where Timestamp > ? order by Timestamp", dateFrom); for (var i = 0; i < data.Count; i++) { From 5afd15cb97a841837044806fe9a5f2fcefb4b3a6 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 16 Nov 2023 15:30:08 +0000 Subject: [PATCH 37/45] Add web tags for RecentApparent, RecentIndoorTemp, RecentIndoorHumidity --- CumulusMX/webtags.cs | 30 ++++++++++++++++++++++++++++++ Updates.txt | 1 + 2 files changed, 31 insertions(+) diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 8ae17ca3..1207f4df 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -5463,6 +5463,15 @@ private string TagRecentFeelsLike(Dictionary tagParams) return CheckRcDp(CheckTempUnit(result.Count == 0 ? station.FeelsLike : result[0].FeelsLike, tagParams), tagParams, cumulus.TempDPlaces); } + private string TagRecentApparent(Dictionary tagParams) + { + var recentTs = GetRecentTs(tagParams); + + var result = station.RecentDataDb.Query("select * from RecentData where Timestamp >= ? order by Timestamp limit 1", recentTs); + + return CheckRcDp(CheckTempUnit(result.Count == 0 ? station.ApparentTemperature : result[0].AppTemp, tagParams), tagParams, cumulus.TempDPlaces); + } + private string TagRecentHumidex(Dictionary tagParams) { var recentTs = GetRecentTs(tagParams); @@ -5552,6 +5561,24 @@ private string TagRecentUv(Dictionary tagParams) return CheckRcDp(result.Count == 0 ? station.UV : result[0].UV, tagParams, cumulus.UVDPlaces); } + private string TagRecentIndoorTemp(Dictionary tagParams) + { + var recentTs = GetRecentTs(tagParams); + + var result = station.RecentDataDb.Query("select * from RecentData where Timestamp >= ? order by Timestamp limit 1", recentTs); + + return CheckRcDp(result.Count == 0 ? station.IndoorTemperature : result[0].IndoorTemp, tagParams, cumulus.TempDPlaces); + } + + private string TagRecentIndoorHumidity(Dictionary tagParams) + { + var recentTs = GetRecentTs(tagParams); + + var result = station.RecentDataDb.Query("select * from RecentData where Timestamp >= ? order by Timestamp limit 1", recentTs); + + return result.Count == 0 ? station.IndoorHumidity.ToString() : result[0].IndoorHumidity.ToString(); + } + private string TagRecentTs(Dictionary tagParams) { var recentTs = GetRecentTs(tagParams); @@ -6462,6 +6489,7 @@ public void InitialiseWebtags() { "RecentDewPoint", TagRecentDewPoint }, { "RecentHeatIndex", TagRecentHeatIndex }, { "RecentFeelsLike", TagRecentFeelsLike }, + { "RecentApparent", TagRecentApparent }, { "RecentHumidex", TagRecentHumidex }, { "RecentHumidity", TagRecentHumidity }, { "RecentPressure", TagRecentPressure }, @@ -6469,6 +6497,8 @@ public void InitialiseWebtags() { "RecentRainToday", TagRecentRainToday }, { "RecentSolarRad", TagRecentSolarRad }, { "RecentUV", TagRecentUv }, + { "RecentIndoorTemp", TagRecentIndoorTemp }, + { "RecentIndoorHumidity", TagRecentIndoorHumidity }, { "RecentTS", TagRecentTs }, // Recent history with comma decimals replaced { "RCRecentOutsideTemp", TagRcRecentOutsideTemp }, diff --git a/Updates.txt b/Updates.txt index 210aabff..91228a98 100644 --- a/Updates.txt +++ b/Updates.txt @@ -8,6 +8,7 @@ New - Like the existing <#windrun> it also accepts the "unit" parameter to return the value in miles, kilometers, or nautical miles - PHP upload advanced option to disable the use of GET for small files and fallback to using POST for all files - Ecowitt stations can now map the indoor temp/hum values to the primary outdoor sensor (primarily of use for 4G stations that do not allow extra T/H sensors to be added) +- New web tags for <#RecentApparent>, <#RecentIndoorTemp>, <#RecentIndoorHumidity> Changed - All third-party upload issues are now classified as Warnings From a3779a921d36543c0325b596eeec6bca81086bec Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Thu, 16 Nov 2023 15:37:09 +0000 Subject: [PATCH 38/45] Fix logic on Ecowitt API catch-up error messages for temp/hum --- CumulusMX/EcowittApi.cs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index b6720814..8ae34565 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -1306,16 +1306,16 @@ private void ApplyHistoricData(KeyValuePair rec) cumulus.LogWarningMessage("ApplyHistoricData: Missing indoor humidity data"); } - if (rec.Value.Humidity.HasValue) + if (cumulus.Gw1000PrimaryTHSensor == 0) { - if (cumulus.Gw1000PrimaryTHSensor == 0) + if (rec.Value.Humidity.HasValue) { station.DoOutdoorHumidity(rec.Value.Humidity.Value, rec.Key); } - } - else - { - cumulus.LogWarningMessage("ApplyHistoricData: Missing outdoor humidity data"); + else + { + cumulus.LogWarningMessage("ApplyHistoricData: Missing outdoor humidity data"); + } } } @@ -1373,17 +1373,17 @@ private void ApplyHistoricData(KeyValuePair rec) // = avg for period try { - if (rec.Value.Temp.HasValue) + if (cumulus.Gw1000PrimaryTHSensor == 0) { - if (cumulus.Gw1000PrimaryTHSensor == 0) + if (rec.Value.Temp.HasValue) { var tempVal = (double) rec.Value.Temp; station.DoOutdoorTemp(tempVal, rec.Key); } - } - else - { - cumulus.LogWarningMessage("ApplyHistoricData: Missing outdoor temperature data"); + else + { + cumulus.LogWarningMessage("ApplyHistoricData: Missing outdoor temperature data"); + } } } catch (Exception ex) From 098b61b50939dba97fcf0a513f8cc7cd008ccb40 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Fri, 17 Nov 2023 15:00:55 +0000 Subject: [PATCH 39/45] PHP compression test now rerun at each update interval until it succeeds --- CumulusMX/Cumulus.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index e60ba943..c167b409 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -9881,9 +9881,8 @@ public void DoHttpFiles(DateTime now) LogDebugMessage("ProcessHttpFiles: Uploading http files"); // do we perform a second chance compresssion test? - if (FtpOptions.PhpCompression == "none" && FtpOptions.PhpCompressionRetries == 0) + if (FtpOptions.PhpCompression == "notchecked") { - FtpOptions.PhpCompressionRetries++; TestPhpUploadCompression(); } @@ -11627,7 +11626,7 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s request.Method = HttpMethod.Post; // Compress? if supported and payload exceeds 500 bytes - if (data.Length >= 500 && FtpOptions.PhpCompression != "none") + if (data.Length >= 500 && (FtpOptions.PhpCompression == "gzip" || (FtpOptions.PhpCompression == "deflate") { using (var ms = new MemoryStream()) { @@ -14070,8 +14069,7 @@ public class FtpOptionsClass public string PhpUrl { get; set; } public string PhpSecret { get; set; } public bool PhpIgnoreCertErrors { get; set; } - public string PhpCompression { get; set; } = "none"; - public int PhpCompressionRetries { get; set; } + public string PhpCompression { get; set; } = "notchecked"; public int MaxConcurrentUploads { get; set; } public bool PhpUseGet { get; set; } } From 236b03ae39bdee675aeab962a387e607676c8b34 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Fri, 17 Nov 2023 15:01:51 +0000 Subject: [PATCH 40/45] typo fix --- CumulusMX/Cumulus.cs | 2 +- Updates.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index c167b409..91c2682d 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -11626,7 +11626,7 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s request.Method = HttpMethod.Post; // Compress? if supported and payload exceeds 500 bytes - if (data.Length >= 500 && (FtpOptions.PhpCompression == "gzip" || (FtpOptions.PhpCompression == "deflate") + if (data.Length >= 500 && (FtpOptions.PhpCompression == "gzip" || (FtpOptions.PhpCompression == "deflate")) { using (var ms = new MemoryStream()) { diff --git a/Updates.txt b/Updates.txt index 91228a98..519666ee 100644 --- a/Updates.txt +++ b/Updates.txt @@ -35,7 +35,7 @@ Fixed - Fix Wizard and Settings screens did not allow the entry of an IMEI number for the Ecowitt Cloud API device ID - Fix some potential errors in the dewpoint calculations on Ecowitt stations when using a mapped primary temp/hum sensor - Fix for zero length string error when parsing web tokens -- Perform a second chance PHP upload compression test at the first upload interval if no compression detected at start-up +- Perform a second chance PHP upload compression test at upload intervals if no compression detected at start-up 3.27.0 - b3257 From fd8a9a2b5e01f764ebf578ce8b3ce7f9dfec81ed Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Fri, 17 Nov 2023 15:02:18 +0000 Subject: [PATCH 41/45] typo fix --- CumulusMX/Cumulus.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 91c2682d..1e07eebe 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -11626,7 +11626,7 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s request.Method = HttpMethod.Post; // Compress? if supported and payload exceeds 500 bytes - if (data.Length >= 500 && (FtpOptions.PhpCompression == "gzip" || (FtpOptions.PhpCompression == "deflate")) + if (data.Length >= 500 && (FtpOptions.PhpCompression == "gzip" || FtpOptions.PhpCompression == "deflate")) { using (var ms = new MemoryStream()) { From 92abdeaf2a1f5f32c34c9983209844c1789e8574 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Fri, 17 Nov 2023 15:41:27 +0000 Subject: [PATCH 42/45] Improved PHP upload testing at intervals, and make the function synchronous. --- CumulusMX/Cumulus.cs | 11 +++++++++-- CumulusMX/CumulusMX.csproj | 2 +- Updates.txt | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 1e07eebe..509251d4 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -1755,7 +1755,7 @@ internal void SetupPhpUploadClients() } - internal async void TestPhpUploadCompression() + internal void TestPhpUploadCompression() { LogDebugMessage($"Testing PHP upload compression: '{FtpOptions.PhpUrl}'"); using (var request = new HttpRequestMessage(HttpMethod.Get, FtpOptions.PhpUrl)) @@ -1765,7 +1765,8 @@ internal async void TestPhpUploadCompression() request.Headers.Add("Accept", "text/html"); request.Headers.Add("Accept-Encoding", "gzip, deflate"); - var response = await phpUploadHttpClient.SendAsync(request); + // we do this async + var response = phpUploadHttpClient.SendAsync(request).Result; response.EnsureSuccessStatusCode(); var encoding = response.Content.Headers.ContentEncoding; @@ -10565,6 +10566,12 @@ public async Task DoIntervalUpload() var taskCount = 0; var runningTaskCount = 0; + // do we perform a second chance compresssion test? + if (FtpOptions.PhpCompression == "notchecked") + { + TestPhpUploadCompression(); + } + if (NOAAconf.NeedFtp) { // upload NOAA Monthly report diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index fb14192a..f25e4fc0 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -77,7 +77,7 @@ CumulusMX.Program AnyCPU Copyright © 2015-2023 Cumulus MX - 3.27.1.3261 + 3.27.1.3262 diff --git a/Updates.txt b/Updates.txt index 519666ee..6ad0014f 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,4 @@ -3.27.1 - b3261 +3.27.1 - b3262 —————————————— New - Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: <#LatestErrorEnc>, <#LatestErrorJsEnc> From 19cc5f1b123773781dbf0e77af0c2bec7a023d89 Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Fri, 17 Nov 2023 15:43:39 +0000 Subject: [PATCH 43/45] Make PHP compression test in Internet Settings async --- CumulusMX/InternetSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CumulusMX/InternetSettings.cs b/CumulusMX/InternetSettings.cs index 467185c9..bc7cdf47 100644 --- a/CumulusMX/InternetSettings.cs +++ b/CumulusMX/InternetSettings.cs @@ -3,6 +3,7 @@ using System.IO; using System.Net; using System.Text; +using System.Threading.Tasks; using System.Web; using EmbedIO; @@ -66,7 +67,7 @@ public string UpdateConfig(IHttpContext context) if (cumulus.phpUploadHttpClient == null) { cumulus.SetupPhpUploadClients(); - cumulus.TestPhpUploadCompression(); + Task.Run(() => cumulus.TestPhpUploadCompression()); } } cumulus.FtpOptions.FtpMode = (Cumulus.FtpProtocols) settings.website.sslftp; From 44c97b23c43de95e2f8268e8f7471312c48d1efd Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Mon, 20 Nov 2023 15:47:39 +0000 Subject: [PATCH 44/45] Fix uploads being disabled at strtup when using HP Fix uncaught exception on start-up for Ecowitt Extra Sensors --- CumulusMX/Cumulus.cs | 10 ++++++++-- CumulusMX/GW1000Station.cs | 5 ++--- CumulusMX/HttpStationEcowitt.cs | 5 ++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 509251d4..fac97b12 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -4794,7 +4794,8 @@ private void ReadIniFile() FtpOptions.Username = ini.GetValue("FTP site", "Username", ""); FtpOptions.Password = ini.GetValue("FTP site", "Password", ""); FtpOptions.Directory = ini.GetValue("FTP site", "Directory", ""); - if (FtpOptions.Hostname == "" && FtpOptions.Enabled) + FtpOptions.FtpMode = (FtpProtocols) ini.GetValue("FTP site", "Sslftp", 0); + if (FtpOptions.Enabled && FtpOptions.Hostname == "" && FtpOptions.FtpMode != FtpProtocols.PHP) { FtpOptions.Enabled = false; rewriteRequired = true; @@ -4803,7 +4804,6 @@ private void ReadIniFile() FtpOptions.AutoDetect = ini.GetValue("FTP site", "ConnectionAutoDetect", false); FtpOptions.IgnoreCertErrors = ini.GetValue("FTP site", "IgnoreCertErrors", false); FtpOptions.ActiveMode = ini.GetValue("FTP site", "ActiveFTP", false); - FtpOptions.FtpMode = (FtpProtocols) ini.GetValue("FTP site", "Sslftp", 0); // BUILD 3092 - added alternate SFTP authentication options FtpOptions.SshAuthen = ini.GetValue("FTP site", "SshFtpAuthentication", "password"); if (!sshAuthenticationVals.Contains(FtpOptions.SshAuthen)) @@ -4848,6 +4848,12 @@ private void ReadIniFile() FtpOptions.MaxConcurrentUploads = ini.GetValue("FTP site", "MaxConcurrentUploads", boolWindows ? 4 : 1); FtpOptions.PhpUseGet = ini.GetValue("FTP site", "PHP-UseGet", true); + if (FtpOptions.Enabled && FtpOptions.PhpUrl == "" && FtpOptions.FtpMode == FtpProtocols.PHP) + { + FtpOptions.Enabled = false; + rewriteRequired = true; + } + MoonImage.Ftp = ini.GetValue("FTP site", "IncludeMoonImage", false); MoonImage.Copy = ini.GetValue("FTP site", "CopyMoonImage", false); diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index 92dbe1ef..f7faa898 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -102,6 +102,8 @@ public GW1000Station(Cumulus cumulus) : base(cumulus) Api = new GW1000Api(cumulus); + ecowittApi = new EcowittApi(cumulus, this); + if (DoDiscovery()) { PostDiscovery(); @@ -245,9 +247,6 @@ public override void getAndProcessHistoryData() try { - - ecowittApi = new EcowittApi(cumulus, this); - do { GetHistoricData(); diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index f6de90be..7ece95bd 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -120,6 +120,8 @@ public HttpStationEcowitt(Cumulus cumulus, WeatherStation station = null) : base cumulus.Units.LeafWetnessUnitText = "%"; } + ecowittApi = new EcowittApi(cumulus, this); + // Only perform the Start-up if we are a proper station, not a Extra Sensor if (mainStation) { @@ -177,9 +179,6 @@ public override void getAndProcessHistoryData() try { - - ecowittApi = new EcowittApi(cumulus, this); - do { GetHistoricData(); From 0d0e53438a05c3c9919c0732e7136a8cb0b4c1de Mon Sep 17 00:00:00 2001 From: Mark Crossley Date: Mon, 20 Nov 2023 15:48:56 +0000 Subject: [PATCH 45/45] Increment build # for release --- CumulusMX/CumulusMX.csproj | 2 +- Updates.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index f25e4fc0..1c35417c 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -77,7 +77,7 @@ CumulusMX.Program AnyCPU Copyright © 2015-2023 Cumulus MX - 3.27.1.3262 + 3.27.1.3263 diff --git a/Updates.txt b/Updates.txt index 6ad0014f..fadba150 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,4 @@ -3.27.1 - b3262 +3.27.1 - b3263 —————————————— New - Two new web tags for the Latest Error. These tags will encode the string for either HTML or JavaScript: <#LatestErrorEnc>, <#LatestErrorJsEnc>