diff --git a/CumulusMX/AirQualityIndices.cs b/CumulusMX/AirQualityIndices.cs index 24ff6ca6..383c7954 100644 --- a/CumulusMX/AirQualityIndices.cs +++ b/CumulusMX/AirQualityIndices.cs @@ -73,7 +73,7 @@ public static int US_EPApm2p5(double pmVal) retVal = Interpolate(0, 12, pmVal) * 50; } //return (Ihigh - Ilow) / (Chigh - Clow) * (pmVal - Clow) + Ilow; - return (int)Math.Round(retVal); + return (int) Math.Round(retVal); } /* @@ -145,7 +145,7 @@ public static int US_EPApm10(double pmVal) retVal = Interpolate(0, 54, pmVal) * 50; } //return (Ihigh - Ilow) / (Chigh - Clow) * (pmVal - Clow) + Ilow; - return (int)Math.Round(retVal); + return (int) Math.Round(retVal); } @@ -215,15 +215,15 @@ public static double UK_COMEAPpm10(double pmVal) */ public static double EU_AQIpm2p5h1(double pmVal) { - if (pmVal > 110) // Very High + if (pmVal > 110) // Very High return 5; - else if (pmVal >= 55) // High + else if (pmVal >= 55) // High return 4 + Interpolate(55, 110, pmVal); - else if (pmVal >= 30) // Medium + else if (pmVal >= 30) // Medium return 3 + Interpolate(30, 55, pmVal); - else if (pmVal >= 15) // Low + else if (pmVal >= 15) // Low return 2 + Interpolate(15, 30, pmVal); - else // Very Low + else // Very Low return 1 + Interpolate(0, 15, pmVal); } @@ -233,15 +233,15 @@ public static double EU_AQIpm2p5h1(double pmVal) */ public static double EU_AQI2p5h24(double pmVal) { - if (pmVal > 60) // Very High + if (pmVal > 60) // Very High return 5; - else if (pmVal >= 30) // High + else if (pmVal >= 30) // High return 4 + Interpolate(30, 60, pmVal); - else if (pmVal >= 20) // Medium + else if (pmVal >= 20) // Medium return 3 + Interpolate(20, 30, pmVal); - else if (pmVal >= 10) // Low + else if (pmVal >= 10) // Low return 2 + Interpolate(10, 20, pmVal); - else // Very Low + else // Very Low return 1 + Interpolate(0, 10, pmVal); } @@ -251,15 +251,15 @@ public static double EU_AQI2p5h24(double pmVal) */ public static double EU_AQI10h1(double pmVal) { - if (pmVal > 180) // Very High + if (pmVal > 180) // Very High return 5; - else if (pmVal >= 90) // High + else if (pmVal >= 90) // High return 4 + Interpolate(90, 180, pmVal); - else if (pmVal >= 50) // Medium + else if (pmVal >= 50) // Medium return 3 + Interpolate(50, 90, pmVal); - else if (pmVal >= 25) // Low + else if (pmVal >= 25) // Low return 2 + Interpolate(25, 50, pmVal); - else // Very Low + else // Very Low return 1 + Interpolate(0, 25, pmVal); } @@ -269,15 +269,15 @@ public static double EU_AQI10h1(double pmVal) */ public static double EU_AQI10h24(double pmVal) { - if (pmVal > 100) // Very High + if (pmVal > 100) // Very High return 5; - else if (pmVal >= 50) // High + else if (pmVal >= 50) // High return 4 + Interpolate(50, 100, pmVal); - else if (pmVal >= 30) // Medium + else if (pmVal >= 30) // Medium return 3 + Interpolate(30, 50, pmVal); - else if (pmVal >= 15) // Low + else if (pmVal >= 15) // Low return 2 + Interpolate(15, 30, pmVal); - else // Very Low + else // Very Low return 1 + Interpolate(0, 15, pmVal); } @@ -288,7 +288,7 @@ public static double EU_AQI10h24(double pmVal) */ public static int CA_AQHI(double pmVal) { - var aqi = (int)(1000 / 10.4 * (Math.Exp(0.000487 * pmVal) - 1)); + var aqi = (int) (1000 / 10.4 * (Math.Exp(0.000487 * pmVal) - 1)); return aqi < 1 ? 1 : aqi; } diff --git a/CumulusMX/Alarm.cs b/CumulusMX/Alarm.cs index 4abfc13f..356bf3c0 100644 --- a/CumulusMX/Alarm.cs +++ b/CumulusMX/Alarm.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Threading.Tasks; namespace CumulusMX diff --git a/CumulusMX/AlarmSettings.cs b/CumulusMX/AlarmSettings.cs index 6749c9df..2861cb10 100644 --- a/CumulusMX/AlarmSettings.cs +++ b/CumulusMX/AlarmSettings.cs @@ -1,9 +1,11 @@ using System; using System.IO; using System.Net; -using ServiceStack; + using EmbedIO; +using ServiceStack; + namespace CumulusMX { public class AlarmSettings @@ -308,6 +310,158 @@ public string GetAlarmSettings() return retObject.ToJson(); } + public string GetAlarmInfo() + { + //var InvC = new CultureInfo(""); + + var data = new JsonAlarmInfoData() + { + tempBelow = new JsonAlarmInfo() + { + Enabled = cumulus.LowTempAlarm.Enabled, + SoundEnabled = cumulus.LowTempAlarm.Sound, + Sound = cumulus.LowTempAlarm.SoundFile, + Notify = cumulus.LowTempAlarm.Notify + }, + tempAbove = new JsonAlarmInfo() + { + Enabled = cumulus.HighTempAlarm.Enabled, + SoundEnabled = cumulus.HighTempAlarm.Sound, + Sound = cumulus.HighTempAlarm.SoundFile, + Notify = cumulus.HighTempAlarm.Notify + }, + tempChange = new JsonAlarmInfo() + { + Enabled = cumulus.TempChangeAlarm.Enabled, + SoundEnabled = cumulus.TempChangeAlarm.Sound, + Sound = cumulus.TempChangeAlarm.SoundFile, + Notify = cumulus.TempChangeAlarm.Notify + }, + pressBelow = new JsonAlarmInfo() + { + Enabled = cumulus.LowPressAlarm.Enabled, + SoundEnabled = cumulus.LowPressAlarm.Sound, + Sound = cumulus.LowPressAlarm.SoundFile, + Notify = cumulus.LowPressAlarm.Notify + }, + pressAbove = new JsonAlarmInfo() + { + Enabled = cumulus.HighPressAlarm.Enabled, + SoundEnabled = cumulus.HighPressAlarm.Sound, + Sound = cumulus.HighPressAlarm.SoundFile, + Notify = cumulus.HighPressAlarm.Notify + }, + pressChange = new JsonAlarmInfo() + { + Enabled = cumulus.PressChangeAlarm.Enabled, + SoundEnabled = cumulus.PressChangeAlarm.Sound, + Sound = cumulus.PressChangeAlarm.SoundFile, + Notify = cumulus.PressChangeAlarm.Notify + }, + rainAbove = new JsonAlarmInfo() + { + Enabled = cumulus.HighRainTodayAlarm.Enabled, + SoundEnabled = cumulus.HighRainTodayAlarm.Sound, + Sound = cumulus.HighRainTodayAlarm.SoundFile, + Notify = cumulus.HighRainTodayAlarm.Notify + }, + rainRateAbove = new JsonAlarmInfo() + { + Enabled = cumulus.HighRainRateAlarm.Enabled, + SoundEnabled = cumulus.HighRainRateAlarm.Sound, + Sound = cumulus.HighRainRateAlarm.SoundFile, + Notify = cumulus.HighRainRateAlarm.Notify + }, + isRaining = new JsonAlarmInfo() + { + Enabled = cumulus.IsRainingAlarm.Enabled, + SoundEnabled = cumulus.IsRainingAlarm.Sound, + Sound = cumulus.IsRainingAlarm.SoundFile, + Notify = cumulus.IsRainingAlarm.Notify + }, + gustAbove = new JsonAlarmInfo() + { + Enabled = cumulus.HighGustAlarm.Enabled, + SoundEnabled = cumulus.HighGustAlarm.Sound, + Sound = cumulus.HighGustAlarm.SoundFile, + Notify = cumulus.HighGustAlarm.Notify + }, + windAbove = new JsonAlarmInfo() + { + Enabled = cumulus.HighWindAlarm.Enabled, + SoundEnabled = cumulus.HighWindAlarm.Sound, + Sound = cumulus.HighWindAlarm.SoundFile, + Notify = cumulus.HighWindAlarm.Notify + }, + contactLost = new JsonAlarmInfo() + { + Enabled = cumulus.SensorAlarm.Enabled, + SoundEnabled = cumulus.SensorAlarm.Sound, + Sound = cumulus.SensorAlarm.SoundFile, + Notify = cumulus.SensorAlarm.Notify + }, + newRecord = new JsonAlarmInfo() + { + Enabled = cumulus.NewRecordAlarm.Enabled, + SoundEnabled = cumulus.NewRecordAlarm.Sound, + Sound = cumulus.NewRecordAlarm.SoundFile, + Notify = cumulus.NewRecordAlarm.Notify + }, + dataStopped = new JsonAlarmInfo() + { + Enabled = cumulus.DataStoppedAlarm.Enabled, + SoundEnabled = cumulus.DataStoppedAlarm.Sound, + Sound = cumulus.DataStoppedAlarm.SoundFile, + Notify = cumulus.DataStoppedAlarm.Notify + }, + batteryLow = new JsonAlarmInfo() + { + Enabled = cumulus.BatteryLowAlarm.Enabled, + SoundEnabled = cumulus.BatteryLowAlarm.Sound, + Sound = cumulus.BatteryLowAlarm.SoundFile, + Notify = cumulus.BatteryLowAlarm.Notify + }, + spike = new JsonAlarmInfo() + { + Enabled = cumulus.SpikeAlarm.Enabled, + SoundEnabled = cumulus.SpikeAlarm.Sound, + Sound = cumulus.SpikeAlarm.SoundFile, + Notify = cumulus.SpikeAlarm.Notify + }, + upgrade = new JsonAlarmInfo() + { + Enabled = cumulus.UpgradeAlarm.Enabled, + SoundEnabled = cumulus.UpgradeAlarm.Sound, + Sound = cumulus.UpgradeAlarm.SoundFile, + Notify = cumulus.UpgradeAlarm.Notify + }, + httpUpload = new JsonAlarmInfo() + { + Enabled = cumulus.ThirdPartyAlarm.Enabled, + SoundEnabled = cumulus.ThirdPartyAlarm.Sound, + Sound = cumulus.ThirdPartyAlarm.SoundFile, + Notify = cumulus.ThirdPartyAlarm.Notify + }, + mySqlUpload = new JsonAlarmInfo() + { + Enabled = cumulus.MySqlUploadAlarm.Enabled, + SoundEnabled = cumulus.MySqlUploadAlarm.Sound, + Sound = cumulus.MySqlUploadAlarm.SoundFile, + Notify = cumulus.MySqlUploadAlarm.Notify + }, + ftpUpload = new JsonAlarmInfo() + { + Enabled = cumulus.FtpAlarm.Enabled, + SoundEnabled = cumulus.FtpAlarm.Sound, + Sound = cumulus.FtpAlarm.SoundFile, + Notify = cumulus.FtpAlarm.Notify + } + }; + + return data.ToJson(); + } + + public string UpdateAlarmSettings(IHttpContext context) { var json = ""; @@ -331,7 +485,7 @@ public string UpdateAlarmSettings(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Alarm Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Alarm Data: " + json); context.Response.StatusCode = 500; return msg; @@ -586,7 +740,7 @@ public string UpdateAlarmSettings(IHttpContext context) if (emailRequired && !EmailSender.CheckEmailAddress(result.email.fromEmail.Trim())) { var msg = "ERROR: Alarm email option enabled and an invalid Alarm from email address entered"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -603,7 +757,7 @@ public string UpdateAlarmSettings(IHttpContext context) if (!EmailSender.CheckEmailAddress(emails[i])) { var msg = "ERROR: Invalid Alarm destination email address entered"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -619,7 +773,7 @@ public string UpdateAlarmSettings(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage("Error processing Alarm settings: " + ex.Message); + cumulus.LogErrorMessage("Error processing Alarm settings: " + ex.Message); cumulus.LogDebugMessage("Alarm Data: " + json); context.Response.StatusCode = 500; return ex.Message; @@ -645,7 +799,7 @@ public string TestEmail(IHttpContext context) if (!EmailSender.CheckEmailAddress(result.fromEmail.Trim())) { var msg = "ERROR: Invalid Alarm from email address entered"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -659,7 +813,7 @@ public string TestEmail(IHttpContext context) if (!EmailSender.CheckEmailAddress(dest[i])) { var msg = "ERROR: Invalid Alarm destination email address entered"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -727,6 +881,39 @@ public class JsonAlarmValues public string ActionParams { get; set; } } + public class JsonAlarmInfoData + { + public JsonAlarmInfo tempBelow { get; set; } + public JsonAlarmInfo tempAbove { get; set; } + public JsonAlarmInfo tempChange { get; set; } + public JsonAlarmInfo pressBelow { get; set; } + public JsonAlarmInfo pressAbove { get; set; } + public JsonAlarmInfo pressChange { get; set; } + public JsonAlarmInfo rainAbove { get; set; } + public JsonAlarmInfo rainRateAbove { get; set; } + public JsonAlarmInfo gustAbove { get; set; } + public JsonAlarmInfo windAbove { get; set; } + public JsonAlarmInfo newRecord { get; set; } + public JsonAlarmInfo contactLost { get; set; } + public JsonAlarmInfo dataStopped { get; set; } + public JsonAlarmInfo batteryLow { get; set; } + public JsonAlarmInfo spike { get; set; } + public JsonAlarmInfo upgrade { get; set; } + public JsonAlarmInfo httpUpload { get; set; } + public JsonAlarmInfo mySqlUpload { get; set; } + public JsonAlarmInfo isRaining { get; set; } + public JsonAlarmInfo ftpUpload { get; set; } + } + + + public class JsonAlarmInfo + { + public bool Enabled { get; set; } + public bool SoundEnabled { get; set; } + public string Sound { get; set; } + public bool Notify { get; set; } + } + public class JsonAlarmEmail { public string fromEmail { get; set; } diff --git a/CumulusMX/Api.cs b/CumulusMX/Api.cs index f06260fd..7bd46568 100644 --- a/CumulusMX/Api.cs +++ b/CumulusMX/Api.cs @@ -1,25 +1,25 @@ using System; -using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web; + using EmbedIO; using EmbedIO.Routing; using EmbedIO.WebApi; -using SQLite; -using Swan.Formatters; + namespace CumulusMX { public static class Api { internal static WeatherStation Station; + internal static Cumulus cumulus; public static ProgramSettings programSettings; internal static StationSettings stationSettings; public static InternetSettings internetSettings; - public static ThirdPartySettings thirdpartySettings; + public static ThirdPartySettings thirdpartySettings; public static ExtraSensorSettings extraSensorSettings; public static CalibrationSettings calibrationSettings; public static NOAASettings noaaSettings; @@ -47,7 +47,7 @@ private static string EscapeUnicode(string input) if (ch <= 0x7f) sb.Append(ch); else - sb.AppendFormat(CultureInfo.InvariantCulture, "\\u{0:x4}", (int)ch); + sb.AppendFormat(CultureInfo.InvariantCulture, "\\u{0:x4}", (int) ch); } return sb.ToString(); } @@ -127,7 +127,7 @@ public async Task GetEditData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/edit: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/edit: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -145,6 +145,11 @@ public async Task PostEditData(string req) try { + if (!(await Authenticate(HttpContext))) + { + return; + } + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) { string res; @@ -214,7 +219,7 @@ public async Task PostEditData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/edit: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/edit: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -273,16 +278,26 @@ public async Task GetData(string req) await writer.WriteAsync(Station.GetDiarySummary()); break; case "mysqlcache.json": - await writer.WriteAsync(Station.GetCachedSqlCommands(draw, start, length, search)); + if (await Authenticate(HttpContext)) + { + await writer.WriteAsync(Station.GetCachedSqlCommands(draw, start, length, search)); + } + break; + case "errorlog.json": + if (await Authenticate(HttpContext)) + { + await writer.WriteAsync(cumulus.GetErrorLog()); + } break; default: Response.StatusCode = 404; break; } - } } + } + } catch (Exception ex) { - Program.cumulus.LogMessage($"api/data: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/data: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -323,7 +338,7 @@ public async Task PostTags(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/tags: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/tags: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -358,7 +373,7 @@ public async Task GetTags(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/tags: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/tags: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -382,11 +397,20 @@ public async Task GetGraphData(string req) var incremental = false; DateTime? start = null; + DateTime? end = null; - if (this.Request.QueryString.AllKeys.Contains("start") && long.TryParse(this.Request.QueryString.Get("start"), out long ts)) + if (Request.QueryString.AllKeys.Contains("start") && long.TryParse(Request.QueryString.Get("start"), out long ts)) { start = Utils.FromUnixTime(ts); - incremental = true; + if (!Request.QueryString.AllKeys.Contains("end")) + incremental = true; + } + + if (Request.QueryString.AllKeys.Contains("end") && long.TryParse(Request.QueryString.Get("end"), out ts)) + { + end = Utils.FromUnixTime(ts); + if (end > DateTime.Now) + end = DateTime.Now; } try @@ -395,6 +419,7 @@ public async Task GetGraphData(string req) { switch (req) { + // recent data case "tempdata.json": await writer.WriteAsync(Station.GetTempGraphData(incremental, true, start)); break; @@ -416,21 +441,6 @@ public async Task GetGraphData(string req) case "solardata.json": await writer.WriteAsync(Station.GetSolarGraphData(incremental, true, start)); break; - case "dailyrain.json": - await writer.WriteAsync(Station.GetDailyRainGraphData()); - break; - case "sunhours.json": - await writer.WriteAsync(Station.GetSunHoursGraphData(true)); - break; - case "dailytemp.json": - await writer.WriteAsync(Station.GetDailyTempGraphData(true)); - break; - case "units.json": - await writer.WriteAsync(Station.GetUnits()); - break; - case "graphconfig.json": - await writer.WriteAsync(Station.GetGraphConfig(true)); - break; case "airqualitydata.json": await writer.WriteAsync(Station.GetAqGraphData(incremental, start)); break; @@ -458,20 +468,90 @@ public async Task GetGraphData(string req) case "co2sensor.json": await writer.WriteAsync(Station.GetCo2SensorGraphData(incremental, true, start)); break; + + // daily data + case "dailyrain.json": + await writer.WriteAsync(Station.GetDailyRainGraphData()); + break; + case "sunhours.json": + await writer.WriteAsync(Station.GetSunHoursGraphData(true)); + break; + case "dailytemp.json": + await writer.WriteAsync(Station.GetDailyTempGraphData(true)); + break; + case "units.json": + await writer.WriteAsync(Station.GetUnits()); + break; + + // interval data + case "intvtemp.json": + await writer.WriteAsync(Station.GetIntervalTempGraphData(true, start, end)); + break; + case "intvwind.json": + await writer.WriteAsync(Station.GetIntervalWindGraphData(true, start, end)); + break; + case "intvrain.json": + await writer.WriteAsync(Station.GetIntervalRainGraphData(true, start, end)); + break; + case "intvpress.json": + await writer.WriteAsync(Station.GetIntervaPressGraphData(true, start, end)); + break; + case "intvhum.json": + await writer.WriteAsync(Station.GetIntervalHumGraphData(true, start, end)); + break; + case "intvsolar.json": + await writer.WriteAsync(Station.GetIntervalSolarGraphData(true, start, end)); + break; + case "intvairquality.json": + // TODO + break; + case "intvextratemp.json": + await writer.WriteAsync(Station.GetExtraTempGraphData(false, true, start, end)); + break; + case "intvextrahum.json": + await writer.WriteAsync(Station.GetExtraHumGraphData(false, true, start, end)); + break; + case "intvextradew.json": + await writer.WriteAsync(Station.GetExtraDewPointGraphData(false, true, start, end)); + break; + case "intvsoiltemp.json": + await writer.WriteAsync(Station.GetSoilTempGraphData(false, true, start, end)); + break; + case "intvsoilmoist.json": + await writer.WriteAsync(Station.GetSoilMoistGraphData(false, true, start, end)); + break; + case "intvleafwetness.json": + await writer.WriteAsync(Station.GetLeafWetnessGraphData(false, true, start, end)); + break; + case "intvusertemp.json": + await writer.WriteAsync(Station.GetUserTempGraphData(false, true, start, end)); + break; + case "intvco2sensor.json": + // TODO + break; + + // config data + case "graphconfig.json": + await writer.WriteAsync(Station.GetGraphConfig(true)); + break; case "availabledata.json": await writer.WriteAsync(Station.GetAvailGraphData(true)); break; case "selectachart.json": await writer.WriteAsync(Station.GetSelectaChartOptions()); break; + case "selectaperiod.json": + await writer.WriteAsync(Station.GetSelectaPeriodOptions()); + break; default: Response.StatusCode = 404; break; } - } } + } + } catch (Exception ex) { - Program.cumulus.LogMessage($"api/graphdata: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/graphdata: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -498,6 +578,9 @@ public async Task SetGraphData(string req) case "selectachart.json": await writer.WriteAsync(stationSettings.SetSelectaChartOptions(HttpContext)); break; + case "selectaperiod.json": + await writer.WriteAsync(stationSettings.SetSelectaPeriodOptions(HttpContext)); + break; default: Response.StatusCode = 404; break; @@ -507,7 +590,7 @@ public async Task SetGraphData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/graphdata: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/graphdata: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -572,7 +655,7 @@ public async Task GetDailyGraphData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/dailygraphdata: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/dailygraphdata: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -622,7 +705,7 @@ public async Task GetAlltimeData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/records/alltime: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/records/alltime: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -677,7 +760,7 @@ public async Task GetMonthlyRecordData(string mon, string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/records/month: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/records/month: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -724,7 +807,7 @@ public async Task GetThisMonthRecordData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/records/thismonth: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/records/thismonth: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -770,13 +853,13 @@ public async Task GetThisYearRecordData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/records/thisyear: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/records/thisyear: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } [Route(HttpVerbs.Get, "/records/thisperiod")] - public async Task GetThisPeriodRecordData(string req) + public async Task GetThisPeriodRecordData() { Response.ContentType = "application/json"; @@ -799,7 +882,7 @@ public async Task GetThisPeriodRecordData(string req) if (query.AllKeys.Contains("startdate")) { // we expect "yyyy-mm-dd" - var start = query["startdate"].Split('-'); + var start = query["startdate"].Split('-'); if (!Int32.TryParse(start[0], out startyear) || startyear < 2000 || startyear > 2050) { @@ -871,7 +954,7 @@ public async Task GetThisPeriodRecordData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/records/thisperiod: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/records/thisperiod: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -925,7 +1008,7 @@ public async Task GetYesterdayData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/todayyest: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/todayyest: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1011,7 +1094,7 @@ public async Task GetExtraData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/extra: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/extra: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1023,36 +1106,15 @@ public class SettingsController : WebApiController [Route(HttpVerbs.Get, "/settings/{req}")] public async Task SettingsGet(string req) { - /* string authorization = context.Request.Headers["Authorization"]; - string userInfo; - string username = ""; - string password = ""; - if (authorization != null) - { - byte[] tempConverted = Convert.FromBase64String(authorization.Replace("Basic ", "").Trim()); - userInfo = System.Text.Encoding.UTF8.GetString(tempConverted); - string[] usernamePassword = userInfo.Split(new string[] {":"}, StringSplitOptions.RemoveEmptyEntries); - username = usernamePassword[0]; - password = usernamePassword[1]; - Console.WriteLine("username = "+username+" password = "+password); - } - else - { - var errorResponse = new - { - Title = "Authentication required", - ErrorCode = "Authentication required", - Description = "You must authenticate", - }; - - context.Response.StatusCode = 401; - return context.JsonResponse(errorResponse); - }*/ - try { Response.ContentType = "application/json"; + if (!(await Authenticate(HttpContext))) + { + return; + } + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) { switch (req) @@ -1084,12 +1146,6 @@ public async Task SettingsGet(string req) case "noaadata.json": await writer.WriteAsync(noaaSettings.GetAlpacaFormData()); break; - case "wsport.json": - await writer.WriteAsync(stationSettings.GetWSport()); - break; - case "version.json": - await writer.WriteAsync(stationSettings.GetVersion()); - break; case "mysqldata.json": await writer.WriteAsync(mySqlSettings.GetAlpacaFormData()); break; @@ -1099,14 +1155,6 @@ public async Task SettingsGet(string req) case "wizard.json": await writer.WriteAsync(wizard.GetAlpacaFormData()); break; - case "dateformat.txt": - Response.ContentType = "text/plain"; - await writer.WriteAsync(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); - break; - case "csvseparator.txt": - Response.ContentType = "text/plain"; - await writer.WriteAsync(CultureInfo.CurrentCulture.TextInfo.ListSeparator); - break; case "customlogsintvl.json": await writer.WriteAsync(customLogs.GetAlpacaFormDataIntvl()); break; @@ -1127,7 +1175,7 @@ public async Task SettingsGet(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/settings: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/settings: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1139,6 +1187,11 @@ public async Task SettingsSet(string req) { Response.ContentType = "application/json"; + if (!(await Authenticate(HttpContext))) + { + return; + } + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) { switch (req) @@ -1220,7 +1273,7 @@ public async Task SettingsSet(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/setsettings: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/setsettings: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1232,7 +1285,7 @@ public class ReportsController : WebApiController [Route(HttpVerbs.Get, "/reports/{req}")] public async Task GetData(string req) { - NOAAReports noaarpts = new NOAAReports(Program.cumulus, Station); + NOAAReports noaarpts = new NOAAReports(cumulus, Station); try { var query = HttpUtility.ParseQueryString(Request.Url.Query); @@ -1272,7 +1325,7 @@ public async Task GetData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/reports: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/reports: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1280,28 +1333,38 @@ public async Task GetData(string req) [Route(HttpVerbs.Get, "/genreports/{req}")] public async Task GenReports(string req) { - NOAAReports noaarpts = new NOAAReports(Program.cumulus, Station); + NOAAReports noaarpts = new NOAAReports(cumulus, Station); try { + if (!(await Authenticate(HttpContext))) + { + return; + } + var query = HttpUtility.ParseQueryString(Request.Url.Query); int month, year; Response.ContentType = "text/plain"; using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) { - if (!Int32.TryParse(query["year"], out year) || year < 2000 || year > 2050) - { - await writer.WriteAsync("Invalid year supplied: " + year); - Response.StatusCode = 406; - return; - } - switch (req) { case "noaayear": + if (!Int32.TryParse(query["year"], out year) || year < 2000 || year > 2050) + { + await writer.WriteAsync("Invalid year supplied: " + year); + Response.StatusCode = 406; + return; + } await writer.WriteAsync(noaarpts.GenerateNoaaYearReport(year)); break; case "noaamonth": + if (!Int32.TryParse(query["year"], out year) || year < 2000 || year > 2050) + { + await writer.WriteAsync("Invalid year supplied: " + year); + Response.StatusCode = 406; + return; + } if (!Int32.TryParse(query["month"], out month) || month < 1 || month > 12) { await writer.WriteAsync("Invalid month supplied: " + month); @@ -1310,6 +1373,9 @@ public async Task GenReports(string req) } await writer.WriteAsync(noaarpts.GenerateNoaaMonthReport(year, month)); break; + case "all": + await writer.WriteAsync(noaarpts.GenerateMissing()); + break; default: Response.StatusCode = 404; throw new Exception(); @@ -1321,7 +1387,7 @@ public async Task GenReports(string req) //using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) // await writer.WriteAsync($"{{\"Title\":\"Unexpected Error\",\"ErrorCode\":\"{ex.GetType().Name}\",\"Description\":\"{ex.Message}\"}}"); Response.StatusCode = 500; - Program.cumulus.LogMessage($"api/genreports: Unexpected Error, ErrorCode: {ex.GetType().Name}, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/genreports: Unexpected Error, ErrorCode: {ex.GetType().Name}, Description: \"{ex.Message}\""); } } } @@ -1370,7 +1436,7 @@ public async Task PostTags(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/httpstation: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/httpstation: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1430,7 +1496,7 @@ public async Task GetStation(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/httpstation: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/httpstation: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1454,6 +1520,11 @@ public async Task GetUtilData(string req) try { + if (!(await Authenticate(HttpContext))) + { + return; + } + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) { switch (req) @@ -1463,7 +1534,7 @@ public async Task GetUtilData(string req) break; case "purgemysql": var cnt = 0; - while (Program.cumulus.MySqlFailedList.TryDequeue(out var item)) + while (cumulus.MySqlFailedList.TryDequeue(out var item)) { cnt++; }; @@ -1487,7 +1558,7 @@ public async Task GetUtilData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/utils: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/utils: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } @@ -1505,6 +1576,11 @@ public async Task PostUtilsData(string req) try { + if (!(await Authenticate(HttpContext))) + { + return; + } + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) { switch (req) @@ -1520,10 +1596,100 @@ public async Task PostUtilsData(string req) } catch (Exception ex) { - Program.cumulus.LogMessage($"api/edit: Unexpected Error, Description: \"{ex.Message}\""); + cumulus.LogErrorMessage($"api/edit: Unexpected Error, Description: \"{ex.Message}\""); Response.StatusCode = 500; } } } + + // Info + public class InfoController : WebApiController + { + [Route(HttpVerbs.Get, "/info/{req}")] + public async Task InfoGet(string req) + { + try + { + Response.ContentType = "application/json"; + using (var writer = HttpContext.OpenResponseText(new UTF8Encoding(false))) + { + switch (req) + { + case "wsport.json": + await writer.WriteAsync(stationSettings.GetWSport()); + break; + case "version.json": + await writer.WriteAsync(stationSettings.GetVersion()); + break; + case "dateformat.txt": + Response.ContentType = "text/plain"; + await writer.WriteAsync(CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern); + break; + case "csvseparator.txt": + Response.ContentType = "text/plain"; + await writer.WriteAsync(CultureInfo.CurrentCulture.TextInfo.ListSeparator); + break; + case "alarms.json": + await writer.WriteAsync(alarmSettings.GetAlarmInfo()); + break; + default: + Response.StatusCode = 404; + break; + } + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"api/info: Unexpected Error, Description: \"{ex.Message}\""); + Response.StatusCode = 500; + } + } + } + private static async Task Authenticate(IHttpContext context) + { + string authorization = context.Request.Headers["Authorization"]; + string userInfo; + string username; + string password; + + if (cumulus.ProgramOptions.SecureSettings) + { + if (authorization != null) + { + byte[] tempConverted = Convert.FromBase64String(authorization.Replace("Basic ", "").Trim()); + userInfo = Encoding.UTF8.GetString(tempConverted); + string[] usernamePassword = userInfo.Split(new char[] { ':' }); + username = usernamePassword[0] ?? string.Empty; + password = usernamePassword[1] ?? string.Empty; + + if (username == cumulus.ProgramOptions.SettingsUsername && password == cumulus.ProgramOptions.SettingsPassword) + { + return true; + } + else + { + context.Response.StatusCode = 401; + context.Response.ContentType = "application/json"; + context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"My Realm\""); + using (var writer = context.OpenResponseText(new UTF8Encoding(false))) + await writer.WriteAsync("{\"Title\":\"Authentication required\",\"ErrorCode\":\"Authentication required\",\"Description\":\"You must authenticate\"}"); + + return false; + } + } + else + { + context.Response.StatusCode = 401; + context.Response.ContentType = "application/json"; + context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"My Realm\""); + using (var writer = context.OpenResponseText(new UTF8Encoding(false))) + await writer.WriteAsync("{\"Title\":\"Authentication required\",\"ErrorCode\":\"Authentication required\",\"Description\":\"You must authenticate\"}"); + + return false; + } + } + + return true; + } } } diff --git a/CumulusMX/ApiTagProcessor.cs b/CumulusMX/ApiTagProcessor.cs index 5506c072..dc7d5a68 100644 --- a/CumulusMX/ApiTagProcessor.cs +++ b/CumulusMX/ApiTagProcessor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text; + using EmbedIO; namespace CumulusMX @@ -69,7 +70,7 @@ public string ProcessJson(IHttpRequest request) } catch (Exception ex) { - cumulus.LogMessage($"API tag: Error - {ex.Message}"); + cumulus.LogErrorMessage($"API tag: Error - {ex.Message}"); output.Append($"\"ERROR\":\"{ex.Message}\"}}"); } @@ -98,7 +99,7 @@ public string ProcessText(IHttpRequest request) } catch (Exception ex) { - cumulus.LogMessage($"API tag: Error - {ex.Message}"); + cumulus.LogErrorMessage($"API tag: Error - {ex.Message}"); return $"{{\"ERROR\":\"{ex.Message}\"}}"; } } diff --git a/CumulusMX/App.config b/CumulusMX/App.config index 68243e26..b6794241 100644 --- a/CumulusMX/App.config +++ b/CumulusMX/App.config @@ -10,48 +10,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/CumulusMX/AstroLib.cs b/CumulusMX/AstroLib.cs index 688e689f..44f10695 100644 --- a/CumulusMX/AstroLib.cs +++ b/CumulusMX/AstroLib.cs @@ -524,7 +524,7 @@ private static void CalculateSunPosition(DateTime dateTime, double latitude, dou var zone = 0.0; var julianDate = dateTime.ToOADate() + 2415018.5; - var julianCentry = (julianDate - 2451545 ) / 36525.0; + var julianCentry = (julianDate - 2451545) / 36525.0; var geoMeanLongSun = PutIn360Deg(280.46646 + julianCentry * (36000.76983 + julianCentry * 0.0003032)); var geoMeanAnomSun = 357.52911 + julianCentry * (35999.05029 - 0.0001537 * julianCentry); var eccEarthOrbit = 0.016708634 - julianCentry * (0.000042037 + 0.0000001267 * julianCentry); @@ -558,7 +558,7 @@ private static void CalculateSunPosition(DateTime dateTime, double latitude, dou else if (solarElevation > -0.575) refraction = 1735.0 + solarElevation * (-518.2 + solarElevation * (103.4 + solarElevation * (-12.79 + solarElevation * 0.711))); else - refraction = -20.772 / Math.Tan(DegToRad(solarElevation)); + refraction = -20.772 / Math.Tan(DegToRad(solarElevation)); altitude = solarElevation + refraction / 3600.0; @@ -730,18 +730,18 @@ private static double CalcSunDistance(DateTime dDate, DateTime dEpoch) //double fOblique = GetEarthObliquity(dEpoch, true); const double fSMA = 149598500.0; - double fN = (360.0/365.242191)*fD; + double fN = (360.0 / 365.242191) * fD; fN = PutIn360Deg(fN); double fM = fN + fSolarMEL - fSolarPL; fM = PutIn360Deg(fM); fM = DegToRad(fM); double fE = CalcEccentricAnomaly(fM, fM, fSunEarthEcc, fAcc); - double fTanV2 = Math.Sqrt((1.0 + fSunEarthEcc)/(1.0 - fSunEarthEcc))*Math.Tan(fE/2.0); - double fV = Math.Atan(fTanV2)*2.0; - double fDistance = (fSMA*(1.0 - (fSunEarthEcc*fSunEarthEcc)))/(1.0 + (fSunEarthEcc*Math.Cos(fV))); + double fTanV2 = Math.Sqrt((1.0 + fSunEarthEcc) / (1.0 - fSunEarthEcc)) * Math.Tan(fE / 2.0); + double fV = Math.Atan(fTanV2) * 2.0; + double fDistance = (fSMA * (1.0 - (fSunEarthEcc * fSunEarthEcc))) / (1.0 + (fSunEarthEcc * Math.Cos(fV))); // Convert from km to AU - return fDistance/149597871.0; + return fDistance / 149597871.0; } private static double GetSunEarthEcc(DateTime dDate, bool b0Epoch) @@ -753,8 +753,8 @@ private static double GetSunEarthEcc(DateTime dDate, bool b0Epoch) { fJD -= 1.0; } - double fT = (fJD - 2415020.0)/36525.0; - double fEcc = 0.01675104 - (0.0000418*fT) - (0.000000126*fT*fT); + double fT = (fJD - 2415020.0) / 36525.0; + double fEcc = 0.01675104 - (0.0000418 * fT) - (0.000000126 * fT * fT); return fEcc; } @@ -783,10 +783,10 @@ private static double CalcEccentricAnomaly(double fEGuess, double fMA, double fE double fEG = fEGuess; - double fDelta = fEG - (fEcc*Math.Sin(fEG)) - fMA; + double fDelta = fEG - (fEcc * Math.Sin(fEG)) - fMA; if (Math.Abs(fDelta) > fAcc) { - double fDeltaE = (fDelta/(1.0 - (fEcc*Math.Cos(fEG)))); + double fDeltaE = (fDelta / (1.0 - (fEcc * Math.Cos(fEG)))); double fETmp = fEG - fDeltaE; fE = CalcEccentricAnomaly(fETmp, fMA, fEcc, fAcc); } @@ -809,7 +809,7 @@ public static long GetDaysBetween(DateTime dDate, DateTime dSecDate) } else { - fDays = (long) Math.Floor(Math.Abs(fDays))*-1; + fDays = (long) Math.Floor(Math.Abs(fDays)) * -1; } return (long) Math.Floor(fDays); } @@ -827,7 +827,7 @@ private static double GetJulianDay(DateTime dDate, int iZone) int iHour = dDate.Hour; int iMinute = dDate.Minute; int iSecond = dDate.Second; - double fFrac = iDay + ((iHour + (iMinute/60) + (iSecond/60/60))/24); + double fFrac = iDay + ((iHour + (iMinute / 60) + (iSecond / 60 / 60)) / 24); iGreg = iYear < 1582 ? 0 : 1; if ((iMonth == 1) || (iMonth == 2)) { @@ -836,16 +836,16 @@ private static double GetJulianDay(DateTime dDate, int iZone) } double fA = (long) Math.Floor(iYear / 100.0); - double fB = (2 - fA + (long) Math.Floor(fA/4))*iGreg; + double fB = (2 - fA + (long) Math.Floor(fA / 4)) * iGreg; if (iYear < 0) { - fC = (int) Math.Floor((365.25*iYear) - 0.75); + fC = (int) Math.Floor((365.25 * iYear) - 0.75); } else { - fC = (int) Math.Floor(365.25*iYear); + fC = (int) Math.Floor(365.25 * iYear); } - double fD = (int) Math.Floor(30.6001*(iMonth + 1)); + double fD = (int) Math.Floor(30.6001 * (iMonth + 1)); double fJD = fB + fC + fD + 1720994.5; fJD += fFrac; return fJD; @@ -871,8 +871,8 @@ private static double GetSolarMEL(DateTime dDate, bool b0Epoch) fJD -= 1.0; } - double fT = (fJD - 2415020.0)/36525.0; - double fLong = 279.6966778 + (36000.76892*fT) + (0.0003025*fT*fT); + double fT = (fJD - 2415020.0) / 36525.0; + double fLong = 279.6966778 + (36000.76892 * fT) + (0.0003025 * fT * fT); fLong = PutIn360Deg(fLong); return fLong; } @@ -888,8 +888,8 @@ private static double GetSolarPerigeeLong(DateTime dDate, bool b0Epoch) fJD -= 1.0; } - double fT = (fJD - 2415020.0)/36525.0; - double fLong = 281.2208444 + (1.719175*fT) + (0.000452778*fT*fT); + double fT = (fJD - 2415020.0) / 36525.0; + double fLong = 281.2208444 + (1.719175 * fT) + (0.000452778 * fT * fT); fLong = PutIn360Deg(fLong); return fLong; } diff --git a/CumulusMX/CalibrationSettings.cs b/CumulusMX/CalibrationSettings.cs index 6aba6d31..07015436 100644 --- a/CumulusMX/CalibrationSettings.cs +++ b/CumulusMX/CalibrationSettings.cs @@ -2,9 +2,11 @@ using System.Globalization; using System.IO; using System.Net; -using ServiceStack; + using EmbedIO; +using ServiceStack; + namespace CumulusMX { public class CalibrationSettings @@ -35,7 +37,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Calibration Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Calibration Data: " + json); context.Response.StatusCode = 500; return msg; @@ -47,7 +49,7 @@ public string UpdateConfig(IHttpContext context) cumulus.LogMessage("Updating calibration settings"); // offsets - cumulus.Calib.Press.Offset = Convert.ToDouble(settings.pressure.offset,invC); + cumulus.Calib.Press.Offset = Convert.ToDouble(settings.pressure.offset, invC); cumulus.Calib.Temp.Offset = Convert.ToDouble(settings.temp.offset, invC); cumulus.Calib.InTemp.Offset = Convert.ToDouble(settings.tempin.offset, invC); cumulus.Calib.Hum.Offset = settings.hum.offset; @@ -115,7 +117,7 @@ public string UpdateConfig(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage("Error setting Calibration settings: " + ex.Message); + cumulus.LogErrorMessage("Error setting Calibration settings: " + ex.Message); cumulus.LogDebugMessage("Calibration Data: " + json); context.Response.StatusCode = 500; return ex.Message; @@ -155,7 +157,7 @@ public string GetAlpacaFormData() var hum = new JsonCalibrationSettings() { - offset = (int)cumulus.Calib.Hum.Offset, + offset = (int) cumulus.Calib.Hum.Offset, multiplier = cumulus.Calib.Hum.Mult, multiplier2 = cumulus.Calib.Hum.Mult2, spike = cumulus.Spike.HumidityDiff @@ -186,7 +188,7 @@ public string GetAlpacaFormData() var winddir = new JsonCalibrationSettings() { - offset = (int)cumulus.Calib.WindDir.Offset + offset = (int) cumulus.Calib.WindDir.Offset }; var rain = new JsonCalibrationSettings() diff --git a/CumulusMX/CalibrationsLimits.cs b/CumulusMX/CalibrationsLimits.cs index 10d01b99..f9baf185 100644 --- a/CumulusMX/CalibrationsLimits.cs +++ b/CumulusMX/CalibrationsLimits.cs @@ -46,12 +46,12 @@ public double Calibrate(double value) public class Limits { - public double TempHigh = 60; // Celsius + public double TempHigh = 60; // Celsius public double TempLow = -60; // Celsius public double DewHigh = 40; // Celsius - public double PressHigh = 1090; // hPa - public double PressLow = 870; // hPa - public double WindHigh = 90; // m/s + public double PressHigh = 1090; // hPa + public double PressLow = 870; // hPa + public double WindHigh = 90; // m/s } public class Spikes diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index 491f1ab6..a0edabdf 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -1,19 +1,6 @@ -using EmbedIO; -using EmbedIO.WebApi; -using EmbedIO.Files; -using EmbedIO.Utilities; -using FluentFTP; -using FluentFTP.Helpers; -using MySqlConnector; -using Renci.SshNet; -using SQLite; -using ServiceStack; -using ServiceStack.Text; -using Swan; using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Data.Common; using System.Diagnostics; using System.Globalization; using System.IO; @@ -21,15 +8,10 @@ using System.Linq; using System.Net; using System.Net.Http; -using static System.Net.Mime.MediaTypeNames; -using System.Net.Sockets; using System.Net.NetworkInformation; -using System.Numerics; +using System.Net.Sockets; using System.Reflection; using System.Runtime.InteropServices; -using System.Runtime.InteropServices.ComTypes; -using System.Runtime.CompilerServices; -using System.Runtime.Serialization; using System.Security.AccessControl; using System.Security.Principal; using System.Text; @@ -37,11 +19,27 @@ using System.Threading; using System.Threading.Tasks; using System.Timers; + +using EmbedIO; +using EmbedIO.Files; +using EmbedIO.Utilities; +using EmbedIO.WebApi; + +using FluentFTP; +using FluentFTP.Helpers; + +using MySqlConnector; + +using Renci.SshNet; + +using ServiceStack; +using ServiceStack.Text; + +using SQLite; + +using Swan; + using Timer = System.Timers.Timer; -using System.Web; -using System.Web.Caching; -using System.Web.UI.WebControls; -using System.Xml.Linq; namespace CumulusMX { @@ -128,6 +126,16 @@ public enum PrimaryAqSensor EcowittCO2 = 6 } + public enum LogLevel + { + Info = 0, + Warning = 1, + Error = 2, + Critical = 3 + } + + public LogLevel ErrorListLoggingLevel = LogLevel.Warning; + private readonly string[] sshAuthenticationVals = { "password", "psk", "password_psk" }; /* @@ -229,6 +237,7 @@ public struct TExtraFiles internal HttpStationEcowitt ecowittExtra; internal HttpStationAmbient ambientExtra; + internal EcowittCloudStation ecowittCloudExtra; public DateTime LastUpdateTime; @@ -371,10 +380,6 @@ public struct TExtraFiles public int RecordSetTimeoutHrs = 24; - private const int VP2SERIALCONNECTION = 0; - //private const int VP2USBCONNECTION = 1; - //private const int VP2TCPIPCONNECTION = 2; - public string AlltimeIniFile; public string Alltimelogfile; public string MonthlyAlltimeIniFile; @@ -399,8 +404,8 @@ public struct TExtraFiles private List OWMList = new List(); // Use thread safe queues for the MySQL command lists - private ConcurrentQueue MySqlList = new ConcurrentQueue(); - public ConcurrentQueue MySqlFailedList = new ConcurrentQueue(); + private readonly ConcurrentQueue MySqlList = new ConcurrentQueue(); + public readonly ConcurrentQueue MySqlFailedList = new ConcurrentQueue(); // Calibration settings /// @@ -434,6 +439,8 @@ public struct TExtraFiles public GraphOptions GraphOptions = new GraphOptions(); public SelectaChartOptions SelectaChartOptions = new SelectaChartOptions(); + public SelectaChartOptions SelectaPeriodOptions = new SelectaChartOptions(); + public DisplayOptions DisplayOptions = new DisplayOptions(); @@ -635,17 +642,22 @@ public struct MqttSettings "HTTP Ecowitt", // 14 "HTTP Ambient", // 15 "WeatherFlow Tempest", // 16 - "Simulator" // 17 + "Simulator", // 17 + "Ecowitt Cloud", // 18 + "Davis Cloud (WLL/WLC)", // 19 + "Davis Cloud (VP2)" // 20 }; - public string[] APRSstationtype = { "DsVP", "DsVP", "WMR928", "WM918", "EW", "FO", "WS2300", "FOs", "WMR100", "WMR200", "IMET", "DsVP", "Ecow", "Unkn", "Ecow", "Ambt", "Tmpt", "Simul" }; + public string[] APRSstationtype = { "DsVP", "DsVP", "WMR928", "WM918", "EW", "FO", "WS2300", "FOs", "WMR100", "WMR200", "IMET", "DsVP", "Ecow", "Unkn", "Ecow", "Ambt", "Tmpt", "Simul", "Ecow", "DsVP", "DsVP" }; public string loggingfile; public string ftpLogfile; + public static Queue ErrorList = new Queue(50); + //private PingReply pingReply; - private SemaphoreSlim uploadCountLimitSemaphoreSlim; + private readonly SemaphoreSlim uploadCountLimitSemaphoreSlim; // Global cancellation token for when CMX is stopping public readonly CancellationTokenSource tokenSource = new CancellationTokenSource(); @@ -744,7 +756,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) catch (FileNotFoundException) { var msg = "Error: cannot find the file: " + srcfile; - LogMessage(msg); + LogErrorMessage(msg); LogConsoleMessage(msg); Program.exitSystem = true; return; @@ -752,7 +764,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) catch (Exception ex) { var msg = $"Error copying file {srcfile}: {ex.Message}"; - LogMessage(msg); + LogErrorMessage(msg); LogConsoleMessage(msg); Program.exitSystem = true; return; @@ -774,7 +786,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) } catch (Exception e) { - LogMessage("Error: Unable to access the System Up Time performance counter. System up time will not be available"); + LogErrorMessage("Error: Unable to access the System Up Time performance counter. System up time will not be available"); LogDebugMessage($"Error: {e}"); } } @@ -1164,7 +1176,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) } catch (Exception e) { - LogMessage($"PING #{cnt} to {ProgramOptions.StartupPingHost} failed with error: {e.InnerException.Message}"); + LogErrorMessage($"PING #{cnt} to {ProgramOptions.StartupPingHost} failed with error: {e.InnerException.Message}"); } } @@ -1182,7 +1194,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) if (DateTime.Now >= pingTimeoutDT) { // yep, so attempt to cancel the task - LogMessage($"Nothing returned from PING #{attempt}, attempting the cancel the task"); + LogErrorMessage($"Nothing returned from PING #{attempt}, attempting the cancel the task"); pingTokenSource.Cancel(); // and double the timeout for next attempt pingTimeoutSecs *= 2; @@ -1204,7 +1216,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) } catch (Exception ex) { - LogMessage($"PING #{attempt}: Error with DNS refresh - {ex.Message}"); + LogErrorMessage($"PING #{attempt}: Error with DNS refresh - {ex.Message}"); } } } @@ -1213,7 +1225,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) if (DateTime.Now >= escapeTime) { LogConsoleMessage(msg3, ConsoleColor.Yellow); - LogMessage(msg3); + LogWarningMessage(msg3); } else { @@ -1236,7 +1248,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) } catch (Exception ex) { - LogMessage($"Error running start-up task: {ex.Message}"); + LogErrorMessage($"Error running start-up task: {ex.Message}"); } } @@ -1263,7 +1275,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) if (duplicates.Count > 0) { LogConsoleMessage($"WARNING: Duplicate entries ({duplicates.Count}) found in your Weather Diary database - please see log file for details"); - LogMessage($"Duplicate entries ({duplicates.Count}) found in the Weather Diary database. The following entries will be removed..."); + LogWarningMessage($"Duplicate entries ({duplicates.Count}) found in the Weather Diary database. The following entries will be removed..."); foreach (var rec in duplicates) { @@ -1397,8 +1409,6 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) // switch off logging from Unosquare.Swan which underlies embedIO Swan.Logging.Logger.NoLogging(); - httpFiles = new HttpFiles(this); - var assemblyPath = Path.GetDirectoryName(typeof(Program).Assembly.Location); var htmlRootPath = Path.Combine(assemblyPath, "interface"); @@ -1421,6 +1431,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) .WithController() .WithController() .WithController() + .WithController() ) .WithWebApi("/station", m => m .WithController() @@ -1432,6 +1443,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) // Set up the API web server // Some APi functions require the station, so set them after station initialisation + Api.cumulus = this; Api.programSettings = new ProgramSettings(this); Api.stationSettings = new StationSettings(this); Api.internetSettings = new InternetSettings(this); @@ -1442,7 +1454,6 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) Api.alarmSettings = new AlarmSettings(this); Api.mySqlSettings = new MysqlSettings(this); Api.customLogs = new CustomLogs(this); - Api.httpFiles = httpFiles; Api.dataEditor = new DataEditor(this); Api.tagProcessor = new ApiTagProcessor(this); Api.wizard = new Wizard(this); @@ -1485,7 +1496,9 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) syncInit.Wait(); //LogDebugMessage("Lock: Cumulus has lock"); - LogMessage("Opening station"); + LogMessage("Opening station type " + StationType); + if (StationType >= 0 && StationType < StationDesc.Length) + LogConsoleMessage($"Opening station type {StationType} - {StationDesc[StationType]}"); switch (StationType) { @@ -1556,9 +1569,19 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) Manufacturer = SIMULATOR; station = new Simulator(this); break; + case StationTypes.EcowittCloud: + Manufacturer = ECOWITT; + station = new EcowittCloudStation(this); + break; + case StationTypes.DavisCloudWll: + case StationTypes.DavisCloudVP2: + Manufacturer = DAVIS; + station = new DavisCloudStation(this); + break; + default: LogConsoleMessage("Station type not set", ConsoleColor.Red); - LogMessage("Station type not set"); + LogMessage("Station type not set = " + StationType); break; } @@ -1582,33 +1605,49 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) Api.stationAmbient = (HttpStationAmbient) station; } - LogMessage("Creating extra sensors"); if (AirLinkInEnabled) { + LogMessage("Creating indoor AirLink station"); + LogConsoleMessage($"Opening indoor AirLink"); airLinkDataIn = new AirLinkData(); airLinkIn = new DavisAirLink(this, true, station); } if (AirLinkOutEnabled) { + LogMessage("Creating outdoor AirLink station"); + LogConsoleMessage($"Opening outdoor AirLink"); airLinkDataOut = new AirLinkData(); airLinkOut = new DavisAirLink(this, false, station); } if (EcowittExtraEnabled) { + LogMessage("Creating Ecowitt extra sensors station"); + LogConsoleMessage($"Opening Ecowitt extra sensors"); ecowittExtra = new HttpStationEcowitt(this, station); Api.stationEcowittExtra = ecowittExtra; } if (AmbientExtraEnabled) { + LogMessage("Creating Ambient extra sensors station"); + LogConsoleMessage($"Opening Ambient extra sensors"); ambientExtra = new HttpStationAmbient(this, station); Api.stationAmbientExtra = ambientExtra; } + if (EcowittCloudExtraEnabled) + { + LogMessage("Creating Ecowitt cloud extra sensors station"); + LogConsoleMessage($"Opening Ecowitt cloud extra sensors"); + ecowittCloudExtra = new EcowittCloudStation(this, station); + } 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; RealtimeTimer.Elapsed += RealtimeTimerTick; @@ -1646,6 +1685,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) InitialiseRG11(); + // Do the start-up MySQL commands before the station is started if (MySqlSettings.CustomStartUp.Enabled) { @@ -1702,15 +1742,15 @@ internal void SetupPhpUploadClients() ClientCertificateOptions = ClientCertificateOption.Manual, ServerCertificateCustomValidationCallback = (sender, cert, chain, errors) => { - return FtpOptions.PhpIgnoreCertErrors ? true : errors == System.Net.Security.SslPolicyErrors.None; + return FtpOptions.PhpIgnoreCertErrors || errors == System.Net.Security.SslPolicyErrors.None; }, - MaxConnectionsPerServer = 50, + MaxConnectionsPerServer = 20, AllowAutoRedirect = false }; phpUploadHttpClient = new HttpClient(phpUploadHttpHandler); - - phpUploadHttpClient.Timeout = TimeSpan.FromSeconds(20); + // 5 second timeout + phpUploadHttpClient.Timeout = new TimeSpan(0, 0, 5); } @@ -1723,6 +1763,7 @@ internal async void TestPhpUploadCompression() { request.Headers.Add("Accept", "text/html"); request.Headers.Add("Accept-Encoding", "gzip, deflate"); + var response = await phpUploadHttpClient.SendAsync(request); response.EnsureSuccessStatusCode(); var encoding = response.Content.Headers.ContentEncoding; @@ -1737,6 +1778,9 @@ internal async void TestPhpUploadCompression() { LogDebugMessage($"PHP upload supports {FtpOptions.PhpCompression} compression"); } + + // Check the max requests + CheckPhpMaxUploads(response.Headers); } catch (Exception ex) { @@ -1745,6 +1789,28 @@ internal async void TestPhpUploadCompression() } } + private void CheckPhpMaxUploads(System.Net.Http.Headers.HttpResponseHeaders headers) + { + if (headers.Connection.Contains("keep-alive")) + { + var keepalive = headers.Connection.ToString(); + //LogDebugMessage("PHP upload, server responded with Keep-Alive: " + keepalive); + if (keepalive.ContainsCI("max=")) + { + var regex = new Regex(@"max[\s]*=[\s]*([\d]+)"); + var match = regex.Match(keepalive); + if (regex.IsMatch(keepalive)) + { + LogDebugMessage($"PHP upload - will reset connection after {match.Groups[1].Value} requests"); + } + } + if (keepalive.ContainsCI("max=")) + { + LogDebugMessage("PHP upload - remote server has closed the connection"); + } + } + } + private void CustomHttpSecondsTimerTick(object sender, ElapsedEventArgs e) { if (!station.DataStopped) @@ -2114,12 +2180,12 @@ private void RG11StateChange(object sender, SerialPinChangedEventArgs e) bool isCTS = e.EventType == SerialPinChange.CtsChanged; // Is this a trigger that the first RG11 is configured for? - bool isDevice1 = (((SerialPort)sender).PortName == RG11Port) && ((isDSR && RG11DTRmode) || (isCTS && !RG11DTRmode)); + bool isDevice1 = (((SerialPort) sender).PortName == RG11Port) && ((isDSR && RG11DTRmode) || (isCTS && !RG11DTRmode)); // Is this a trigger that the second RG11 is configured for? - bool isDevice2 = (((SerialPort)sender).PortName == RG11Port2) && ((isDSR && RG11DTRmode2) || (isCTS && !RG11DTRmode2)); + bool isDevice2 = (((SerialPort) sender).PortName == RG11Port2) && ((isDSR && RG11DTRmode2) || (isCTS && !RG11DTRmode2)); // is the pin on or off? - bool isOn = (isDSR && ((SerialPort)sender).DsrHolding) || (isCTS && ((SerialPort)sender).CtsHolding); + bool isOn = (isDSR && ((SerialPort) sender).DsrHolding) || (isCTS && ((SerialPort) sender).CtsHolding); if (isDevice1) { @@ -2167,12 +2233,12 @@ private void WebTimerTick(object sender, ElapsedEventArgs e) if (WebUpdating == 1) { - LogMessage("Warning, previous web update is still in progress, first chance, skipping this interval"); + LogWarningMessage("Warning, previous web update is still in progress, first chance, skipping this interval"); WebUpdating++; } else if (WebUpdating >= 2) { - LogMessage("Warning, previous web update is still in progress, second chance, aborting connection"); + LogWarningMessage("Warning, previous web update is still in progress, second chance, aborting connection"); if (ftpThread.ThreadState == System.Threading.ThreadState.Running) ftpThread.Abort(); LogMessage("Trying new web update"); @@ -2270,7 +2336,7 @@ internal async void UpdateWunderground(DateTime timestamp) } catch (Exception ex) { - LogMessage("Wunderground: ERROR - " + ex.Message); + LogWarningMessage("Wunderground: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "Wunderground: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2304,7 +2370,7 @@ internal async void UpdateWindy(DateTime timestamp) LogDebugMessage("Windy: Response = " + response.StatusCode + ": " + responseBodyAsText); if (response.StatusCode != HttpStatusCode.OK) { - LogMessage("Windy: ERROR - Response = " + response.StatusCode + ": " + responseBodyAsText); + LogWarningMessage("Windy: ERROR - Response = " + response.StatusCode + ": " + responseBodyAsText); ThirdPartyAlarm.LastMessage = "Windy: HTTP response - " + response.StatusCode; ThirdPartyAlarm.Triggered = true; } @@ -2316,7 +2382,7 @@ internal async void UpdateWindy(DateTime timestamp) } catch (Exception ex) { - LogMessage("Windy: ERROR - " + ex.Message); + LogErrorMessage("Windy: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "Windy: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2350,7 +2416,7 @@ internal async void UpdateWindGuru(DateTime timestamp) LogDebugMessage("WindGuru: " + response.StatusCode + ": " + responseBodyAsText); if (response.StatusCode != HttpStatusCode.OK) { - LogMessage("WindGuru: ERROR - " + response.StatusCode + ": " + responseBodyAsText); + LogWarningMessage("WindGuru: ERROR - " + response.StatusCode + ": " + responseBodyAsText); ThirdPartyAlarm.LastMessage = "WindGuru: HTTP response - " + response.StatusCode; ThirdPartyAlarm.Triggered = true; } @@ -2362,7 +2428,7 @@ internal async void UpdateWindGuru(DateTime timestamp) } catch (Exception ex) { - LogMessage("WindGuru: ERROR - " + ex.Message); + LogErrorMessage("WindGuru: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "WindGuru: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2402,7 +2468,7 @@ internal async void UpdateAwekas(DateTime timestamp) if (response.StatusCode != HttpStatusCode.OK) { - LogMessage($"AWEKAS: ERROR - Response code = {response.StatusCode}, body = {responseBodyAsText}"); + LogWarningMessage($"AWEKAS: ERROR - Response code = {response.StatusCode}, body = {responseBodyAsText}"); ThirdPartyAlarm.LastMessage = $"AWEKAS: HTTP Response code = {response.StatusCode}, body = {responseBodyAsText}"; ThirdPartyAlarm.Triggered = true; AWEKAS.Updating = false; @@ -2421,7 +2487,7 @@ internal async void UpdateAwekas(DateTime timestamp) catch (Exception ex) { LogMessage("AWEKAS: Exception deserializing response = " + ex.Message); - LogMessage($"AWEKAS: ERROR - Response body = {responseBodyAsText}"); + LogErrorMessage($"AWEKAS: ERROR - Response body = {responseBodyAsText}"); ThirdPartyAlarm.LastMessage = "AWEKAS deserializing response: " + ex.Message; ThirdPartyAlarm.Triggered = true; AWEKAS.Updating = false; @@ -2442,7 +2508,7 @@ internal async void UpdateAwekas(DateTime timestamp) { if (respJson.minuploadtime > 0 && respJson.authentication == 0) { - LogMessage("AWEKAS: Authentication error"); + LogWarningMessage("AWEKAS: Authentication error"); if (AWEKAS.Interval < 300) { AWEKAS.RateLimited = true; @@ -2455,7 +2521,7 @@ internal async void UpdateAwekas(DateTime timestamp) } else if (respJson.minuploadtime == 0) { - LogMessage("AWEKAS: Too many requests, rate limited"); + LogWarningMessage("AWEKAS: Too many requests, rate limited"); // AWEKAS PLus allows minimum of 60 second updates, try that first if (!AWEKAS.RateLimited && AWEKAS.Interval < 60) { @@ -2479,7 +2545,7 @@ internal async void UpdateAwekas(DateTime timestamp) } else { - LogMessage("AWEKAS: Unknown error"); + LogWarningMessage("AWEKAS: Unknown error"); ThirdPartyAlarm.LastMessage = "AWEKAS: Unknown error"; ThirdPartyAlarm.Triggered = true; } @@ -2514,7 +2580,7 @@ internal async void UpdateAwekas(DateTime timestamp) } catch (Exception ex) { - LogMessage("AWEKAS: Exception = " + ex.Message); + LogErrorMessage("AWEKAS: Exception = " + ex.Message); ThirdPartyAlarm.LastMessage = "AWEKAS: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2584,12 +2650,12 @@ internal async void UpdateWCloud(DateTime timestamp) if ((int) response.StatusCode == 200) LogDebugMessage($"WeatherCloud: Response = {msg} ({response.StatusCode}): {responseBodyAsText}"); else - LogMessage($"WeatherCloud: ERROR - Response = {msg} ({response.StatusCode}): {responseBodyAsText}"); + LogWarningMessage($"WeatherCloud: ERROR - Response = {msg} ({response.StatusCode}): {responseBodyAsText}"); } } catch (Exception ex) { - LogMessage("WeatherCloud: ERROR - " + ex.Message); + LogErrorMessage("WeatherCloud: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "WeatherCloud: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2627,7 +2693,7 @@ internal async void UpdateOpenWeatherMap(DateTime timestamp) LogDebugMessage($"OpenWeatherMap: Response code = {status} - {response.StatusCode}"); if (response.StatusCode != HttpStatusCode.NoContent) { - LogMessage($"OpenWeatherMap: ERROR - Response code = {response.StatusCode}, Response data = {responseBodyAsText}"); + LogWarningMessage($"OpenWeatherMap: ERROR - Response code = {response.StatusCode}, Response data = {responseBodyAsText}"); ThirdPartyAlarm.LastMessage = $"OpenWeatherMap: HTTP response - {response.StatusCode}, Response data = {responseBodyAsText}"; ThirdPartyAlarm.Triggered = true; } @@ -2639,7 +2705,7 @@ internal async void UpdateOpenWeatherMap(DateTime timestamp) } catch (Exception ex) { - LogMessage("OpenWeatherMap: ERROR - " + ex.Message); + LogErrorMessage("OpenWeatherMap: ERROR - " + ex.Message); ThirdPartyAlarm.LastMessage = "OpenWeatherMap: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -2670,7 +2736,7 @@ internal OpenWeatherMapStation[] GetOpenWeatherMapStations() } catch (Exception ex) { - LogMessage("OpenWeatherMap: Get Stations ERROR - " + ex.Message); + LogErrorMessage("OpenWeatherMap: Get Stations ERROR - " + ex.Message); } return retVal; @@ -2714,13 +2780,13 @@ internal void CreateOpenWeatherMapStation() } else { - LogMessage($"OpenWeatherMap: Failed to create new station. Error - {response.StatusCode}, text - {responseBodyAsText}"); + LogWarningMessage($"OpenWeatherMap: Failed to create new station. Error - {response.StatusCode}, text - {responseBodyAsText}"); } } } catch (Exception ex) { - LogMessage("OpenWeatherMap: Create station ERROR - " + ex.Message); + LogErrorMessage("OpenWeatherMap: Create station ERROR - " + ex.Message); } } @@ -2751,7 +2817,7 @@ internal void EnableOpenWeatherMap() // multiple stations defined, the user must select which one to use var msg = $"Multiple OpenWeatherMap stations found, please select the correct station id and enter it into your configuration"; LogConsoleMessage(msg); - LogMessage("OpenWeatherMap: " + msg); + LogWarningMessage("OpenWeatherMap: " + msg); foreach (var station in stations) { msg = $" Station Id = {station.id}, Name = {station.name}"; @@ -2776,7 +2842,7 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs if ((!station.PressReadyToPlot || !station.TempReadyToPlot || !station.WindReadyToPlot) && !StationOptions.NoSensorCheck) { // not all the data is ready and NoSensorCheck is not enabled - LogMessage($"Realtime[{cycle}]: Not all data is ready, aborting process"); + LogWarningMessage($"Realtime[{cycle}]: Not all data is ready, aborting process"); return; } @@ -2786,7 +2852,7 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs // Process any files if (RealtimeCopyInProgress || RealtimeFtpInProgress) { - LogMessage($"Realtime[{cycle}]: Warning, a previous cycle is still processing local files. Skipping this interval."); + LogWarningMessage($"Realtime[{cycle}]: Warning, a previous cycle is still processing local files. Skipping this interval."); } else { @@ -2809,13 +2875,13 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs // real time interval is in ms, if a session has been uploading for 3 minutes - abort it and reconnect if (realtimeFTPRetries * RealtimeInterval / 1000 > 3 * 60) { - LogMessage($"Realtime[{cycle}]: Realtime has been in progress for more than 3 minutes, attempting to reconnect."); + LogWarningMessage($"Realtime[{cycle}]: Realtime has been in progress for more than 3 minutes, attempting to reconnect."); //RealtimeFTPConnectionTest(cycle); RealtimeFTPReconnect(); } else { - LogMessage($"Realtime[{cycle}]: No FTP attempted this cycle"); + LogWarningMessage($"Realtime[{cycle}]: No FTP attempted this cycle"); } } else @@ -2835,7 +2901,7 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs if (!reconnecting || FtpOptions.FtpMode == FtpProtocols.PHP) { // Finally we can do some FTP! - RealtimeFtpInProgress = true; + //RealtimeFtpInProgress = true; try { @@ -2846,7 +2912,7 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs { LogExceptionMessage(ex, $"Realtime[{cycle}]: Error during realtime upload."); } - RealtimeFtpInProgress = false; + //RealtimeFtpInProgress = false; } } } @@ -2866,7 +2932,7 @@ internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs LogDebugMessage($"Realtime[{cycle}]: Execute realtime program - {RealtimeProgram}, with parameters - {args}"); Utils.RunExternalTask(RealtimeProgram, args, false); } - catch(Exception ex) + catch (Exception ex) { LogDebugMessage($"Realtime[{cycle}]: Error in realtime program - {RealtimeProgram}. Error: {ex.Message}"); } @@ -3002,7 +3068,7 @@ await Task.Run(() => } else { - LogMessage("RealtimeReconnect: Realtime ftp connection failed to connect after reinitialisation"); + LogWarningMessage("RealtimeReconnect: Realtime ftp connection failed to connect after reinitialisation"); } } @@ -3034,7 +3100,7 @@ await Task.Run(() => if (pwd.Length == 0) { connected = false; - LogMessage("RealtimeReconnect: Realtime ftp connection test failed to get Present Working Directory"); + LogWarningMessage("RealtimeReconnect: Realtime ftp connection test failed to get Present Working Directory"); } else { @@ -3117,7 +3183,7 @@ private void RealtimeLocalCopy(byte cycle) } catch (Exception ex) { - LogMessage($"RealtimeLocalCopy[{cycle}]: Error copying [{srcFile}] to [{dstFile}. Error = {ex.Message}"); + LogErrorMessage($"RealtimeLocalCopy[{cycle}]: Error copying [{srcFile}] to [{dstFile}. Error = {ex.Message}"); } } } @@ -3132,6 +3198,7 @@ private async void RealtimeUpload(byte cycle) var taskCount = 0; var runningTaskCount = 0; + RealtimeFtpInProgress = true; if (FtpOptions.Directory.Length > 0) { @@ -3254,7 +3321,7 @@ private async void RealtimeUpload(byte cycle) if (!File.Exists(uploadfile)) { - LogMessage($"Realtime[{cycle}]: Warning, extra web file not found! - {uploadfile}"); + LogWarningMessage($"Realtime[{cycle}]: Warning, extra web file not found! - {uploadfile}"); return; } @@ -3338,6 +3405,7 @@ private async void RealtimeUpload(byte cycle) } LogDebugMessage($"Realtime[{cycle}]: Real time files complete, {tasklist.Count()} files uploaded"); tasklist.Clear(); + RealtimeFtpInProgress = false; } else { @@ -3398,10 +3466,12 @@ private async void RealtimeUpload(byte cycle) } else { - LogMessage($"Realtime[{cycle}]: Warning, extra web file[{i}] not found! - {uploadfile}"); + LogWarningMessage($"Realtime[{cycle}]: Warning, extra web file[{i}] not found! - {uploadfile}"); } } } + // all done for none-PHP + RealtimeFtpInProgress = false; } } @@ -3463,7 +3533,7 @@ private void CreateRealtimeHTMLfiles(int cycle) } else { - LogMessage($"Realtime[{cycle}]: Extra realtime web file[{i}] not found - {uploadfile}"); + LogWarningMessage($"Realtime[{cycle}]: Extra realtime web file[{i}] not found - {uploadfile}"); } } @@ -3573,7 +3643,7 @@ private static bool IsRunningOnMac() internal void DoMoonPhase() { DateTime now = DateTime.Now; - double[] moonriseset = MoonriseMoonset.MoonRise(now.Year, now.Month, now.Day, TimeZone.CurrentTimeZone.GetUtcOffset(now).TotalHours, (double)Latitude, (double)Longitude); + double[] moonriseset = MoonriseMoonset.MoonRise(now.Year, now.Month, now.Day, TimeZone.CurrentTimeZone.GetUtcOffset(now).TotalHours, (double) Latitude, (double) Longitude); MoonRiseTime = TimeSpan.FromHours(moonriseset[0]); MoonSetTime = TimeSpan.FromHours(moonriseset[1]); @@ -3637,7 +3707,7 @@ internal void DoMoonImage() if (MoonImage.Enabled) { LogDebugMessage("Generating new Moon image"); - var ret = MoonriseMoonset.CreateMoonImage(MoonPhaseAngle, (double)Latitude, MoonImage.Size); + var ret = MoonriseMoonset.CreateMoonImage(MoonPhaseAngle, (double) Latitude, MoonImage.Size); if (ret == "OK") { @@ -4096,7 +4166,7 @@ public string RemoveOldDiagsFiles(string logType) fileEntries.RemoveAt(0); } - filename= $"{directory}FTP-{DateTime.Now:yyyyMMdd-HHmmss}.txt"; + filename = $"{directory}FTP-{DateTime.Now:yyyyMMdd-HHmmss}.txt"; } return filename; @@ -4190,7 +4260,7 @@ private void ReadIniFile() if (ProgramOptions.Culture.RemoveSpaceFromDateSeparator && CultureInfo.CurrentCulture.DateTimeFormat.DateSeparator.Contains(" ")) { // get the existing culture - var newCulture = (CultureInfo)CultureInfo.CurrentCulture.Clone(); + var newCulture = (CultureInfo) CultureInfo.CurrentCulture.Clone(); // change the date separator newCulture.DateTimeFormat.DateSeparator = CultureInfo.CurrentCulture.DateTimeFormat.DateSeparator.Replace(" ", ""); // set current thread culture @@ -4221,6 +4291,11 @@ private void ReadIniFile() ProgramOptions.DebugLogging = ini.GetValue("Station", "Logging", false); ProgramOptions.DataLogging = ini.GetValue("Station", "DataLogging", false); } + ErrorListLoggingLevel = (LogLevel) ini.GetValue("Program", "ErrorListLoggingLevel", (int)LogLevel.Warning); + + ProgramOptions.SecureSettings = ini.GetValue("Program", "SecureSettings", false); + ProgramOptions.SettingsUsername = ini.GetValue("Program", "SettingsUsername", ""); + ProgramOptions.SettingsPassword = ini.GetValue("Program", "SettingsPassword", ""); ComportName = ini.GetValue("Station", "ComportName", DefaultComportName); @@ -4262,7 +4337,7 @@ private void ReadIniFile() DavisOptions.RainGaugeType = -1; rewriteRequired = true; } - DavisOptions.ConnectionType = ini.GetValue("Station", "VP2ConnectionType", VP2SERIALCONNECTION); + DavisOptions.ConnectionType = ini.GetValue("Station", "VP2ConnectionType", 0); DavisOptions.TCPPort = ini.GetValue("Station", "VP2TCPPort", 22222); DavisOptions.IPAddr = ini.GetValue("Station", "VP2IPAddr", "0.0.0.0"); @@ -4279,14 +4354,14 @@ private void ReadIniFile() if (Latitude > 90 || Latitude < -90) { Latitude = 0; - LogMessage($"Error, invalid latitude value in Cumulus.ini [{Latitude}], defaulting to zero."); + LogErrorMessage($"Error, invalid latitude value in Cumulus.ini [{Latitude}], defaulting to zero."); rewriteRequired = true; } Longitude = ini.GetValue("Station", "Longitude", (decimal) 0.0); if (Longitude > 180 || Longitude < -180) { Longitude = 0; - LogMessage($"Error, invalid longitude value in Cumulus.ini [{Longitude}], defaulting to zero."); + LogErrorMessage($"Error, invalid longitude value in Cumulus.ini [{Longitude}], defaulting to zero."); rewriteRequired = true; } @@ -4475,7 +4550,7 @@ private void ReadIniFile() RecordsBeganDateTime = DateTime.ParseExact(RecordsBeganDate, "yyyy-MM-dd", CultureInfo.InvariantCulture); } - LogMessage($"Cumulus start date Parsed: {RecordsBeganDateTime:yyyy-MM-dd}" ); + LogMessage($"Cumulus start date Parsed: {RecordsBeganDateTime:yyyy-MM-dd}"); ImetOptions.WaitTime = ini.GetValue("Station", "ImetWaitTime", 500); ImetOptions.ReadDelay = ini.GetValue("Station", "ImetReadDelay", 500); @@ -4605,6 +4680,7 @@ private void ReadIniFile() Gw1000PrimaryTHSensor = ini.GetValue("GW1000", "PrimaryTHSensor", 0); // 0=default, 1-8=extra t/h sensor number 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); EcowittExtraUseSolar = ini.GetValue("GW1000", "ExtraSensorUseSolar", true); EcowittExtraUseUv = ini.GetValue("GW1000", "ExtraSensorUseUv", true); EcowittExtraUseTempHum = ini.GetValue("GW1000", "ExtraSensorUseTempHum", true); @@ -4644,6 +4720,12 @@ private void ReadIniFile() { EcowittForwarders[i] = ini.GetValue("GW1000", "Forwarder" + i, ""); } + EcowittExtraUseMainForwarders = ini.GetValue("GW1000", "ExtraUseMainForwarders", false); + // extra forwarders + for (int i = 0; i < EcowittExtraForwarders.Length; i++) + { + EcowittExtraForwarders[i] = ini.GetValue("GW1000", "ExtraForwarder" + i, ""); + } // Ambient settings AmbientExtraEnabled = ini.GetValue("Ambient", "ExtraSensorDataEnabled", false); @@ -4710,20 +4792,20 @@ 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); + 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)) { FtpOptions.SshAuthen = "password"; - LogMessage($"Error, invalid SshFtpAuthentication value in Cumulus.ini [{FtpOptions.SshAuthen}], defaulting to Password."); + LogWarningMessage($"Error, invalid SshFtpAuthentication value in Cumulus.ini [{FtpOptions.SshAuthen}], defaulting to Password."); rewriteRequired = true; } FtpOptions.SshPskFile = ini.GetValue("FTP site", "SshFtpPskFile", ""); if (FtpOptions.SshPskFile.Length > 0 && (FtpOptions.SshAuthen == "psk" || FtpOptions.SshAuthen == "password_psk") && !File.Exists(FtpOptions.SshPskFile)) { FtpOptions.SshPskFile = ""; - LogMessage($"Error, file name specified by SshFtpPskFile value in Cumulus.ini does not exist [{FtpOptions.SshPskFile}]."); + LogErrorMessage($"Error, file name specified by SshFtpPskFile value in Cumulus.ini does not exist [{FtpOptions.SshPskFile}]."); rewriteRequired = true; } FtpOptions.DisableEPSV = ini.GetValue("FTP site", "DisableEPSV", false); @@ -4862,7 +4944,7 @@ private void ReadIniFile() GraphDays = ini.GetValue("Graphs", "ChartMaxDays", 31); GraphHours = ini.GetValue("Graphs", "GraphHours", 72); - RecentDataDays = (int)Math.Ceiling(Math.Max(7, GraphHours / 24.0)); + RecentDataDays = (int) Math.Ceiling(Math.Max(7, GraphHours / 24.0)); MoonImage.Enabled = ini.GetValue("Graphs", "MoonImageEnabled", false); MoonImage.Size = ini.GetValue("Graphs", "MoonImageSize", 100); if (MoonImage.Size < 10) @@ -4904,18 +4986,18 @@ private void ReadIniFile() GraphOptions.Visible.AqSensor.PmAvg.Vals = ini.GetValue("Graphs", "Aq-PmAvgVisible", new int[4]); GraphOptions.Visible.AqSensor.Temp.Vals = ini.GetValue("Graphs", "Aq-TempVisible", new int[4]); GraphOptions.Visible.AqSensor.Hum.Vals = ini.GetValue("Graphs", "Aq-HumVisible", new int[4]); - GraphOptions.Visible.CO2Sensor.CO2.Val = ini.GetValue("Graphs", "CO2-CO2", 1); - GraphOptions.Visible.CO2Sensor.CO2Avg.Val = ini.GetValue("Graphs", "CO2-CO2Avg", 1); - GraphOptions.Visible.CO2Sensor.Pm25.Val = ini.GetValue("Graphs", "CO2-Pm25", 1); - GraphOptions.Visible.CO2Sensor.Pm25Avg.Val = ini.GetValue("Graphs", "CO2-Pm25Avg", 1); - GraphOptions.Visible.CO2Sensor.Pm10.Val = ini.GetValue("Graphs", "CO2-Pm10", 1); - GraphOptions.Visible.CO2Sensor.Pm10Avg.Val = ini.GetValue("Graphs", "CO2-Pm10Avg", 1); + GraphOptions.Visible.CO2Sensor.CO2.Val = ini.GetValue("Graphs", "CO2-CO2", 0); + GraphOptions.Visible.CO2Sensor.CO2Avg.Val = ini.GetValue("Graphs", "CO2-CO2Avg", 0); + GraphOptions.Visible.CO2Sensor.Pm25.Val = ini.GetValue("Graphs", "CO2-Pm25", 0); + GraphOptions.Visible.CO2Sensor.Pm25Avg.Val = ini.GetValue("Graphs", "CO2-Pm25Avg", 0); + GraphOptions.Visible.CO2Sensor.Pm10.Val = ini.GetValue("Graphs", "CO2-Pm10", 0); + GraphOptions.Visible.CO2Sensor.Pm10Avg.Val = ini.GetValue("Graphs", "CO2-Pm10Avg", 0); GraphOptions.Visible.CO2Sensor.Temp.Val = ini.GetValue("Graphs", "CO2-Temp", 0); GraphOptions.Visible.CO2Sensor.Hum.Val = ini.GetValue("Graphs", "CO2-Hum", 0); GraphOptions.Colour.Temp = ini.GetValue("GraphColours", "TempColour", "#ff0000"); GraphOptions.Colour.InTemp = ini.GetValue("GraphColours", "InTempColour", "#50b432"); - GraphOptions.Colour.HeatIndex= ini.GetValue("GraphColours", "HIColour", "#9161c9"); + GraphOptions.Colour.HeatIndex = ini.GetValue("GraphColours", "HIColour", "#9161c9"); GraphOptions.Colour.DewPoint = ini.GetValue("GraphColours", "DPColour", "#ffff00"); GraphOptions.Colour.WindChill = ini.GetValue("GraphColours", "WCColour", "#ffa500"); GraphOptions.Colour.AppTemp = ini.GetValue("GraphColours", "AppTempColour", "#00fffe"); @@ -5140,7 +5222,7 @@ private void ReadIniFile() LowTempAlarm.Notify = ini.GetValue("Alarms", "LowTempAlarmNotify", false); LowTempAlarm.Email = ini.GetValue("Alarms", "LowTempAlarmEmail", false); LowTempAlarm.Latch = ini.GetValue("Alarms", "LowTempAlarmLatch", false); - LowTempAlarm.LatchHours = ini.GetValue("Alarms", "LowTempAlarmLatchHours", 24); + LowTempAlarm.LatchHours = ini.GetValue("Alarms", "LowTempAlarmLatchHours", 24.0); LowTempAlarm.Action = ini.GetValue("Alarms", "LowTempAlarmAction", ""); LowTempAlarm.ActionParams = ini.GetValue("Alarms", "LowTempAlarmActionParams", ""); @@ -5156,7 +5238,7 @@ private void ReadIniFile() HighTempAlarm.Notify = ini.GetValue("Alarms", "HighTempAlarmNotify", false); HighTempAlarm.Email = ini.GetValue("Alarms", "HighTempAlarmEmail", false); HighTempAlarm.Latch = ini.GetValue("Alarms", "HighTempAlarmLatch", false); - HighTempAlarm.LatchHours = ini.GetValue("Alarms", "HighTempAlarmLatchHours", 24); + HighTempAlarm.LatchHours = ini.GetValue("Alarms", "HighTempAlarmLatchHours", 24.0); HighTempAlarm.Action = ini.GetValue("Alarms", "HighTempAlarmAction", ""); HighTempAlarm.ActionParams = ini.GetValue("Alarms", "HighTempAlarmActionParams", ""); @@ -5172,7 +5254,7 @@ private void ReadIniFile() TempChangeAlarm.Notify = ini.GetValue("Alarms", "TempChangeAlarmNotify", false); TempChangeAlarm.Email = ini.GetValue("Alarms", "TempChangeAlarmEmail", false); TempChangeAlarm.Latch = ini.GetValue("Alarms", "TempChangeAlarmLatch", false); - TempChangeAlarm.LatchHours = ini.GetValue("Alarms", "TempChangeAlarmLatchHours", 24); + TempChangeAlarm.LatchHours = ini.GetValue("Alarms", "TempChangeAlarmLatchHours", 24.0); TempChangeAlarm.Action = ini.GetValue("Alarms", "TempChangeAlarmAction", ""); TempChangeAlarm.ActionParams = ini.GetValue("Alarms", "TempChangeAlarmActionParams", ""); @@ -5188,7 +5270,7 @@ private void ReadIniFile() LowPressAlarm.Notify = ini.GetValue("Alarms", "LowPressAlarmNotify", false); LowPressAlarm.Email = ini.GetValue("Alarms", "LowPressAlarmEmail", false); LowPressAlarm.Latch = ini.GetValue("Alarms", "LowPressAlarmLatch", false); - LowPressAlarm.LatchHours = ini.GetValue("Alarms", "LowPressAlarmLatchHours", 24); + LowPressAlarm.LatchHours = ini.GetValue("Alarms", "LowPressAlarmLatchHours", 24.0); LowPressAlarm.Action = ini.GetValue("Alarms", "LowPressAlarmAction", ""); LowPressAlarm.ActionParams = ini.GetValue("Alarms", "LowPressAlarmActionParams", ""); @@ -5204,7 +5286,7 @@ private void ReadIniFile() HighPressAlarm.Notify = ini.GetValue("Alarms", "HighPressAlarmNotify", false); HighPressAlarm.Email = ini.GetValue("Alarms", "HighPressAlarmEmail", false); HighPressAlarm.Latch = ini.GetValue("Alarms", "HighPressAlarmLatch", false); - HighPressAlarm.LatchHours = ini.GetValue("Alarms", "HighPressAlarmLatchHours", 24); + HighPressAlarm.LatchHours = ini.GetValue("Alarms", "HighPressAlarmLatchHours", 24.0); HighPressAlarm.Action = ini.GetValue("Alarms", "HighPressAlarmAction", ""); HighPressAlarm.ActionParams = ini.GetValue("Alarms", "HighPressAlarmActionParams", ""); @@ -5220,7 +5302,7 @@ private void ReadIniFile() PressChangeAlarm.Notify = ini.GetValue("Alarms", "PressChangeAlarmNotify", false); PressChangeAlarm.Email = ini.GetValue("Alarms", "PressChangeAlarmEmail", false); PressChangeAlarm.Latch = ini.GetValue("Alarms", "PressChangeAlarmLatch", false); - PressChangeAlarm.LatchHours = ini.GetValue("Alarms", "PressChangeAlarmLatchHours", 24); + PressChangeAlarm.LatchHours = ini.GetValue("Alarms", "PressChangeAlarmLatchHours", 24.0); PressChangeAlarm.Action = ini.GetValue("Alarms", "PressChangeAlarmAction", ""); PressChangeAlarm.ActionParams = ini.GetValue("Alarms", "PressChangeAlarmActionParams", ""); @@ -5236,7 +5318,7 @@ private void ReadIniFile() HighRainTodayAlarm.Notify = ini.GetValue("Alarms", "HighRainTodayAlarmNotify", false); HighRainTodayAlarm.Email = ini.GetValue("Alarms", "HighRainTodayAlarmEmail", false); HighRainTodayAlarm.Latch = ini.GetValue("Alarms", "HighRainTodayAlarmLatch", false); - HighRainTodayAlarm.LatchHours = ini.GetValue("Alarms", "HighRainTodayAlarmLatchHours", 24); + HighRainTodayAlarm.LatchHours = ini.GetValue("Alarms", "HighRainTodayAlarmLatchHours", 24.0); HighRainTodayAlarm.Action = ini.GetValue("Alarms", "HighRainTodayAlarmAction", ""); HighRainTodayAlarm.ActionParams = ini.GetValue("Alarms", "HighRainTodayAlarmActionParams", ""); @@ -5252,7 +5334,7 @@ private void ReadIniFile() HighRainRateAlarm.Notify = ini.GetValue("Alarms", "HighRainRateAlarmNotify", false); HighRainRateAlarm.Email = ini.GetValue("Alarms", "HighRainRateAlarmEmail", false); HighRainRateAlarm.Latch = ini.GetValue("Alarms", "HighRainRateAlarmLatch", false); - HighRainRateAlarm.LatchHours = ini.GetValue("Alarms", "HighRainRateAlarmLatchHours", 24); + HighRainRateAlarm.LatchHours = ini.GetValue("Alarms", "HighRainRateAlarmLatchHours", 24.0); HighRainRateAlarm.Action = ini.GetValue("Alarms", "HighRainRateAlarmAction", ""); HighRainRateAlarm.ActionParams = ini.GetValue("Alarms", "HighRainRateAlarmActionParams", ""); @@ -5262,7 +5344,7 @@ private void ReadIniFile() IsRainingAlarm.Notify = ini.GetValue("Alarms", "IsRainingAlarmNotify", false); IsRainingAlarm.Email = ini.GetValue("Alarms", "IsRainingAlarmEmail", false); IsRainingAlarm.Latch = ini.GetValue("Alarms", "IsRainingAlarmLatch", false); - IsRainingAlarm.LatchHours = ini.GetValue("Alarms", "IsRainingAlarmLatchHours", 1); + IsRainingAlarm.LatchHours = ini.GetValue("Alarms", "IsRainingAlarmLatchHours", 1.0); IsRainingAlarm.Action = ini.GetValue("Alarms", "IsRainingAlarmAction", ""); IsRainingAlarm.ActionParams = ini.GetValue("Alarms", "IsRainingAlarmActionParams", ""); @@ -5278,7 +5360,7 @@ private void ReadIniFile() HighGustAlarm.Notify = ini.GetValue("Alarms", "HighGustAlarmNotify", false); HighGustAlarm.Email = ini.GetValue("Alarms", "HighGustAlarmEmail", false); HighGustAlarm.Latch = ini.GetValue("Alarms", "HighGustAlarmLatch", false); - HighGustAlarm.LatchHours = ini.GetValue("Alarms", "HighGustAlarmLatchHours", 24); + HighGustAlarm.LatchHours = ini.GetValue("Alarms", "HighGustAlarmLatchHours", 24.0); HighGustAlarm.Action = ini.GetValue("Alarms", "HighGustAlarmAction", ""); HighGustAlarm.ActionParams = ini.GetValue("Alarms", "HighGustAlarmActionParams", ""); @@ -5294,7 +5376,7 @@ private void ReadIniFile() HighWindAlarm.Notify = ini.GetValue("Alarms", "HighWindAlarmNotify", false); HighWindAlarm.Email = ini.GetValue("Alarms", "HighWindAlarmEmail", false); HighWindAlarm.Latch = ini.GetValue("Alarms", "HighWindAlarmLatch", false); - HighWindAlarm.LatchHours = ini.GetValue("Alarms", "HighWindAlarmLatchHours", 24); + HighWindAlarm.LatchHours = ini.GetValue("Alarms", "HighWindAlarmLatchHours", 24.0); HighWindAlarm.Action = ini.GetValue("Alarms", "HighWindAlarmAction", ""); HighWindAlarm.ActionParams = ini.GetValue("Alarms", "HighWindAlarmActionParams", ""); @@ -5309,7 +5391,7 @@ private void ReadIniFile() SensorAlarm.Notify = ini.GetValue("Alarms", "SensorAlarmNotify", true); SensorAlarm.Email = ini.GetValue("Alarms", "SensorAlarmEmail", false); SensorAlarm.Latch = ini.GetValue("Alarms", "SensorAlarmLatch", true); - SensorAlarm.LatchHours = ini.GetValue("Alarms", "SensorAlarmLatchHours", 1); + SensorAlarm.LatchHours = ini.GetValue("Alarms", "SensorAlarmLatchHours", 1.0); SensorAlarm.TriggerThreshold = ini.GetValue("Alarms", "SensorAlarmTriggerCount", 2); SensorAlarm.Action = ini.GetValue("Alarms", "SensorAlarmAction", ""); SensorAlarm.ActionParams = ini.GetValue("Alarms", "SensorAlarmActionParams", ""); @@ -5325,7 +5407,7 @@ private void ReadIniFile() DataStoppedAlarm.Notify = ini.GetValue("Alarms", "DataStoppedAlarmNotify", true); DataStoppedAlarm.Email = ini.GetValue("Alarms", "DataStoppedAlarmEmail", false); DataStoppedAlarm.Latch = ini.GetValue("Alarms", "DataStoppedAlarmLatch", true); - DataStoppedAlarm.LatchHours = ini.GetValue("Alarms", "DataStoppedAlarmLatchHours", 1); + DataStoppedAlarm.LatchHours = ini.GetValue("Alarms", "DataStoppedAlarmLatchHours", 1.0); DataStoppedAlarm.TriggerThreshold = ini.GetValue("Alarms", "DataStoppedAlarmTriggerCount", 2); DataStoppedAlarm.Action = ini.GetValue("Alarms", "DataStoppedAlarmAction", ""); DataStoppedAlarm.ActionParams = ini.GetValue("Alarms", "DataStoppedAlarmActionParams", ""); @@ -5337,7 +5419,7 @@ private void ReadIniFile() BatteryLowAlarm.Notify = ini.GetValue("Alarms", "BatteryLowAlarmNotify", false); BatteryLowAlarm.Email = ini.GetValue("Alarms", "BatteryLowAlarmEmail", false); BatteryLowAlarm.Latch = ini.GetValue("Alarms", "BatteryLowAlarmLatch", false); - BatteryLowAlarm.LatchHours = ini.GetValue("Alarms", "BatteryLowAlarmLatchHours", 24); + BatteryLowAlarm.LatchHours = ini.GetValue("Alarms", "BatteryLowAlarmLatchHours", 24.0); BatteryLowAlarm.TriggerThreshold = ini.GetValue("Alarms", "BatteryLowAlarmTriggerCount", 1); BatteryLowAlarm.Action = ini.GetValue("Alarms", "BatteryLowAlarmAction", ""); BatteryLowAlarm.ActionParams = ini.GetValue("Alarms", "BatteryLowAlarmActionParams", ""); @@ -5348,7 +5430,7 @@ private void ReadIniFile() SpikeAlarm.Notify = ini.GetValue("Alarms", "DataSpikeAlarmNotify", true); SpikeAlarm.Email = ini.GetValue("Alarms", "DataSpikeAlarmEmail", true); SpikeAlarm.Latch = ini.GetValue("Alarms", "DataSpikeAlarmLatch", true); - SpikeAlarm.LatchHours = ini.GetValue("Alarms", "DataSpikeAlarmLatchHours", 24); + SpikeAlarm.LatchHours = ini.GetValue("Alarms", "DataSpikeAlarmLatchHours", 24.0); SpikeAlarm.TriggerThreshold = ini.GetValue("Alarms", "DataSpikeAlarmTriggerCount", 1); SpikeAlarm.Action = ini.GetValue("Alarms", "DataSpikeAlarmAction", ""); SpikeAlarm.ActionParams = ini.GetValue("Alarms", "DataSpikeAlarmActionParams", ""); @@ -5359,7 +5441,7 @@ private void ReadIniFile() UpgradeAlarm.Notify = ini.GetValue("Alarms", "UpgradeAlarmNotify", true); UpgradeAlarm.Email = ini.GetValue("Alarms", "UpgradeAlarmEmail", false); UpgradeAlarm.Latch = ini.GetValue("Alarms", "UpgradeAlarmLatch", false); - UpgradeAlarm.LatchHours = ini.GetValue("Alarms", "UpgradeAlarmLatchHours", 24); + UpgradeAlarm.LatchHours = ini.GetValue("Alarms", "UpgradeAlarmLatchHours", 24.0); UpgradeAlarm.Action = ini.GetValue("Alarms", "UpgradeAlarmAction", ""); UpgradeAlarm.ActionParams = ini.GetValue("Alarms", "UpgradeAlarmActionParams", ""); @@ -5369,7 +5451,7 @@ private void ReadIniFile() ThirdPartyAlarm.Notify = ini.GetValue("Alarms", "HttpUploadAlarmNotify", false); ThirdPartyAlarm.Email = ini.GetValue("Alarms", "HttpUploadAlarmEmail", false); ThirdPartyAlarm.Latch = ini.GetValue("Alarms", "HttpUploadAlarmLatch", false); - ThirdPartyAlarm.LatchHours = ini.GetValue("Alarms", "HttpUploadAlarmLatchHours", 24); + ThirdPartyAlarm.LatchHours = ini.GetValue("Alarms", "HttpUploadAlarmLatchHours", 24.0); ThirdPartyAlarm.TriggerThreshold = ini.GetValue("Alarms", "HttpUploadAlarmTriggerCount", 1); ThirdPartyAlarm.Action = ini.GetValue("Alarms", "HttpUploadAlarmAction", ""); ThirdPartyAlarm.ActionParams = ini.GetValue("Alarms", "HttpUploadAlarmActionParams", ""); @@ -5380,7 +5462,7 @@ private void ReadIniFile() MySqlUploadAlarm.Notify = ini.GetValue("Alarms", "MySqlUploadAlarmNotify", false); MySqlUploadAlarm.Email = ini.GetValue("Alarms", "MySqlUploadAlarmEmail", false); MySqlUploadAlarm.Latch = ini.GetValue("Alarms", "MySqlUploadAlarmLatch", false); - MySqlUploadAlarm.LatchHours = ini.GetValue("Alarms", "MySqlUploadAlarmLatchHours", 24); + MySqlUploadAlarm.LatchHours = ini.GetValue("Alarms", "MySqlUploadAlarmLatchHours", 24.0); MySqlUploadAlarm.TriggerThreshold = ini.GetValue("Alarms", "MySqlUploadAlarmTriggerCount", 1); MySqlUploadAlarm.Action = ini.GetValue("Alarms", "MySqlUploadAlarmAction", ""); MySqlUploadAlarm.ActionParams = ini.GetValue("Alarms", "MySqlUploadAlarmActionParams", ""); @@ -5391,7 +5473,7 @@ private void ReadIniFile() NewRecordAlarm.Notify = ini.GetValue("Alarms", "NewRecordAlarmNotify", false); NewRecordAlarm.Email = ini.GetValue("Alarms", "NewRecordAlarmEmail", false); NewRecordAlarm.Latch = ini.GetValue("Alarms", "NewRecordAlarmLatch", false); - NewRecordAlarm.LatchHours = ini.GetValue("Alarms", "NewRecordAlarmLatchHours", 24); + NewRecordAlarm.LatchHours = ini.GetValue("Alarms", "NewRecordAlarmLatchHours", 24.0); NewRecordAlarm.Action = ini.GetValue("Alarms", "NewRecordAlarmAction", ""); NewRecordAlarm.ActionParams = ini.GetValue("Alarms", "NewRecordAlarmActionParams", ""); @@ -5401,7 +5483,7 @@ private void ReadIniFile() FtpAlarm.Notify = ini.GetValue("Alarms", "FtpAlarmNotify", false); FtpAlarm.Email = ini.GetValue("Alarms", "FtpAlarmEmail", false); FtpAlarm.Latch = ini.GetValue("Alarms", "FtpAlarmLatch", false); - FtpAlarm.LatchHours = ini.GetValue("Alarms", "FtpAlarmLatchHours", 24); + FtpAlarm.LatchHours = ini.GetValue("Alarms", "FtpAlarmLatchHours", 24.0); FtpAlarm.Action = ini.GetValue("Alarms", "FtpAlarmAction", ""); FtpAlarm.ActionParams = ini.GetValue("Alarms", "FtpAlarmActionParams", ""); @@ -5615,7 +5697,7 @@ private void ReadIniFile() // MySQL - common MySqlConnSettings.Server = ini.GetValue("MySQL", "Host", "127.0.0.1"); - MySqlConnSettings.Port = (uint)ini.GetValue("MySQL", "Port", 3306); + MySqlConnSettings.Port = (uint) ini.GetValue("MySQL", "Port", 3306); MySqlConnSettings.UserID = ini.GetValue("MySQL", "User", ""); MySqlConnSettings.Password = ini.GetValue("MySQL", "Pass", ""); MySqlConnSettings.Database = ini.GetValue("MySQL", "Database", "database"); @@ -5766,6 +5848,13 @@ private void ReadIniFile() SelectaChartOptions.colours[i] = ini.GetValue("Select-a-Chart", "Colour" + i, ""); } + // Select-a-Period settings + for (int i = 0; i < SelectaPeriodOptions.series.Length; i++) + { + SelectaPeriodOptions.series[i] = ini.GetValue("Select-a-Period", "Series" + i, "0"); + SelectaPeriodOptions.colours[i] = ini.GetValue("Select-a-Period", "Colour" + i, ""); + } + // Email settings SmtpOptions.Enabled = ini.GetValue("SMTP", "Enabled", false); SmtpOptions.Server = ini.GetValue("SMTP", "ServerName", ""); @@ -5884,6 +5973,11 @@ internal void WriteIniFile() ini.SetValue("Station", "WarnMultiple", ProgramOptions.WarnMultiple); ini.SetValue("Station", "ListWebTags", ProgramOptions.ListWebTags); + ini.SetValue("Program", "ErrorListLoggingLevel", (int) ErrorListLoggingLevel); + + ini.SetValue("Program", "SecureSettings", ProgramOptions.SecureSettings); + ini.SetValue("Program", "SettingsUsername", ProgramOptions.SettingsUsername); + ini.SetValue("Program", "SettingsPassword", ProgramOptions.SettingsPassword); ini.SetValue("Station", "Type", StationType); @@ -6080,6 +6174,7 @@ internal void WriteIniFile() ini.SetValue("GW1000", "PrimaryTHSensor", Gw1000PrimaryTHSensor); ini.SetValue("GW1000", "PrimaryRainSensor", Gw1000PrimaryRainSensor); ini.SetValue("GW1000", "ExtraSensorDataEnabled", EcowittExtraEnabled); + ini.SetValue("GW1000", "ExtraCloudSensorDataEnabled", EcowittCloudExtraEnabled); ini.SetValue("GW1000", "ExtraSensorUseSolar", EcowittExtraUseSolar); ini.SetValue("GW1000", "ExtraSensorUseUv", EcowittExtraUseUv); ini.SetValue("GW1000", "ExtraSensorUseTempHum", EcowittExtraUseTempHum); @@ -6116,6 +6211,15 @@ internal void WriteIniFile() else ini.SetValue("GW1000", "Forwarder" + i, EcowittForwarders[i].ToString()); } + // extra forwarders + ini.SetValue("GW1000", "ExtraUseMainForwarders", EcowittExtraUseMainForwarders); + for (int i = 0; i < EcowittExtraForwarders.Length; i++) + { + if (string.IsNullOrEmpty(EcowittExtraForwarders[i])) + ini.DeleteValue("GW1000", "ExtraForwarder" + i); + else + ini.SetValue("GW1000", "ExtraForwarder" + i, EcowittExtraForwarders[i].ToString()); + } // Ambient settings ini.SetValue("Ambient", "ExtraSensorDataEnabled", AmbientExtraEnabled); @@ -6158,7 +6262,7 @@ internal void WriteIniFile() ini.SetValue("FTP site", "Directory", FtpOptions.Directory); //ini.SetValue("FTP site", "AutoUpdate", WebAutoUpdate); // Deprecated - now read-only - ini.SetValue("FTP site", "Sslftp", (int)FtpOptions.FtpMode); + ini.SetValue("FTP site", "Sslftp", (int) FtpOptions.FtpMode); // BUILD 3092 - added alternate SFTP authentication options ini.SetValue("FTP site", "SshFtpAuthentication", FtpOptions.SshAuthen); ini.SetValue("FTP site", "SshFtpPskFile", FtpOptions.SshPskFile); @@ -6944,12 +7048,20 @@ internal void WriteIniFile() } } + // Select-a-Chart for (int i = 0; i < SelectaChartOptions.series.Length; i++) { ini.SetValue("Select-a-Chart", "Series" + i, SelectaChartOptions.series[i]); ini.SetValue("Select-a-Chart", "Colour" + i, SelectaChartOptions.colours[i]); } + // Select-a-Period + for (int i = 0; i < SelectaPeriodOptions.series.Length; i++) + { + ini.SetValue("Select-a-Period", "Series" + i, SelectaPeriodOptions.series[i]); + ini.SetValue("Select-a-Period", "Colour" + i, SelectaPeriodOptions.colours[i]); + } + // Email settings ini.SetValue("SMTP", "Enabled", SmtpOptions.Enabled); ini.SetValue("SMTP", "ServerName", SmtpOptions.Server); @@ -7334,6 +7446,7 @@ 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); @@ -7406,6 +7519,7 @@ public void WriteStringsFile() public bool AirLinkOutEnabled { get; set; } public bool EcowittExtraEnabled { get; set; } + public bool EcowittCloudExtraEnabled { get; set; } public bool EcowittExtraUseSolar { get; set; } public bool EcowittExtraUseUv { get; set; } public bool EcowittExtraUseTempHum { get; set; } @@ -7429,6 +7543,8 @@ public void WriteStringsFile() public string EcowittExtraLocalAddr { get; set; } public int EcowittExtraCustomInterval { get; set; } public string[] EcowittForwarders = new string[10]; + public bool EcowittExtraUseMainForwarders { get; set; } + public string[] EcowittExtraForwarders = new string[10]; public int[] EcowittMapWN34 = new int[9]; @@ -7684,7 +7800,7 @@ public void WriteStringsFile() public FileGenerationOptions[] GraphDataEodFiles; -// private WebSocketServer wsServer; + // private WebSocketServer wsServer; /* public string Getversion() @@ -8027,7 +8143,7 @@ public async void DoExtraLogFile(DateTime timestamp) sb.Append("0" + ListSeparator); //40 - was leaf temp 1 sb.Append("0" + ListSeparator); //41 - was leaf temp 2 - sb.Append(station.LeafWetness1.ToString(LeafWetFormat) + ListSeparator); //42 + sb.Append(station.LeafWetness1.ToString(LeafWetFormat) + ListSeparator); //42 sb.Append(station.LeafWetness2.ToString(LeafWetFormat) + ListSeparator); //43 for (int i = 5; i <= 16; i++) @@ -8091,7 +8207,7 @@ public async void DoExtraLogFile(DateTime timestamp) } catch (Exception ex) { - LogMessage($"DoExtraLogFile: Error writing log entry {timestamp} - {ex.Message}"); + LogErrorMessage($"DoExtraLogFile: Error writing log entry {timestamp} - {ex.Message}"); retries--; await Task.Delay(250); } @@ -8201,16 +8317,16 @@ public async void DoAirLinkLogFile(DateTime timestamp) } else // Zero decimals - truncate value rather than round { - sb.Append((int)airLinkDataIn.aqiPm2p5 + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm2p5_1hr + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm2p5_3hr + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm2p5_24hr + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm2p5_nowcast + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm10 + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm10_1hr + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm10_3hr + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm10_24hr + ListSeparator); - sb.Append((int)airLinkDataIn.aqiPm10_nowcast + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm2p5 + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm2p5_1hr + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm2p5_3hr + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm2p5_24hr + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm2p5_nowcast + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm10 + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm10_1hr + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm10_3hr + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm10_24hr + ListSeparator); + sb.Append((int) airLinkDataIn.aqiPm10_nowcast + ListSeparator); } } else @@ -8280,7 +8396,7 @@ public async void DoAirLinkLogFile(DateTime timestamp) LogDebugMessage($"DoAirLinkLogFile: Log entry for {timestamp} written"); } } - catch(Exception ex) + catch (Exception ex) { LogDebugMessage($"DoAirLinkLogFile: Error writing log entry for {timestamp} - {ex.Message}"); retries--; @@ -8472,12 +8588,12 @@ private void CheckForSingleInstance(bool Windows) else { LogConsoleMessage("Cumulus is already running - but 'Stop second instance' is disabled", ConsoleColor.Yellow); - LogMessage("Stop second instance: Cumulus is already running but 'Stop second instance' is disabled - continuing"); + LogWarningMessage("Stop second instance: Cumulus is already running but 'Stop second instance' is disabled - continuing"); } } catch (Exception ex) { - LogMessage("Stop second instance: File Error! - " + ex); + LogWarningMessage("Stop second instance: File Error! - " + ex); LogMessage("Stop second instance: File HResult - " + ex.HResult); LogMessage("Stop second instance: File HResult - " + (ex.HResult & 0x0000FFFF)); @@ -8513,32 +8629,39 @@ public void BackupData(bool daily, DateTime timestamp) { string[] dirs = Directory.GetDirectories(dirpath); Array.Sort(dirs); - var dirlist = new List(dirs); + var dircnt = dirs.Length; - while (dirlist.Count > 10) + foreach (var dir in dirs) { + // leave the last 10 in place + if (dircnt <= 10) + break; + try { - if (Path.GetFileName(dirlist[0]) == "daily") + if (Path.GetFileName(dir) == "daily") { LogMessage("BackupData: *** Error - the backup folder has unexpected contents"); - break; + continue; } else { - Directory.Delete(dirlist[0], true); - dirlist.RemoveAt(0); + Directory.Delete(dir, true); + dircnt--; } } catch (UnauthorizedAccessException) { - LogErrorMessage("BackupData: Error, no permission to read/delete folder: " + dirlist[0]); + LogErrorMessage("BackupData: Error, no permission to read/delete folder: " + dir); + LogConsoleMessage("Error, no permission to read/delete folder: " + dir, ConsoleColor.Yellow); + break; } catch (Exception ex) { - LogErrorMessage($"BackupData: Error while attempting to read/delete folder: {dirlist[0]}, error message: {ex.Message}"); + LogErrorMessage($"BackupData: Error while attempting to read/delete folder: {dir}, error message: {ex.Message}"); + LogConsoleMessage($"Error while attempting to read/delete folder: {dir}, error message: {ex.Message}", ConsoleColor.Yellow); + break; } - } string foldername = timestamp.ToString("yyyyMMddHHmmss"); @@ -8639,7 +8762,7 @@ public void BackupData(bool daily, DateTime timestamp) } } - private void CopyBackupFile(string src, string dest, bool overwrite=false) + private void CopyBackupFile(string src, string dest, bool overwrite = false) { try { @@ -8650,7 +8773,7 @@ private void CopyBackupFile(string src, string dest, bool overwrite=false) } catch (Exception e) { - LogMessage($"BackupData: Error copying {src} - {e}"); + LogErrorMessage($"BackupData: Error copying {src} - {e}"); } } @@ -9020,11 +9143,20 @@ public string BeaufortDesc(double Bspeed) } } + + public void LogCriticalMessage(string message) + { + LogMessage(message, LogLevel.Critical); + } + public void LogErrorMessage(string message) { - LatestError = message; - LatestErrorTS = DateTime.Now; - LogMessage(message); + LogMessage(message, LogLevel.Error); + } + + public void LogWarningMessage(string message) + { + LogMessage(message, LogLevel.Warning); } public void LogSpikeRemoval(string message) @@ -9203,7 +9335,7 @@ public async void DoHTMLFiles() } else { - LogMessage($"Interval: Warning, extra web file[{i}] not found - {uploadfile}"); + LogWarningMessage($"Interval: Warning, extra web file[{i}] not found - {uploadfile}"); } } } @@ -9229,7 +9361,7 @@ public async void DoHTMLFiles() } catch (Exception ex) { - LogMessage("Interval: Error starting external program: " + ex.Message); + LogWarningMessage("Interval: Error starting external program: " + ex.Message); } } @@ -9265,7 +9397,7 @@ public void DoLocalCopy() } catch (Exception ex) { - LogMessage("LocalCopy: Error with paths - " + ex.Message); + LogErrorMessage("LocalCopy: Error with paths - " + ex.Message); } @@ -9299,12 +9431,12 @@ public void DoLocalCopy() } catch (Exception ex) { - LogMessage("LocalCopy: Error copy NOAA reports - " + ex.Message); + LogErrorMessage("LocalCopy: Error copy NOAA reports - " + ex.Message); } } catch (Exception e) { - LogMessage($"LocalCopy: Error copying file {srcfile} - {e.Message}"); + LogErrorMessage($"LocalCopy: Error copying file {srcfile} - {e.Message}"); } } @@ -9343,7 +9475,7 @@ public void DoLocalCopy() } catch (Exception e) { - LogMessage($"LocalCopy: Error copying standard data file [{StdWebFiles[i].LocalFileName}]"); + LogErrorMessage($"LocalCopy: Error copying standard data file [{StdWebFiles[i].LocalFileName}]"); LogMessage($"LocalCopy: Error = {e.Message}"); failed++; } @@ -9351,8 +9483,6 @@ public void DoLocalCopy() } LogDebugMessage($"LocalCopy: Done copying standard web files - Success: {success}, Failed: {failed}"); - var oldset = DateTime.Now.AddHours(-GraphHours); - LogDebugMessage("LocalCopy: Copying graph data files"); success = 0; failed = 0; @@ -9385,7 +9515,7 @@ public void DoLocalCopy() } catch (Exception e) { - LogMessage($"LocalCopy: Error copying graph data file [{srcfile}]"); + LogErrorMessage($"LocalCopy: Error copying graph data file [{srcfile}]"); LogMessage($"LocalCopy: Error = {e.Message}"); failed++; } @@ -9421,7 +9551,7 @@ public void DoLocalCopy() } catch (Exception e) { - LogMessage($"LocalCopy: Error copying daily graph data file [{srcfile}]"); + LogErrorMessage($"LocalCopy: Error copying daily graph data file [{srcfile}]"); LogMessage($"LocalCopy: Error = {e.Message}"); failed++; } @@ -9441,7 +9571,7 @@ public void DoLocalCopy() } catch (Exception e) { - LogMessage($"LocalCopy: Error copying moon image - {e.Message}"); + LogErrorMessage($"LocalCopy: Error copying moon image - {e.Message}"); } } @@ -10079,7 +10209,7 @@ public async Task DoIntervalUpload() } catch (Exception e) { - LogMessage($"SFTP[Int]: Error uploading moon image - {e.Message}"); + LogErrorMessage($"SFTP[Int]: Error uploading moon image - {e.Message}"); FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading moon image - {e.Message}"; } @@ -10379,7 +10509,7 @@ public async Task DoIntervalUpload() } catch (Exception e) { - LogMessage($"FTP[Int]: Error uploading moon image - {e.Message}"); + LogErrorMessage($"FTP[Int]: Error uploading moon image - {e.Message}"); FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error uploading moon image - {e.Message}"; } @@ -10536,7 +10666,7 @@ public async Task DoIntervalUpload() if (!File.Exists(uploadfile)) { - LogMessage($"PHP[Int]: Extra web file - {uploadfile} - not found!"); + LogWarningMessage($"PHP[Int]: Extra web file - {uploadfile} - not found!"); return; } @@ -10724,7 +10854,8 @@ public async Task DoIntervalUpload() .ForEach(item => { Interlocked.Increment(ref taskCount); - try { + try + { #if DEBUG LogDebugMessage($"PHP[Int]: Graph data file: {item.LocalFileName} waiting for semaphore [{uploadCountLimitSemaphoreSlim.CurrentCount}]"); uploadCountLimitSemaphoreSlim.Wait(cancellationToken); @@ -10948,7 +11079,7 @@ public async Task DoIntervalUpload() { Task.WaitAll(tasklist.ToArray(), cancellationToken); } - catch(Exception ex) + catch (Exception ex) { LogExceptionMessage(ex, "PHP[Int]: Error waiting on upload tasks"); FtpAlarm.Triggered = true; @@ -10984,7 +11115,7 @@ private bool UploadFile(FtpClient conn, string localfile, string remotefile, int { if (!File.Exists(localfile)) { - LogMessage($"FTP[{cycleStr}]: Error! Local file not found, aborting upload: {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}"; return true; @@ -11019,7 +11150,7 @@ private bool UploadStream(FtpClient conn, string remotefile, Stream dataStream, { if (dataStream.Length == 0) { - LogMessage($"FTP[{cycleStr}]: The data is empty - skipping upload of {remotefile}"); + LogWarningMessage($"FTP[{cycleStr}]: The data is empty - skipping upload of {remotefile}"); FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The data is empty - skipping upload of {remotefile}"; @@ -11080,7 +11211,7 @@ private bool UploadStream(FtpClient conn, string remotefile, Stream dataStream, if (status.IsFailure()) { - LogMessage($"FTP[{cycleStr}]: Upload of {remotefile} failed"); + LogErrorMessage($"FTP[{cycleStr}]: Upload of {remotefile} failed"); } else if (FTPRename) { @@ -11137,13 +11268,13 @@ public Stream GenerateStreamFromString(string s) // Return True if the connection still exists // Return False if the connection is disposed, null, or not connected - private bool UploadFile(SftpClient conn, string localfile, string remotefile, int cycle=-1) + private bool UploadFile(SftpClient conn, string localfile, string remotefile, int cycle = -1) { string cycleStr = cycle >= 0 ? cycle.ToString() : "Int"; if (!File.Exists(localfile)) { - LogMessage($"SFTP[{cycleStr}]: Error! Local file not found, aborting upload: {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}"; @@ -11168,7 +11299,7 @@ private bool UploadFile(SftpClient conn, string localfile, string remotefile, in FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"The SFTP object is disposed - skipping upload of {localfile}"; - if (cycle >=0) + if (cycle >= 0) RealtimeFTPReconnect(); return false; @@ -11198,7 +11329,7 @@ private bool UploadFile(SftpClient conn, string localfile, string remotefile, in return true; } - private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, int cycle=-1) + private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, int cycle = -1) { string remotefilename = FTPRename ? remotefile + "tmp" : remotefile; string cycleStr = cycle >= 0 ? cycle.ToString() : "Int"; @@ -11339,7 +11470,7 @@ private bool UploadStream(SftpClient conn, string remotefile, Stream dataStream, // Return True if the upload worked // Return False if the upload failed - private async Task UploadFile(HttpClient httpclient, string localfile, string remotefile, int cycle = -1, bool binary = false, bool utf8=true) + private async Task UploadFile(HttpClient httpclient, string localfile, string remotefile, int cycle = -1, bool binary = false, bool utf8 = true) { string cycleStr = cycle >= 0 ? cycle.ToString() : "Int"; @@ -11349,7 +11480,7 @@ private async Task UploadFile(HttpClient httpclient, string localfile, str { if (!File.Exists(localfile)) { - LogMessage($"PHP[{cycleStr}]: Error! Local file not found, aborting upload: {localfile}"); + LogWarningMessage($"PHP[{cycleStr}]: Error! Local file not found, aborting upload: {localfile}"); FtpAlarm.Triggered = true; FtpAlarm.LastMessage = $"Error! Local file not found, aborting upload: {localfile}"; @@ -11383,13 +11514,13 @@ private async Task UploadFile(HttpClient httpclient, string localfile, str } } - private async Task UploadString(HttpClient httpclient, bool incremental, string oldest, string data, string remotefile, int cycle = -1, bool binary = false, bool utf8=true) + private async Task UploadString(HttpClient httpclient, bool incremental, string oldest, string data, string remotefile, int cycle = -1, bool binary = false, bool utf8 = true) { string cycleStr = cycle >= 0 ? cycle.ToString() : "Int"; if (string.IsNullOrEmpty(data)) { - LogMessage($"PHP[{cycleStr}]: Uploading to {remotefile}. Error: The data string is empty, ignoring this upload"); + LogWarningMessage($"PHP[{cycleStr}]: Uploading to {remotefile}. Error: The data string is empty, ignoring this upload"); return false; } @@ -11407,11 +11538,11 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s { var encoding = new UTF8Encoding(false); - using (var request = new HttpRequestMessage(HttpMethod.Post, FtpOptions.PhpUrl)) + using (var request = new HttpRequestMessage()) { var unixTs = Utils.ToUnixTime(DateTime.Now).ToString(); - var signature = Utils.GetSHA256Hash(FtpOptions.PhpSecret, unixTs + remotefile + data); + request.RequestUri = new Uri(FtpOptions.PhpUrl); // disable expect 100 - PHP doesn't support it request.Headers.ExpectContinue = false; request.Headers.Add("ACTION", incremental ? "append" : "replace"); @@ -11420,57 +11551,91 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s { request.Headers.Add("OLDEST", oldest); } - request.Headers.Add("TS", unixTs); + + var signature = Utils.GetSHA256Hash(FtpOptions.PhpSecret, unixTs + remotefile + data); request.Headers.Add("SIGNATURE", signature); + + request.Headers.Add("TS", unixTs); request.Headers.Add("BINARY", binary ? "1" : "0"); request.Headers.Add("UTF8", utf8 ? "1" : "0"); - // Compress? if supported and payload exceeds 500 bytes - if (data.Length < 500 || FtpOptions.PhpCompression == "none") + + int len; + string encData = string.Empty; + + if (binary) + { + len = data.Length; + } + else + { + encData = Convert.ToBase64String(encoding.GetBytes(data)); + len = encData.Length; + } + + // if content < 7 KB-ish + if (len < 7000) { - request.Content = new StringContent(data, encoding, "text/plain"); + + if (!binary) + { + data = encData; + } + // send data in GET headers + request.Method = HttpMethod.Get; + request.Headers.Add("DATA", data); } + // else > 7 kB else { - using (var ms = new System.IO.MemoryStream()) + // send as POST + request.Method = HttpMethod.Post; + + // Compress? if supported and payload exceeds 500 bytes + if (data.Length >= 500 && FtpOptions.PhpCompression != "none") { - if (FtpOptions.PhpCompression == "gzip") + using (var ms = new MemoryStream()) { - using (var zipped = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true)) + if (FtpOptions.PhpCompression == "gzip") { - var byteData = encoding.GetBytes(data); - zipped.Write(byteData, 0, byteData.Length); + using (var zipped = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true)) + { + var byteData = encoding.GetBytes(data); + zipped.Write(byteData, 0, byteData.Length); + } } - } - else if (FtpOptions.PhpCompression == "deflate") - { - using (var zipped = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true)) + else if (FtpOptions.PhpCompression == "deflate") { - var byteData = encoding.GetBytes(data); - zipped.Write(byteData, 0, byteData.Length); + using (var zipped = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true)) + { + var byteData = encoding.GetBytes(data); + zipped.Write(byteData, 0, byteData.Length); + } } - } - ms.Position = 0; - byte[] compressed = new byte[ms.Length]; - ms.Read(compressed, 0, compressed.Length); + ms.Position = 0; + byte[] compressed = new byte[ms.Length]; + ms.Read(compressed, 0, compressed.Length); - outStream = new MemoryStream(compressed); - streamContent = new StreamContent(outStream); - streamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain"); - streamContent.Headers.Add("Content-Encoding", FtpOptions.PhpCompression); - streamContent.Headers.ContentLength = outStream.Length; + outStream = new MemoryStream(compressed); + streamContent = new StreamContent(outStream); + streamContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("text/plain"); + streamContent.Headers.Add("Content-Encoding", FtpOptions.PhpCompression); + streamContent.Headers.ContentLength = outStream.Length; - request.Content = streamContent; + request.Content = streamContent; + } } } + LogDebugMessage($"PHP[{cycleStr}]: Sending via {request.Method}"); + using (var response = await httpclient.SendAsync(request)) { //response.EnsureSuccessStatusCode(); var responseBodyAsText = await response.Content.ReadAsStringAsync(); if (response.StatusCode != HttpStatusCode.OK) { - LogMessage($"PHP[{cycleStr}]: Upload to {remotefile}: Response code = {(int) response.StatusCode}: {response.StatusCode}"); + LogWarningMessage($"PHP[{cycleStr}]: Upload to {remotefile}: Response code = {(int)response.StatusCode}: {response.StatusCode}"); LogMessage($"PHP[{cycleStr}]: Upload to {remotefile}: Response text follows:\n{responseBodyAsText}"); } else @@ -11479,13 +11644,15 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s LogDataMessage($"PHP[{cycleStr}]: Upload to {remotefile}: Response text follows:\n{responseBodyAsText}"); } - if (outStream != null) - outStream.Dispose(); + CheckPhpMaxUploads(response.Headers); - if (streamContent != null) - streamContent.Dispose(); + if (outStream != null) + outStream.Dispose(); - return response.StatusCode == HttpStatusCode.OK; + if (streamContent != null) + streamContent.Dispose(); + + return response.StatusCode == HttpStatusCode.OK; } } } @@ -11522,9 +11689,24 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s return false; } - public void LogMessage(string message) + public void LogMessage(string message, LogLevel level=LogLevel.Info) { Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff ") + message); + + if (level >= ErrorListLoggingLevel) + { + while (ErrorList.Count >= 50) + { + _ = ErrorList.Dequeue(); + } + ErrorList.Enqueue((DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss - ") + WebUtility.HtmlEncode(message))); + } + + if (level >= LogLevel.Error) + { + LatestError = message; + LatestErrorTS = DateTime.Now; + } } public void LogDebugMessage(string message) @@ -11604,6 +11786,12 @@ public void LogExceptionMessage(Exception ex, string message) { LogMessage(message); LogMessage(message + " - " + Utils.ExceptionToString(ex)); + + while (ErrorList.Count >= 50) + { + _ = ErrorList.Dequeue(); + } + ErrorList.Enqueue((DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss - ") + message)); } /* @@ -11613,6 +11801,19 @@ public string ReplaceCommas(string AStr) } */ + public string GetErrorLog() + { + var arr = ErrorList.ToArray(); + + if (arr.Length == 0) + { + return "[\"No errors recorded so far\"]"; + } + + return arr.Reverse().ToJson(); + } + + private void CreateRealtimeFile(int cycle) { // Does the user want to create the realtime.txt file? @@ -11630,7 +11831,7 @@ private void CreateRealtimeFile(int cycle) } catch (Exception ex) { - LogMessage("Error encountered during Realtime file update."); + LogErrorMessage("Error encountered during Realtime file update."); LogMessage(ex.Message); } } @@ -11771,7 +11972,7 @@ 59 8.4 Feels Like temperature } catch (Exception ex) { - LogMessage("Error encountered during Realtime file update."); + LogErrorMessage("Error encountered during Realtime file update."); LogMessage(ex.Message); } return string.Empty; @@ -11780,7 +11981,7 @@ 59 8.4 Feels Like temperature public void MySqlRealtimeFile(int cycle, bool live, DateTime? logdate = null) { - DateTime timestamp = (DateTime)(live ? DateTime.Now : logdate); + DateTime timestamp = (DateTime) (live ? DateTime.Now : logdate); if (!MySqlSettings.Realtime.Enabled) return; @@ -11838,7 +12039,7 @@ public void MySqlRealtimeFile(int cycle, bool live, DateTime? logdate = null) values.Append(station.Humidex.ToString(TempFormat, InvC) + ','); values.Append(station.UV.ToString(UVFormat, InvC) + ','); values.Append(station.ET.ToString(ETFormat, InvC) + ','); - values.Append(((int)station.SolarRad).ToString() + ','); + values.Append(((int) station.SolarRad).ToString() + ','); values.Append(station.AvgBearing.ToString() + ','); values.Append(station.RainLastHour.ToString(RainFormat, InvC) + ','); values.Append(station.Forecastnumber.ToString() + ",'"); @@ -11894,12 +12095,12 @@ private void ProcessTemplateFile(string template, string outputfile, bool useApp } catch (Exception e) { - LogMessage($"ProcessTemplateFile: Error writing to file '{outputfile}', error was - {e}"); + LogErrorMessage($"ProcessTemplateFile: Error writing to file '{outputfile}', error was - {e}"); } } } - private string ProcessTemplateFile2String(string template, bool useAppDir, bool utf8=false) + private string ProcessTemplateFile2String(string template, bool useAppDir, bool utf8 = false) { string templatefile = template; @@ -11917,7 +12118,7 @@ private string ProcessTemplateFile2String(string template, bool useAppDir, bool } else { - LogMessage($"ProcessTemplateFile: Error, template file not found - {templatefile}"); + LogWarningMessage($"ProcessTemplateFile: Error, template file not found - {templatefile}"); } return string.Empty; } @@ -11940,7 +12141,7 @@ private async Task ProcessTemplateFile2StringAsync(string template, bool } else { - LogMessage($"ProcessTemplateFile: Error, template file not found - {templatefile}"); + LogWarningMessage($"ProcessTemplateFile: Error, template file not found - {templatefile}"); } return string.Empty; } @@ -12168,7 +12369,7 @@ private async void CustomMysqlSecondsTimerTick(object sender, ElapsedEventArgs e } catch (Exception ex) { - LogMessage($"CustomSqlSecs[{i}]: Error - " + ex.Message); + LogErrorMessage($"CustomSqlSecs[{i}]: Error - " + ex.Message); } } customMySqlSecondsUpdateInProgress = false; @@ -12209,7 +12410,7 @@ internal async void CustomMysqlMinutesTimerTick() } catch (Exception ex) { - LogMessage($"CustomSqlMins[{i}]: Error - " + ex.Message); + LogErrorMessage($"CustomSqlMins[{i}]: Error - " + ex.Message); } } customMySqlMinutesUpdateInProgress = false; @@ -12242,7 +12443,7 @@ internal async void CustomMysqlRolloverTimerTick() } catch (Exception ex) { - LogMessage($"CustomSqlRollover[{i}]: Error - " + ex.Message); + LogErrorMessage($"CustomSqlRollover[{i}]: Error - " + ex.Message); } } customMySqlRolloverUpdateInProgress = false; @@ -12378,7 +12579,7 @@ public async Task CheckMySQLFailedUploads(string callingFunction, List c } catch (Exception ex) { - LogMessage($"{callingFunction}: Error - " + ex.Message); + LogErrorMessage($"{callingFunction}: Error - " + ex.Message); SqlCatchingUp = false; } } @@ -12437,7 +12638,7 @@ public void DoExtraEndOfDayFiles() } else { - LogMessage($"EOD: Error extra file {uploadfile} not found"); + LogWarningMessage($"EOD: Error extra file {uploadfile} not found"); } } } @@ -12539,11 +12740,11 @@ private void RealtimeFTPLogin() } catch (Exception ex) { - LogMessage($"RealtimeFTPLogin: Error connecting ftp - {ex.Message}"); + LogErrorMessage($"RealtimeFTPLogin: Error connecting ftp - {ex.Message}"); if (ex.InnerException != null) { ex = Utils.GetOriginalException(ex); - LogMessage($"RealtimeFTPLogin: Base exception - {ex.Message}"); + LogErrorMessage($"RealtimeFTPLogin: Base exception - {ex.Message}"); } RealtimeFTP.Disconnect(); } @@ -12580,7 +12781,7 @@ private void RealtimeSSHLogin() } else { - LogMessage($"RealtimeSSHLogin: Invalid SshftpAuthentication specified [{FtpOptions.SshAuthen}]"); + LogWarningMessage($"RealtimeSSHLogin: Invalid SshftpAuthentication specified [{FtpOptions.SshAuthen}]"); return; } @@ -12609,7 +12810,7 @@ private void RealtimeSSHLogin() } catch (Exception ex) { - LogMessage($"RealtimeSSHLogin: Error connecting SFTP - {ex.Message}"); + LogErrorMessage($"RealtimeSSHLogin: Error connecting SFTP - {ex.Message}"); } } } @@ -12630,7 +12831,7 @@ private async void WundCatchup() } catch (Exception ex) { - LogMessage("WU update: " + ex.Message); + LogErrorMessage("WU update: " + ex.Message); } } @@ -12654,7 +12855,7 @@ private async void WindyCatchUp() } catch (Exception ex) { - LogMessage("Windy update: " + ex.Message); + LogErrorMessage("Windy update: " + ex.Message); } } @@ -12684,7 +12885,7 @@ private async void PWSCatchUp() } catch (Exception ex) { - LogMessage("PWS update: " + ex.Message); + LogErrorMessage("PWS update: " + ex.Message); } } @@ -12714,7 +12915,7 @@ private async void WOWCatchUp() } catch (Exception ex) { - LogMessage("WOW update: " + ex.Message); + LogErrorMessage("WOW update: " + ex.Message); } } @@ -12754,7 +12955,7 @@ private async void OpenWeatherMapCatchUp() } catch (Exception ex) { - LogMessage("OpenWeatherMap: Update error = " + ex.Message); + LogErrorMessage("OpenWeatherMap: Update error = " + ex.Message); } } @@ -12786,7 +12987,7 @@ public async void UpdatePWSweather(DateTime timestamp) var responseBodyAsText = await response.Content.ReadAsStringAsync(); if (response.StatusCode != HttpStatusCode.OK) { - LogMessage($"PWS Response: ERROR - Response code = {response.StatusCode}, Body = {responseBodyAsText}"); + LogWarningMessage($"PWS Response: ERROR - Response code = {response.StatusCode}, Body = {responseBodyAsText}"); ThirdPartyAlarm.LastMessage = $"PWS: HTTP Response code = {response.StatusCode}, Body = {responseBodyAsText}"; ThirdPartyAlarm.Triggered = true; } @@ -12799,7 +13000,7 @@ public async void UpdatePWSweather(DateTime timestamp) } catch (Exception ex) { - LogMessage("PWS update: " + ex.Message); + LogErrorMessage("PWS update: " + ex.Message); ThirdPartyAlarm.LastMessage = "PWS: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -12844,7 +13045,7 @@ public async void UpdateWOW(DateTime timestamp) } catch (Exception ex) { - LogMessage("WOW update: " + ex.Message); + LogErrorMessage("WOW update: " + ex.Message); ThirdPartyAlarm.LastMessage = "WOW: " + ex.Message; ThirdPartyAlarm.Triggered = true; } @@ -12933,7 +13134,7 @@ await Task.Run(() => } catch (Exception ex) { - LogMessage($"{CallingFunction}: Error encountered during MySQL operation = {ex.Message}"); + LogErrorMessage($"{CallingFunction}: Error encountered during MySQL operation = {ex.Message}"); // if debug logging is disable, then log the failing statement anyway if (!DebuggingEnabled) { @@ -13022,7 +13223,7 @@ public void MySqlCommandSync(ConcurrentQueue Cmds, string CallingFunct } catch (Exception ex) { - LogMessage($"{CallingFunction}: Error encountered during MySQL operation = {ex.Message}"); + LogErrorMessage($"{CallingFunction}: Error encountered during MySQL operation = {ex.Message}"); // if debug logging is disable, then log the failing statement anyway if (!DebuggingEnabled) { @@ -13083,7 +13284,7 @@ internal void MySqlCommandErrorHandler(string CallingFunction, int ErrorCode, Co } catch (Exception ex) { - LogMessage($"{CallingFunction}: Error buffering command - " + ex.Message); + LogErrorMessage($"{CallingFunction}: Error buffering command - " + ex.Message); } } } @@ -13123,7 +13324,7 @@ public async void GetLatestVersion() { var msg = $"You are not running the latest version of Cumulus MX, build {LatestBuild} is available."; LogConsoleMessage(msg, ConsoleColor.Cyan); - LogMessage(msg); + LogWarningMessage(msg); UpgradeAlarm.LastMessage = $"Build {LatestBuild} is available"; UpgradeAlarm.Triggered = true; } @@ -13134,7 +13335,7 @@ public async void GetLatestVersion() } else if (int.Parse(Build) > int.Parse(LatestBuild)) { - LogMessage($"This Cumulus MX instance appears to be running a beta/test version. This build = {Build}, latest released build = {LatestBuild}"); + LogWarningMessage($"This Cumulus MX instance appears to be running a beta/test version. This build = {Build}, latest released build = {LatestBuild}"); } else { @@ -13257,7 +13458,7 @@ public async void CustomHttpRolloverUpdate() public void DegToDMS(decimal degrees, out int d, out int m, out int s) { - int secs = (int)(degrees * 60 * 60); + int secs = (int) (degrees * 60 * 60); s = secs % 60; @@ -13402,13 +13603,13 @@ private string GetUploadFilename(string input, DateTime dat) } else { - LogMessage("GetUploadFilename: No match found for in " + input); + LogWarningMessage("GetUploadFilename: No match found for in " + input); return input; } } catch (Exception ex) { - LogMessage($"GetUploadFilename: Error processing , value='{input}', error: {ex.Message}"); + LogErrorMessage($"GetUploadFilename: Error processing , value='{input}', error: {ex.Message}"); } } @@ -13453,13 +13654,13 @@ private string GetRemoteFileName(string input, DateTime dat) } else { - LogMessage("GetRemoteFileName: No match found for in " + input); + LogWarningMessage("GetRemoteFileName: No match found for in " + input); return input; } } catch (Exception ex) { - LogMessage($"GetRemoteFileName: Error processing , input='{input}', error: {ex.Message}"); + LogErrorMessage($"GetRemoteFileName: Error processing , input='{input}', error: {ex.Message}"); } } @@ -13484,22 +13685,22 @@ private void LogPrimaryAqSensor() { switch (StationOptions.PrimaryAqSensor) { - case (int)PrimaryAqSensor.Undefined: + case (int) PrimaryAqSensor.Undefined: LogMessage("Primary AQ Sensor = Undefined"); break; - case (int)PrimaryAqSensor.Ecowitt1: - case (int)PrimaryAqSensor.Ecowitt2: - case (int)PrimaryAqSensor.Ecowitt3: - case (int)PrimaryAqSensor.Ecowitt4: + case (int) PrimaryAqSensor.Ecowitt1: + case (int) PrimaryAqSensor.Ecowitt2: + case (int) PrimaryAqSensor.Ecowitt3: + case (int) PrimaryAqSensor.Ecowitt4: LogMessage("Primary AQ Sensor = Ecowitt" + StationOptions.PrimaryAqSensor); break; - case (int)PrimaryAqSensor.EcowittCO2: + case (int) PrimaryAqSensor.EcowittCO2: LogMessage("Primary AQ Sensor = Ecowitt CO2"); break; - case (int)PrimaryAqSensor.AirLinkIndoor: + case (int) PrimaryAqSensor.AirLinkIndoor: LogMessage("Primary AQ Sensor = AirLink Indoor"); break; - case (int)PrimaryAqSensor.AirLinkOutdoor: + case (int) PrimaryAqSensor.AirLinkOutdoor: LogMessage("Primary AQ Sensor = AirLink Outdoor"); break; } @@ -13510,7 +13711,7 @@ private void PingCompletedCallback(object sender, PingCompletedEventArgs e) // If an error occurred, display the exception to the user. if (e.Error != null) { - LogMessage("Ping failed: " + e.Error.Message + " > " + e.Error.GetBaseException().Message); + LogWarningMessage("Ping failed: " + e.Error.Message + " > " + e.Error.GetBaseException().Message); } else { @@ -13589,6 +13790,9 @@ public static class StationTypes public const int HttpAmbient = 15; public const int Tempest = 16; public const int Simulator = 17; + public const int EcowittCloud = 18; + public const int DavisCloudWll = 19; + public const int DavisCloudVP2 = 20; } /* @@ -13643,6 +13847,9 @@ public class ProgramOptionsClass public int DataStoppedMins { get; set; } public string TimeFormat { get; set; } public string TimeFormatLong { get; set; } + public bool SecureSettings { get; set; } + public string SettingsUsername { get; set; } + public string SettingsPassword { get; set; } } public class CultureConfig @@ -13803,7 +14010,7 @@ public class FineOffsetOptions public bool SetLoggerInterval { get; set; } public int VendorID { get; set; } public int ProductID { get; set; } -} + } public class ImetOptions { diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index 5cacc453..c61cf21e 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -1,16 +1,10 @@ - - - + Debug AnyCPU {67A70E28-25C7-4C7F-BD7B-959AE6834B2C} - Exe Properties - CumulusMX - CumulusMX - v4.8 - 512 + net48 false @@ -19,189 +13,24 @@ C:\Users\mcrossley\Code\CumulusMX-Dist\ true Disk - false - Foreground - 7 - Days - false - false true - 3064 - 3.3.1.3064 - false - true - true true - AnyCPU - true - full - false - bin\Debug\ - TRACE;DEBUG prompt - 0 MinimumRecommendedRules.ruleset - - - false - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - ".pdb"="" - true - false - - - - FA8B5CBB94229C9648BA8B533426B5DB50CB03F8 - - - CumulusMX_TemporaryKey.pfx - - - false - - - false - + icon.ico - - LocalIntranet - - - - true - - CumulusMX.Program - - - true - - - - - - - - - - False - - - - - - - - - - - - - - - False - ..\..\CumulusMX\CumulusMX\lib\Tmds.MDns.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Settings.settings - - - - Component - - - CumulusService.cs - - - Component - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -213,13 +42,6 @@ Settings.Designer.cs - - - False - .NET Framework 3.5 SP1 - false - - @@ -229,64 +51,35 @@ - - 3.5.2 - - - 47.1.0 - - - 2.1.0 - - - 4.1.0 - - - 4.2.1.781 - - - 2.2.7 - - - 2.2.5 - - - 6.10.0 - - - 2020.0.2 - - - 7.0.2 - - - 7.0.0 - + + + + + + + + + + + + + + + + + lib\Tmds.MDns.dll + - - echo. -echo Starting post-build tasks -echo Copy Updates.txt -xcopy "$(ProjectDir)..\Updates.txt" "$(TargetDir)\Updates.txt" /D /-I /Q /Y -echo copy sqlite 32 -xcopy "$(ProjectDir)\Libs\sqlite3-x86.dll" "$(TargetDir)\x86\sqlite3.dll" /D /-I /Q /Y -echo copy sqlite 64 -xcopy "$(ProjectDir)\Libs\sqlite3-x64.dll" "$(TargetDir)\x64\sqlite3.dll" /D /-I /Q /Y -echo. -echo Update Distribution folder -echo copy MX exe -xcopy "$(TargetPath)" "$(SolutionDir)..\CumulusMX-Dist\CumulusMX" /D /-I /Q /Y -echo copy MX config -xcopy "$(TargetDir)CumulusMX.exe.config" "$(SolutionDir)..\CumulusMX-Dist\CumulusMX" /D /-I /Q /Y - - + $(PackageVersion) + $(PackageVersion) + Exe + CumulusMX.Program + AnyCPU + Copyright © 2015-2023 Cumulus MX + 3.27.0.3257 - \ No newline at end of file diff --git a/CumulusMX/CumulusService.cs b/CumulusMX/CumulusService.cs index fc64b4bb..009ed727 100644 --- a/CumulusMX/CumulusService.cs +++ b/CumulusMX/CumulusService.cs @@ -84,16 +84,16 @@ protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) Program.cumulus.LogMessage("POWER: Detected system OEM EVENT"); break; case PowerBroadcastStatus.PowerStatusChange: - Program.cumulus.LogMessage("POWER: Detected system POWER STATUS CHANGE"); + Program.cumulus.LogWarningMessage("POWER: Detected system POWER STATUS CHANGE"); break; case PowerBroadcastStatus.QuerySuspend: - Program.cumulus.LogMessage("POWER: Detected system QUERY SUSPEND"); + Program.cumulus.LogWarningMessage("POWER: Detected system QUERY SUSPEND"); break; case PowerBroadcastStatus.QuerySuspendFailed: Program.cumulus.LogMessage("POWER: Detected system QUERY SUSPEND FAILED"); break; case PowerBroadcastStatus.ResumeAutomatic: - Program.cumulus.LogMessage("POWER: Detected system RESUME AUTOMATIC"); + Program.cumulus.LogWarningMessage("POWER: Detected system RESUME AUTOMATIC"); break; case PowerBroadcastStatus.ResumeCritical: Program.cumulus.LogMessage("POWER: Detected system RESUME CRITICAL, stopping service"); @@ -103,7 +103,7 @@ protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus) Program.exitSystem = true; break; case PowerBroadcastStatus.ResumeSuspend: - Program.cumulus.LogMessage("POWER: Detected system RESUMING FROM STANDBY"); + Program.cumulus.LogWarningMessage("POWER: Detected system RESUMING FROM STANDBY"); break; case PowerBroadcastStatus.Suspend: Program.cumulus.LogMessage("POWER: Detected system GOING TO STANDBY, stopping service"); diff --git a/CumulusMX/CustomLogs.cs b/CumulusMX/CustomLogs.cs index b1c2d95c..da2002a4 100644 --- a/CumulusMX/CustomLogs.cs +++ b/CumulusMX/CustomLogs.cs @@ -1,11 +1,10 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; -using System.Text; -using System.Threading.Tasks; + using EmbedIO; + using ServiceStack; namespace CumulusMX @@ -91,7 +90,7 @@ public string UpdateConfigIntvl(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Custom Interval Log Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Custom Interval Log Data: " + json); context.Response.StatusCode = 500; return msg; @@ -134,7 +133,7 @@ public string UpdateConfigIntvl(IHttpContext context) catch (Exception ex) { var msg = "Error processing settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -158,7 +157,7 @@ public string UpdateConfigDaily(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Custom Daily Log Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Custom Daily Log Data: " + json); context.Response.StatusCode = 500; return msg; @@ -198,7 +197,7 @@ public string UpdateConfigDaily(IHttpContext context) catch (Exception ex) { var msg = "Error processing settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); context.Response.StatusCode = 500; return msg; } diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs index 6a009dc6..affbcb19 100644 --- a/CumulusMX/DataEditor.cs +++ b/CumulusMX/DataEditor.cs @@ -3,11 +3,10 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; -using System.Web.UI; + using EmbedIO; -using Renci.SshNet.Common; + using ServiceStack; namespace CumulusMX @@ -61,7 +60,7 @@ internal string EditRainToday(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage("Edit rain today: " + ex.Message); + cumulus.LogErrorMessage("Edit rain today: " + ex.Message); } } @@ -123,7 +122,7 @@ internal string EditDiary(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage("Edit Diary: " + ex.Message); + cumulus.LogErrorMessage("Edit Diary: " + ex.Message); return "{\"result\":\"Failed\"}"; } } @@ -161,7 +160,7 @@ internal string DeleteDiary(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage("Delete Diary: " + ex.Message); + cumulus.LogErrorMessage("Delete Diary: " + ex.Message); return "{\"result\":\"Failed\"}"; } } @@ -586,7 +585,7 @@ internal string GetRecordsDayFile(string recordType, DateTime? start = null, Dat } else { - cumulus.LogMessage("GetRecordsDayFile: Error no day file records found"); + cumulus.LogWarningMessage("GetRecordsDayFile: Error no day file records found"); } json.Append($"\"highTempValDayfile\":\"{highTemp.GetValString(cumulus.TempFormat)}\","); @@ -1101,7 +1100,7 @@ internal string GetRecordsLogFile(string recordType) } catch (Exception e) { - cumulus.LogMessage($"GetRecordsLogFile: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogWarningMessage($"GetRecordsLogFile: Error at line {linenum} of {logFile} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); } } @@ -2082,7 +2081,7 @@ internal string GetMonthlyRecDayFile() } else { - cumulus.LogMessage("Error failed to find day records"); + cumulus.LogWarningMessage("Error failed to find day records"); } for (var i = 0; i < 12; i++) @@ -2604,7 +2603,7 @@ internal string GetMonthlyRecLogFile() } catch (Exception e) { - cumulus.LogMessage($"Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogWarningMessage($"Error at line {linenum} of {logFile} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); } } @@ -3314,7 +3313,7 @@ internal string EditDayFile(IHttpContext context) } catch { - cumulus.LogMessage("EditDayFile: Failed, new data does not match required values"); + cumulus.LogErrorMessage("EditDayFile: Failed, new data does not match required values"); cumulus.LogMessage("EditDayFile: Data received - " + newLine); context.Response.StatusCode = 500; @@ -3365,7 +3364,7 @@ internal string EditDayFile(IHttpContext context) updt.Append($"THighHeatInd={(station.DayFile[lineNum].HighHeatIndex > Cumulus.DefaultHiVal ? station.DayFile[lineNum].HighHeatIndexTime.ToString("\\'HH:mm\\'") : "NULL")},"); updt.Append($"HighAppTemp={(station.DayFile[lineNum].HighAppTemp > Cumulus.DefaultHiVal ? station.DayFile[lineNum].HighAppTemp.ToString(cumulus.TempFormat, InvC) : "NULL")},"); updt.Append($"THighAppTemp={(station.DayFile[lineNum].HighAppTemp > Cumulus.DefaultHiVal ? station.DayFile[lineNum].HighAppTempTime.ToString("\\'HH:mm\\'") : "NULL")},"); - updt.Append($"LowAppTemp={(station.DayFile[lineNum].LowAppTemp < Cumulus.DefaultLoVal ? station.DayFile[lineNum].LowAppTemp.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"LowAppTemp={(station.DayFile[lineNum].LowAppTemp < Cumulus.DefaultLoVal ? station.DayFile[lineNum].LowAppTemp.ToString(cumulus.TempFormat, InvC) : "NULL")},"); updt.Append($"TLowAppTemp={(station.DayFile[lineNum].LowAppTemp < Cumulus.DefaultLoVal ? station.DayFile[lineNum].LowAppTempTime.ToString("\\'HH:mm\\'") : "NULL")},"); updt.Append($"HighHourRain={station.DayFile[lineNum].HighHourlyRain.ToString(cumulus.RainFormat, InvC)},"); updt.Append($"THighHourRain={station.DayFile[lineNum].HighHourlyRainTime:\\'HH:mm\\'},"); @@ -3402,7 +3401,7 @@ internal string EditDayFile(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditDayFile: Failed, to update MySQL. Error = {ex.Message}"); + cumulus.LogErrorMessage($"EditDayFile: Failed, to update MySQL. Error = {ex.Message}"); cumulus.LogMessage($"EditDayFile: SQL Update statement = {updateStr}"); context.Response.StatusCode = 501; // Use 501 to signal that SQL failed but file update was OK @@ -3413,13 +3412,13 @@ internal string EditDayFile(IHttpContext context) else { // ohoh! The dates do not match - cumulus.LogMessage($"EditDayFile: Dates do not match. FormDate: {newData.data[0][0]}, FileDate: {orgLine.Split(sep[0])[0]}"); + cumulus.LogErrorMessage($"EditDayFile: Dates do not match. FormDate: {newData.data[0][0]}, FileDate: {orgLine.Split(sep[0])[0]}"); return $"{{\"errors\":{{\"General\":[\"
Dates do not match. FormDate: {newData.data[0][0]}, FileDate: {orgLine.Split(sep[0])[0]}\"]}}}}"; } } catch (Exception ex) { - cumulus.LogMessage($"EditDayFile: Failed. Error = {ex.Message}"); + cumulus.LogErrorMessage($"EditDayFile: Failed. Error = {ex.Message}"); return "{\"errors\":{\"General\":[\"
Error occurred: " + ex.Message + "\"]}}"; } @@ -3439,7 +3438,7 @@ internal string EditDayFile(IHttpContext context) var formDate = newData.data[i][0]; if (lineData[0] != formDate) { - cumulus.LogMessage($"EditDayFile: Entry deletion failed. Line to delete does not match the file contents"); + cumulus.LogErrorMessage($"EditDayFile: Entry deletion failed. Line to delete does not match the file contents"); cumulus.LogMessage($"EditDayFile: Line: {lineNum + 1}, filedate = {lineData[0]}, formdate = {formDate}"); context.Response.StatusCode = 500; return $"{{\"errors\":{{\"Logfile\":[\"
Failed, line to delete does not match the file contents\", \"
Line: {lineNum + 1}, filedate = {lineData[0]}, formdate = {formDate}\"]}}}}"; @@ -3454,7 +3453,7 @@ internal string EditDayFile(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditDayFile: Entry deletion failed. Error = - " + ex.Message); + cumulus.LogErrorMessage($"EditDayFile: Entry deletion failed. Error = - " + ex.Message); cumulus.LogMessage($"EditDayFile: Entry data = " + newData.data[i]); context.Response.StatusCode = 500; return "{\"errors\":{\"Logfile\":[\"
Failed to delete record. Error: " + ex.Message + "\"]}}"; @@ -3469,7 +3468,7 @@ internal string EditDayFile(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditDayFile: Error writing to the dayfile. Error = - " + ex.Message); + cumulus.LogErrorMessage($"EditDayFile: Error writing to the dayfile. Error = - " + ex.Message); context.Response.StatusCode = 500; return "{\"errors\":{\"Logfile\":[\"
Error writing to the dayfile. Error: " + ex.Message + "\"]}}"; } @@ -3478,7 +3477,7 @@ internal string EditDayFile(IHttpContext context) } else { - cumulus.LogMessage($"EditDayFile: Unrecognised action = " + newData.action); + cumulus.LogErrorMessage($"EditDayFile: Unrecognised action = " + newData.action); context.Response.StatusCode = 500; return "{\"errors\":{\"Logfile\":[\"
Failed, unrecognised action = " + newData.action + "\"]}}"; } @@ -3513,7 +3512,7 @@ internal string EditMySqlCache(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditMySqlCache: Failed, to update MySQL statement. Error = {ex.Message}"); + cumulus.LogErrorMessage($"EditMySqlCache: Failed, to update MySQL statement. Error = {ex.Message}"); context.Response.StatusCode = 500; return "{\"errors\":{\"MySqlCache\":[\"Failed to update MySQL cache\"]}, \"data\":[\"" + newRec.statement + "\"]"; @@ -3540,7 +3539,7 @@ internal string EditMySqlCache(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditMySqlCache: Failed, to delete MySQL statement. Error = {ex.Message}"); + cumulus.LogErrorMessage($"EditMySqlCache: Failed, to delete MySQL statement. Error = {ex.Message}"); context.Response.StatusCode = 500; return "{\"errors\":{\"MySqlCache\":[\"Failed to update MySQL cache\"]}, \"data\":[\"" + newRec.statement + "\"]"; @@ -3550,7 +3549,7 @@ internal string EditMySqlCache(IHttpContext context) } else { - cumulus.LogMessage($"EditMySqlCache: Unrecognised action = " + newData.action); + cumulus.LogErrorMessage($"EditMySqlCache: Unrecognised action = " + newData.action); context.Response.StatusCode = 500; return "{\"errors\":{\"SQL cache\":[\"
Failed, unrecognised action = " + newData.action + "\"]}}"; } @@ -3614,7 +3613,7 @@ internal string EditDatalog(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage("EditDataLog: Failed, error = " + ex.Message); + cumulus.LogErrorMessage("EditDataLog: Failed, error = " + ex.Message); cumulus.LogMessage("EditDataLog: Data received - " + newLine); context.Response.StatusCode = 500; @@ -3728,7 +3727,7 @@ internal string EditDatalog(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditDataLog: Failed, to update MySQL. Error = {ex.Message}"); + cumulus.LogErrorMessage($"EditDataLog: Failed, to update MySQL. Error = {ex.Message}"); cumulus.LogMessage($"EditDataLog: SQL Update statement = {updateStr}"); context.Response.StatusCode = 501; // Use 501 to signal that SQL failed but file update was OK @@ -3782,7 +3781,7 @@ internal string EditDatalog(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditDataLog: Entry deletion failed. Error = - " + ex.Message); + cumulus.LogErrorMessage($"EditDataLog: Entry deletion failed. Error = - " + ex.Message); cumulus.LogMessage($"EditDataLog: Entry data = - " + thisrec.ToJson()); context.Response.StatusCode = 500; return "{\"errors\": { \"Logfile\": [\"
Failed to delete record. Error: " + ex.Message + "\"]}}"; @@ -3790,7 +3789,7 @@ internal string EditDatalog(IHttpContext context) } else { - cumulus.LogMessage($"EditDataLog: Error. Line to delete {newData.data[i][0]} {newData.data[i][1]} does not match the file contents {lineData[0]} {lineData[1]}"); + cumulus.LogErrorMessage($"EditDataLog: Error. Line to delete {newData.data[i][0]} {newData.data[i][1]} does not match the file contents {lineData[0]} {lineData[1]}"); context.Response.StatusCode = 500; return "{\"errors\":{\"Logfile\":[\"Failed, line to delete does not match the file contents\"]}}"; } @@ -3805,7 +3804,7 @@ internal string EditDatalog(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage($"EditDataLog: Error writing to the logfile {logfile}. Error = - {ex.Message}"); + cumulus.LogErrorMessage($"EditDataLog: Error writing to the logfile {logfile}. Error = - {ex.Message}"); context.Response.StatusCode = 500; return "{\"errors\":{\"Logfile\":[\"
Error writing to the logfile " + logfile + ". Error: " + ex.Message + "\"]}}"; } @@ -3843,7 +3842,7 @@ private bool SetCurrCondText(string currCondText) } catch (Exception e) { - cumulus.LogMessage("Error writing current conditions to file - " + e.Message); + cumulus.LogErrorMessage("Error writing current conditions to file - " + e.Message); return false; } } diff --git a/CumulusMX/DataStruct.cs b/CumulusMX/DataStruct.cs index b9a573de..3fc32f38 100644 --- a/CumulusMX/DataStruct.cs +++ b/CumulusMX/DataStruct.cs @@ -328,7 +328,7 @@ public string LowWindChillTodayRounded [IgnoreDataMember] public double HighRainRateToday { get; set; } - [DataMember(Name="HighRainRateToday")] + [DataMember(Name = "HighRainRateToday")] public string HighRainRateTodayRounded { get => HighRainRateToday.ToString(cumulus.RainFormat); @@ -524,7 +524,7 @@ public string RecentmaxgustRounded [IgnoreDataMember] public double WindRunToday { get; set; } - [DataMember(Name="WindRunToday")] + [DataMember(Name = "WindRunToday")] public string WindRunTodayRounded { get => WindRunToday.ToString(cumulus.WindRunFormat); diff --git a/CumulusMX/DavisAirLink.cs b/CumulusMX/DavisAirLink.cs index 6d486a09..cb86e58f 100644 --- a/CumulusMX/DavisAirLink.cs +++ b/CumulusMX/DavisAirLink.cs @@ -1,17 +1,18 @@ using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Net.Http; using System.Text; using System.Threading; -using System.Linq; +using System.Threading.Tasks; using System.Timers; -using System.Net.Http; -using Tmds.MDns; -using System.Net; -using System.ComponentModel; -using System.Collections.Generic; + using ServiceStack; + using Swan; -using System.Threading.Tasks; -using ServiceStack.Text; + +using Tmds.MDns; namespace CumulusMX { @@ -107,7 +108,7 @@ public DavisAirLink(Cumulus cumulus, bool indoor, WeatherStation station) { // We didn't find anything on the network msg = "Failed to discover any AirLink devices"; - cumulus.LogMessage("ZeroConf Service: " + msg); + cumulus.LogWarningMessage("ZeroConf Service: " + msg); cumulus.LogConsoleMessage(msg); } else if (discovered.IP.Count == 1 && (string.IsNullOrEmpty(hostname) || discovered.Hostname[0] == hostname) && numOfAirLinks == 1) @@ -159,8 +160,8 @@ public DavisAirLink(Cumulus cumulus, bool indoor, WeatherStation station) if (discovered.IP[idx] != ipaddr) { - cumulus.LogMessage($"ZeroConf Service: Discovered a new IP address for the {locationStr} AirLink that does not match our current one"); - cumulus.LogMessage($"ZeroConf Service: Changing previous {locationStr} IP address: {ipaddr} to {discovered.IP[idx]}"); + cumulus.LogWarningMessage($"ZeroConf Service: Discovered a new IP address for the {locationStr} AirLink that does not match our current one"); + cumulus.LogWarningMessage($"ZeroConf Service: Changing previous {locationStr} IP address: {ipaddr} to {discovered.IP[idx]}"); ipaddr = discovered.IP[idx]; if (indoor) cumulus.AirLinkInIPAddr = ipaddr; @@ -245,9 +246,8 @@ public void Start() cumulus.LogMessage($"AirLink {locationStr} Starting up"); try { - // Get the current conditions and health immediately to populate the web tags + // Get the current conditions immediately to populate the web tags GetAlCurrent(null, null); - GetWlHistoricHealth(); // Create a current conditions thread to poll readings every 30 seconds tmrCurrent.Elapsed += GetAlCurrent; @@ -258,6 +258,9 @@ public void Start() // Only poll health data here if the AirLink is a stand-alone device - the stand-alone history flag shows we have all the required info to poll wl.com if (standaloneHistory) { + // Get the health immediately to populate the web tags + GetWlHistoricHealth(); + // get the health data every 15 minutes tmrHealth = new System.Timers.Timer(); tmrHealth.Elapsed += HealthTimerTick; @@ -341,8 +344,8 @@ private async void GetAlCurrent(object source, ElapsedEventArgs e) } catch (Exception ex) { - cumulus.LogMessage("GetAlCurrent: Error processing the AirLink response"); - cumulus.LogMessage("GetAlCurrent: Error: " + ex.Message); + cumulus.LogErrorMessage("GetAlCurrent: Error processing the AirLink response"); + cumulus.LogErrorMessage("GetAlCurrent: Error: " + ex.Message); } retry = 9; } @@ -359,7 +362,7 @@ private async void GetAlCurrent(object source, ElapsedEventArgs e) } else { - cumulus.LogMessage($"GetAlCurrent: {locationStr} - Invalid IP address: {ip}"); + cumulus.LogErrorMessage($"GetAlCurrent: {locationStr} - Invalid IP address: {ip}"); } updateInProgress = false; } @@ -572,7 +575,7 @@ private void AlReadHistory(object sender, DoWorkEventArgs e) } catch (Exception ex) { - cumulus.LogMessage("AirLink: Exception occurred reading archive data: " + ex.Message); + cumulus.LogErrorMessage("AirLink: Exception occurred reading archive data: " + ex.Message); } } @@ -589,7 +592,7 @@ private void GetWlHistoricData() { if (string.IsNullOrEmpty(cumulus.AirLinkApiKey) || string.IsNullOrEmpty(cumulus.AirLinkApiSecret)) { - cumulus.LogMessage("GetWlHistoricData: Missing AirLink WeatherLink API data in the configuration, aborting!"); + cumulus.LogWarningMessage("GetWlHistoricData: Missing AirLink WeatherLink API data in the configuration, aborting!"); return; } @@ -601,7 +604,7 @@ private void GetWlHistoricData() { if (string.IsNullOrEmpty(cumulus.WllApiKey) || string.IsNullOrEmpty(cumulus.WllApiSecret)) { - cumulus.LogMessage("GetWlHistoricData: Missing WLL WeatherLink API data in the configuration, aborting!"); + cumulus.LogWarningMessage("GetWlHistoricData: Missing WLL WeatherLink API data in the configuration, aborting!"); return; } @@ -614,7 +617,7 @@ private void GetWlHistoricData() if (stationId < 10) { var msg = "No AirLink WeatherLink API station ID in the configuration"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); cumulus.LogConsoleMessage("GetWlHistoricData: " + msg); return; } @@ -663,7 +666,7 @@ private void GetWlHistoricData() using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDebugMessage($"GetWlHistoricData: WeatherLink API Historic Response code: {responseCode}"); cumulus.LogDataMessage($"GetWlHistoricData: WeatherLink API Historic Response: {responseBody}"); } @@ -671,7 +674,7 @@ private void GetWlHistoricData() if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"GetWlHistoricData: WeatherLink API Historic Error: {errObj.code}, {errObj.message}"); + cumulus.LogErrorMessage($"GetWlHistoricData: WeatherLink API Historic Error: {errObj.code}, {errObj.message}"); cumulus.LogConsoleMessage($" - Error {errObj.code}: {errObj.message}"); airLinkLastUpdateTime = Utils.FromUnixTime(endTime); return; @@ -679,52 +682,54 @@ private void GetWlHistoricData() if (responseBody == "{}") { - cumulus.LogMessage("GetWlHistoricData: WeatherLink API Historic: No data was returned. Check your Device Id."); + cumulus.LogWarningMessage("GetWlHistoricData: WeatherLink API Historic: No data was returned. Check your Device Id."); cumulus.LogConsoleMessage(" - No historic data available"); airLinkLastUpdateTime = Utils.FromUnixTime(endTime); return; } - - if (!responseBody.StartsWith("{\"sensors\":[{\"lsid\"")) // sanity check + else if (responseBody.StartsWith("{\"")) // basic sanity check { - cumulus.LogMessage("GetWlHistoricData: Invalid historic message received"); - cumulus.LogDataMessage("GetWlHistoricData: Received: " + responseBody); - airLinkLastUpdateTime = Utils.FromUnixTime(endTime); - return; - } - histObj = responseBody.FromJson(); + histObj = responseBody.FromJson(); - // get the sensor data with the most number of history records - int idxOfSensorWithMostRecs = 0; - for (var i = 0; i < histObj.sensors.Count; i++) - { - if (histObj.sensors[i].sensor_type != 504) + // get the sensor data with the most number of history records + int idxOfSensorWithMostRecs = 0; + for (var i = 0; i < histObj.sensors.Count; i++) { - var recs = histObj.sensors[i].data.Count; - if (recs > noOfRecs) + if (histObj.sensors[i].sensor_type != 504) { - noOfRecs = recs; - idxOfSensorWithMostRecs = i; + var recs = histObj.sensors[i].data.Count; + if (recs > noOfRecs) + { + noOfRecs = recs; + idxOfSensorWithMostRecs = i; + } } } - } - sensorWithMostRecs = histObj.sensors[idxOfSensorWithMostRecs]; + sensorWithMostRecs = histObj.sensors[idxOfSensorWithMostRecs]; + + if (noOfRecs == 0) + { + cumulus.LogMessage("GetWlHistoricData: No historic data available"); + cumulus.LogConsoleMessage(" - No historic data available"); + airLinkLastUpdateTime = Utils.FromUnixTime(endTime); + return; + } - if (noOfRecs == 0) + cumulus.LogMessage($"GetWlHistoricData: Found {noOfRecs} historic records to process"); + } + else // No idea what we got, dump it to the log { - cumulus.LogMessage("GetWlHistoricData: No historic data available"); - cumulus.LogConsoleMessage(" - No historic data available"); - airLinkLastUpdateTime = Utils.FromUnixTime(endTime); + cumulus.LogErrorMessage("GetWlHistoricData: Invalid historic message received"); + cumulus.LogMessage("GetWlHistoricData: Received: " + responseBody); + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); return; } - - cumulus.LogMessage($"GetWlHistoricData: Found {noOfRecs} historic records to process"); } catch (Exception ex) { - cumulus.LogMessage("GetWlHistoricData: Exception: " + ex.Message); + cumulus.LogErrorMessage("GetWlHistoricData: Exception: " + ex.Message); airLinkLastUpdateTime = Utils.FromUnixTime(endTime); return; } @@ -804,12 +809,12 @@ private void GetWlHistoricData() cumulus.DoAirLinkLogFile(timestamp); if (!Program.service) - Console.Write("\r - processed " + (((double)dataIndex + 1) / noOfRecs).ToString("P0")); + Console.Write("\r - processed " + (((double) dataIndex + 1) / noOfRecs).ToString("P0")); cumulus.LogMessage($"GetWlHistoricData: {(dataIndex + 1)} of {noOfRecs} archive entries processed"); } catch (Exception ex) { - cumulus.LogMessage("GetWlHistoricData: Exception: " + ex.Message); + cumulus.LogErrorMessage("GetWlHistoricData: Exception: " + ex.Message); } } @@ -867,7 +872,7 @@ public void DecodeAlHistoric(int dataType, string json) if (data17.temp_avg == -99) { - cumulus.LogMessage($"DecodeAlHistoric: No valid temperature value found"); + cumulus.LogWarningMessage($"DecodeAlHistoric: No valid temperature value found"); } else { @@ -933,7 +938,7 @@ public void DecodeAlHistoric(int dataType, string json) // If we are the primary AQ sensor, // and we are not linked to a WLL, // then add the PM data into the graphdata list - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkIndoor && standaloneHistory) + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkIndoor && standaloneHistory) { //station.UpdateGraphDataAqEntry(Utils.FromUnixTime(data17.ts), cumulus.airLinkDataIn.pm2p5, cumulus.airLinkDataIn.pm10); station.UpdateRecentDataAqEntry(Utils.FromUnixTime(data17.ts), cumulus.airLinkDataIn.pm2p5, cumulus.airLinkDataIn.pm10); @@ -964,7 +969,7 @@ public void DecodeAlHistoric(int dataType, string json) // If we are the primary AQ sensor, // and we are not linked to a WLL, // then add the PM data into the graphdata list - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor && standaloneHistory) + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor && standaloneHistory) { //station.UpdateGraphDataAqEntry(Utils.FromUnixTime(data17.ts), cumulus.airLinkDataOut.pm2p5, cumulus.airLinkDataOut.pm10); station.UpdateRecentDataAqEntry(Utils.FromUnixTime(data17.ts), cumulus.airLinkDataOut.pm2p5, cumulus.airLinkDataOut.pm10); @@ -986,7 +991,7 @@ public void DecodeAlHistoric(int dataType, string json) } catch (Exception ex) { - cumulus.LogMessage($"DecodeAlHistoric: {locationStr} - Exception: {ex.Message}"); + cumulus.LogErrorMessage($"DecodeAlHistoric: {locationStr} - Exception: {ex.Message}"); } } @@ -1016,7 +1021,7 @@ private void GetWlHistoricHealth() { var msg = "Missing AirLink WeatherLink API key/secret in the cumulus.ini file"; cumulus.LogConsoleMessage(msg); - cumulus.LogMessage("AirLinkHealth: " + msg); + cumulus.LogWarningMessage("AirLinkHealth: " + msg); return; } apiKey = cumulus.AirLinkApiKey; @@ -1026,7 +1031,7 @@ private void GetWlHistoricHealth() { var msg = "Missing AirLink WeatherLink API station Id in the cumulus.ini file"; cumulus.LogConsoleMessage(msg); - cumulus.LogMessage("AirLinkHealth: " + msg); + cumulus.LogWarningMessage("AirLinkHealth: " + msg); GetAvailableStationIds(); return; } @@ -1036,7 +1041,7 @@ private void GetWlHistoricHealth() { if (string.IsNullOrEmpty(cumulus.WllApiKey) || string.IsNullOrEmpty(cumulus.WllApiSecret) || cumulus.WllStationId < 10) { - cumulus.LogMessage("AirLinkHealth: Missing WLL WeatherLink API key/secret/station Id in the cumulus.ini file, aborting!"); + cumulus.LogWarningMessage("AirLinkHealth: Missing WLL WeatherLink API key/secret/station Id in the cumulus.ini file, aborting!"); return; } apiKey = cumulus.WllApiKey; @@ -1071,20 +1076,20 @@ private void GetWlHistoricHealth() using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDataMessage($"AirLinkHealth: WeatherLink API Response: {responseCode}: {responseBody}"); } if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"AirLinkHealth: WeatherLink API Error: {errObj.code}, {errObj.message}"); + cumulus.LogErrorMessage($"AirLinkHealth: WeatherLink API Error: {errObj.code}, {errObj.message}"); return; } if (responseBody == "{}") { - cumulus.LogMessage("AirLinkHealth: WeatherLink API: No data was returned. Check your Device Id."); + cumulus.LogWarningMessage("AirLinkHealth: WeatherLink API: No data was returned. Check your Device Id."); airLinkLastUpdateTime = Utils.FromUnixTime(endTime); return; } @@ -1092,7 +1097,7 @@ private void GetWlHistoricHealth() if (!responseBody.StartsWith("{\"sensors\":[{\"lsid\"")) // sanity check { // No idea what we got, dump it to the log - cumulus.LogMessage("AirLinkHealth: Invalid historic message received"); + cumulus.LogErrorMessage("AirLinkHealth: Invalid historic message received"); cumulus.LogDataMessage("AirLinkHealth: Received: " + responseBody); return; } @@ -1121,12 +1126,12 @@ private void GetWlHistoricHealth() } catch (Exception ex) { - cumulus.LogMessage("AirLinkHealth: exception: " + ex.Message); + cumulus.LogErrorMessage("AirLinkHealth: exception: " + ex.Message); } } catch (Exception ex) { - cumulus.LogMessage("AirLinkHealth: exception: " + ex.Message); + cumulus.LogErrorMessage("AirLinkHealth: exception: " + ex.Message); } //cumulus.BatteryLowAlarmState = TxBatText.Contains("LOW") || wllVoltageLow; @@ -1194,7 +1199,7 @@ internal void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) } catch (Exception ex) { - cumulus.LogMessage($"AirLinkHealth: {locationStr} - Error processing firmware version: {ex.Message}"); + cumulus.LogErrorMessage($"AirLinkHealth: {locationStr} - Error processing firmware version: {ex.Message}"); cumulus.LogMessage($"AirLinkHealth: {locationStr} - No valid firmware version found"); if (indoor) { @@ -1220,7 +1225,7 @@ internal void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) { var upt = TimeSpan.FromSeconds(data.uptime); var uptStr = string.Format("{0}d:{1:D2}h:{2:D2}m:{3:D2}s", - (int)upt.TotalDays, + (int) upt.TotalDays, upt.Hours, upt.Minutes, upt.Seconds); @@ -1228,14 +1233,14 @@ internal void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) } catch (Exception ex) { - cumulus.LogMessage($"AirLinkHealth: {locationStr} - Error processing uptime: {ex.Message}"); + cumulus.LogErrorMessage($"AirLinkHealth: {locationStr} - Error processing uptime: {ex.Message}"); } try { var upt = TimeSpan.FromSeconds(data.link_uptime); var uptStr = string.Format("{0}d:{1:D2}h:{2:D2}m:{3:D2}s", - (int)upt.TotalDays, + (int) upt.TotalDays, upt.Hours, upt.Minutes, upt.Seconds); @@ -1243,13 +1248,13 @@ internal void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) } catch (Exception ex) { - cumulus.LogMessage($"AirLinkHealth: {locationStr} - Error processing link uptime: {ex.Message}"); + cumulus.LogErrorMessage($"AirLinkHealth: {locationStr} - Error processing link uptime: {ex.Message}"); } // Only present if WiFi attached if (!data.wifi_rssi.HasValue) { - cumulus.LogMessage($"AirLinkHealth: {locationStr} - No WiFi RSSI value found"); + cumulus.LogWarningMessage($"AirLinkHealth: {locationStr} - No WiFi RSSI value found"); } else { @@ -1266,8 +1271,8 @@ internal void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) try { - var txCnt = (int)data.tx_packets; - var rxCnt = (int)data.rx_packets; + var txCnt = (int) data.tx_packets; + var rxCnt = (int) data.rx_packets; var dropped = data.dropped_packets; var bad = data.packet_errors; var error = data.network_error.HasValue ? data.network_error.Value.ToString() : "none"; @@ -1275,12 +1280,12 @@ internal void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) } catch (Exception ex) { - cumulus.LogMessage($"AirLinkHealth: {locationStr} - Error processing xmt count: {ex.Message}"); + cumulus.LogErrorMessage($"AirLinkHealth: {locationStr} - Error processing xmt count: {ex.Message}"); } } catch (Exception ex) { - cumulus.LogMessage($"AirLinkHealth: {locationStr} - Exception caught in health data: {ex.Message}"); + cumulus.LogErrorMessage($"AirLinkHealth: {locationStr} - Exception caught in health data: {ex.Message}"); } } } @@ -1317,14 +1322,14 @@ private bool GetAvailableStationIds() using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDebugMessage($"WeatherLink API Response: {responseCode}: {responseBody}"); } if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"WeatherLink API Error: {errObj.code} - {errObj.message}"); + cumulus.LogWarningMessage($"WeatherLink API Error: {errObj.code} - {errObj.message}"); return false; } @@ -1340,7 +1345,7 @@ private bool GetAvailableStationIds() if ((station.station_id == cumulus.AirLinkInStationId || station.station_id == cumulus.AirLinkOutStationId) && station.recording_interval != cumulus.logints[cumulus.DataLogInterval]) { - cumulus.LogMessage($" - Cumulus log interval {cumulus.logints[cumulus.DataLogInterval]} does not match this WeatherLink stations log interval {station.recording_interval}"); + cumulus.LogWarningMessage($" - Cumulus log interval {cumulus.logints[cumulus.DataLogInterval]} does not match this WeatherLink stations log interval {station.recording_interval}"); } } if (stationsObj.stations.Count > 1) @@ -1379,13 +1384,13 @@ private void GetAvailableSensors() if (string.IsNullOrEmpty(cumulus.AirLinkApiKey) || string.IsNullOrEmpty(cumulus.AirLinkApiSecret)) { - cumulus.LogMessage("GetAvailableSensors: WeatherLink AirLink API data is missing in the configuration, aborting!"); + cumulus.LogWarningMessage("GetAvailableSensors: WeatherLink AirLink API data is missing in the configuration, aborting!"); return; } if ((indoor && cumulus.AirLinkInStationId < 10) || (!indoor && cumulus.AirLinkOutStationId < 10)) { - cumulus.LogMessage($"GetAvailableSensors: No WeatherLink AirLink API station ID has been configured, aborting!"); + cumulus.LogWarningMessage($"GetAvailableSensors: No WeatherLink AirLink API station ID has been configured, aborting!"); return; } @@ -1405,14 +1410,14 @@ private void GetAvailableSensors() using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDebugMessage($"GetAvailableSensors: WeatherLink API Response: {responseCode}: {responseBody}"); } if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"GetAvailableSensors: WeatherLink API Error: {errObj.code} - {errObj.message}"); + cumulus.LogErrorMessage($"GetAvailableSensors: WeatherLink API Error: {errObj.code} - {errObj.message}"); return; } @@ -1561,7 +1566,7 @@ private void DoAqi(AirLinkData data) data.aqiPm10_24hr = AirQualityIndices.US_EPApm10(data.pm10_24hr); data.aqiPm10_nowcast = AirQualityIndices.US_EPApm10(data.pm10_nowcast); break; - case 1: // UK CMEAP + case 1: // UK COMEAP data.aqiPm2p5 = AirQualityIndices.UK_COMEAPpm2p5(data.pm2p5); data.aqiPm2p5_1hr = AirQualityIndices.UK_COMEAPpm2p5(data.pm2p5_1hr); data.aqiPm2p5_3hr = AirQualityIndices.UK_COMEAPpm2p5(data.pm2p5_3hr); @@ -1654,7 +1659,7 @@ private void DoAqi(AirLinkData data) break; default: - cumulus.LogMessage($"DoAqi: Invalid AQI formula value set [cumulus.airQualityIndex]"); + cumulus.LogErrorMessage($"DoAqi: Invalid AQI formula value set [cumulus.airQualityIndex]"); break; } @@ -1711,17 +1716,17 @@ private class AlCurrentRec public double pm_2p5_nowcast { get; set; } - public double pm_10 { get; set; } // Type 6 - public double pm_10p0 { get; set; } // Type 5 + public double pm_10 { get; set; } // Type 6 + public double pm_10p0 { get; set; } // Type 5 public double pm_10_last { get; set; } - public double pm_10_last_1_hour { get; set; } // Type 6 - public double pm_10p0_last_1_hour { get; set; } // Type 5 - public double pm_10_last_3_hours { get; set; } // Type 6 + public double pm_10_last_1_hour { get; set; } // Type 6 + public double pm_10p0_last_1_hour { get; set; } // Type 5 + public double pm_10_last_3_hours { get; set; } // Type 6 public double pm_10p0_last_3_hours { get; set; } // Type 5 - public double pm_10_last_24_hours { get; set; } // Type 6 + public double pm_10_last_24_hours { get; set; } // Type 6 public double pm_10p0_last_24_hours { get; set; } // Type 5 - public double pm_10_nowcast { get; set; } // Type 6 - public double pm_10p0_nowcast { get; set; } // Type 5 + public double pm_10_nowcast { get; set; } // Type 6 + public double pm_10p0_nowcast { get; set; } // Type 5 public int pct_pm_data_last_1_hour { get; set; } public int pct_pm_data_last_3_hours { get; set; } diff --git a/CumulusMX/DavisCloudStation.cs b/CumulusMX/DavisCloudStation.cs new file mode 100644 index 00000000..532847af --- /dev/null +++ b/CumulusMX/DavisCloudStation.cs @@ -0,0 +1,3735 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Timers; + +using ServiceStack; + +using static System.Collections.Specialized.BitVector32; + +namespace CumulusMX +{ + internal class DavisCloudStation : WeatherStation + { + private readonly System.Timers.Timer tmrCurrent; + private readonly System.Timers.Timer tmrHealth; + //private bool savedUseSpeedForAvgCalc; + private bool savedCalculatePeakGust; + private int maxArchiveRuns = 1; + private int weatherLinkArchiveInterval = 16 * 60; // Used to get historic Health, 16 minutes in seconds only for initial fetch after load + private int wlStationArchiveInterval = 5; + private bool wlLastArchiveFetchOk; + private bool wllVoltageLow; + private readonly AutoResetEvent bwDoneEvent = new AutoResetEvent(false); + private List sensorList; + private bool startingUp = true; + private new readonly Random random = new Random(); + private DateTime lastRecordTime = DateTime.MinValue; + + public DavisCloudStation(Cumulus cumulus) : base(cumulus) + { + calculaterainrate = false; + //cumulus.UseDataLogger = false; + // WLL does not provide a forecast string, so use the Cumulus forecast + cumulus.UseCumulusForecast = true; + // WLL does not provide pressure trend strings + cumulus.StationOptions.UseCumulusPresstrendstr = true; + + noET = false; + // initialise the battery status + TxBatText = "1-NA 2-NA 3-NA 4-NA 5-NA 6-NA 7-NA 8-NA"; + + cumulus.LogMessage("Station type = Davis Cloud Station"); + + // Override the ServiceStack De-serialization function + // Check which format provided, attempt to parse as datetime or return minValue. + // Formats to use for the different date kinds + string utcTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fff'Z'"; + string localTimeFormat = "yyyy-MM-dd'T'HH:mm:ss"; + + ServiceStack.Text.JsConfig.DeSerializeFn = datetimeStr => + { + if (string.IsNullOrWhiteSpace(datetimeStr)) + { + return DateTime.MinValue; + } + + if (datetimeStr.EndsWith("Z") && + DateTime.TryParseExact(datetimeStr, utcTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime resultUtc)) + { + return resultUtc; + } + else if (!datetimeStr.EndsWith("Z") && + DateTime.TryParseExact(datetimeStr, localTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out DateTime resultLocal)) + { + return resultLocal; + } + + return DateTime.MinValue; + }; + + tmrCurrent = new System.Timers.Timer(); + tmrHealth = new System.Timers.Timer(); + + // The Davis leafwetness sensors send a decimal value via WLL (only integer available via VP2/Vue) + cumulus.LeafWetDPlaces = 1; + cumulus.LeafWetFormat = "F1"; + + CalcRecentMaxGust = false; + cumulus.StationOptions.CalcuateAverageWindSpeed = false; + cumulus.StationOptions.UseSpeedForAvgCalc = true; + cumulus.StationOptions.CalculatedDP = false; + cumulus.StationOptions.CalculatedWC = false; + + /* + // If the user is using the default 10 minute Wind gust, always use gust data from the WLL - simple + if (cumulus.StationOptions.PeakGustMinutes == 10) + { + CalcRecentMaxGust = true; + checkWllGustValues = true; + } + else if (cumulus.StationOptions.PeakGustMinutes > 10) + { + // If the user period is greater that 10 minutes, then Cumulus must calculate Gust values + // but we can check the WLL 10 min gust value in case we missed a gust + CalcRecentMaxGust = true; + checkWllGustValues = true; + } + else + { + // User period is less than 10 minutes + CalcRecentMaxGust = true; + checkWllGustValues = true; + } + */ + + + // Sanity check - do we have all the info we need? + if (string.IsNullOrEmpty(cumulus.WllApiKey) && string.IsNullOrEmpty(cumulus.WllApiSecret)) + { + // The basic API details have not been supplied + cumulus.LogWarningMessage("WLL - No WeatherLink.com API configuration supplied, just going to work locally"); + cumulus.LogMessage("WLL - Cannot start historic downloads or retrieve health data"); + cumulus.LogConsoleMessage("*** No WeatherLink.com API details supplied. Cannot start station", ConsoleColor.DarkCyan); + } + else if (string.IsNullOrEmpty(cumulus.WllApiKey) || string.IsNullOrEmpty(cumulus.WllApiSecret)) + { + // One of the API details is missing + if (string.IsNullOrEmpty(cumulus.WllApiKey)) + { + cumulus.LogWarningMessage("WLL - Missing WeatherLink.com API Key"); + cumulus.LogConsoleMessage("*** Missing WeatherLink.com API Key. Cannot start station", ConsoleColor.Yellow); + } + else + { + cumulus.LogWarningMessage("WLL - Missing WeatherLink.com API Secret"); + cumulus.LogConsoleMessage("*** Missing WeatherLink.com API Secret. Cannot start station", ConsoleColor.Yellow); + } + } + + // Get wl.com status + GetSystemStatus(); + + // Perform Station ID checks - If we have API details! + // If the Station ID is missing, this will populate it if the user only has one station associated with the API key + if (cumulus.WllStationId < 10) + { + var msg = "No WeatherLink API station ID in the cumulus.ini file"; + cumulus.LogWarningMessage(msg); + cumulus.LogConsoleMessage(msg); + + GetAvailableStationIds(true); + } + else + { + GetAvailableStationIds(false); + } + + // Sanity check the station id + if (cumulus.WllStationId < 10) + { + // API details supplied, but Station Id is still invalid - do not start the station up. + cumulus.LogErrorMessage("WLL - The WeatherLink.com API is enabled, but no Station Id has been configured, not starting the station. Please correct this and restart Cumulus"); + cumulus.LogConsoleMessage("The WeatherLink.com API is enabled, but no Station Id has been configured. Please correct this and restart Cumulus", ConsoleColor.Yellow); + return; + } + + + // Now get the sensors associated with this station + GetAvailableSensors(); + + DateTime tooOld = new DateTime(0); + + if ((cumulus.LastUpdateTime <= tooOld) || !cumulus.UseDataLogger) + { + // there's nothing in the database, so we haven't got a rain counter + // we can't load the history data, so we'll just have to go live + + timerStartNeeded = true; + LoadLastHoursFromDataLogs(cumulus.LastUpdateTime); + //StartLoop(); + DoDayResetIfNeeded(); + DoTrendValues(DateTime.Now); + + cumulus.LogMessage("Starting Davis WLL"); + StartLoop(); + } + else + { + // Read the data from the WL APIv2 + startReadingHistoryData(); + } + } + + public override void Start() + { + try + { + // Wait for the lock + //cumulus.LogDebugMessage("Lock: Station waiting for lock"); + Cumulus.syncInit.Wait(); + //cumulus.LogDebugMessage("Lock: Station has the lock"); + + // Create a current conditions thread to poll readings every 10 seconds as temperature updates every 10 seconds + //GetWlCurrent(null, null); + //tmrCurrent.Elapsed += GetWlCurrent; + GetWlLastArchive(null, null); + tmrCurrent.Elapsed += GetWlLastArchive; + tmrCurrent.Interval = 30 * 1000; // Every 30 seconds + tmrCurrent.AutoReset = true; + tmrCurrent.Start(); + + // Get the archive data health to do the initial value populations + GetWlHistoricHealth(); + // And reset the fetch interval to 2 minutes + weatherLinkArchiveInterval = 2 * 60; + + } + catch (ThreadAbortException) + { + } + finally + { + //cumulus.LogDebugMessage("Lock: Station releasing lock"); + Cumulus.syncInit.Release(); + } + } + + public override void Stop() + { + cumulus.LogMessage("Closing WLL connections"); + try + { + if (tmrCurrent != null) + tmrCurrent.Stop(); + if (tmrHealth != null) + tmrHealth.Stop(); + } + catch + { + cumulus.LogMessage("Error stopping station timers"); + } + + StopMinuteTimer(); + try + { + if (bw != null && bw.WorkerSupportsCancellation) + { + bw.CancelAsync(); + } + + bwDoneEvent.WaitOne(); + } + catch + { + cumulus.LogMessage("Error stopping station background tasks"); + } + } + + /* + private async void GetWlCurrent(object source, ElapsedEventArgs e) + { + // We only want to process at every archive interval, or on intial load, or if the last fetch was unsucessful + // Initial run, source will be null + // Possible intervals are 1, 5, or 30 minutes, fetch one minute after the archive time to allow for clock differences etc + var now = DateTime.Now; + + if ((source != null && wlLastArchiveFetchOk && ((now.Minute - 1) % wlStationArchiveInterval != 0)) || now.Second > 35) + { + cumulus.LogDebugMessage("GetWlCurrent: Skipping"); + return; + } + + wlLastArchiveFetchOk = false; + + //cumulus.LogMessage("GetWlCurrent: Get WL.com Current Data"); + cumulus.LogMessage("GetWlCurrent: Get WL.com last archive Data"); + + if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) + { + cumulus.LogWarningMessage("GetWlCurrent: Missing WeatherLink API data in the configuration, aborting!"); + cumulus.LastUpdateTime = DateTime.Now; + return; + } + + if (cumulus.WllStationId < 10) + { + const string msg = "No WeatherLink API station ID in the configuration"; + cumulus.LogWarningMessage(msg); + cumulus.LogConsoleMessage("GetWlCurrent: " + msg); + + } + + //cumulus.LogMessage($"GetWlCurrent: Downloading Current Data from weatherlink.com"); + cumulus.LogMessage($"GetWlCurrent: Downloading last archive Data from weatherlink.com"); + + StringBuilder currentUrl = new StringBuilder("https://api.weatherlink.com/v2/current/" + cumulus.WllStationId); + currentUrl.Append("?api-key=" + cumulus.WllApiKey); + + cumulus.LogDebugMessage($"WeatherLink URL = {currentUrl.ToString().Replace(cumulus.WllApiKey, "API_KEY")}"); + + WlCurrent currObj; + + try + { + string responseBody; + int responseCode; + + var request = new HttpRequestMessage(HttpMethod.Get, currentUrl.ToString()); + request.Headers.Add("X-Api-Secret", cumulus.WllApiSecret); + + // we want to do this synchronously, so .Result + using (var response = await Cumulus.MyHttpClient.SendAsync(request)) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"GetWlCurrent: WeatherLink API Current Response code: {responseCode}"); + cumulus.LogDataMessage($"GetWlCurrent: WeatherLink API Current Response: {responseBody}"); + } + + if (responseCode != 200) + { + var error = responseBody.FromJson(); + cumulus.LogErrorMessage($"GetWlCurrent: WeatherLink API Current Error: {error.code}, {error.message}"); + cumulus.LogConsoleMessage($" - Error {error.code}: {error.message}", ConsoleColor.Red); + return; + } + + currObj = responseBody.FromJson(); + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("GetWlCurrent: WeatherLink API Current: No data was returned. Check your Device Id."); + return; + } + else if (responseBody.StartsWith("{\"")) // basic sanity check + { + if (currObj.sensors.Count == 0) + { + cumulus.LogMessage("GetWlCurrent: No current data available"); + return; + } + else + { + cumulus.LogMessage($"GetWlCurrent: Found {currObj.sensors.Count} sensors to process"); + + DecodeCurrent(currObj.sensors); + + if (startingUp) + startingUp = false; + } + } + else // No idea what we got, dump it to the log + { + cumulus.LogErrorMessage("GetWlCurrent: Invalid current response received"); + cumulus.LogMessage("Response body = " + responseBody.ToString()); + return; + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage("GetWlCurrent: Exception: " + ex.Message); + if (ex.InnerException != null) + { + ex = Utils.GetOriginalException(ex); + cumulus.LogMessage($"GetWlCurrent: Base exception - {ex.Message}"); + } + } + + wlLastArchiveFetchOk = true; + + return; + } + */ + + private async void GetWlLastArchive(object source, ElapsedEventArgs e) + { + // We only want to process at every archive interval, or on intial load, or if the last fetch was unsucessful + // Initial run, source will be null + // Possible intervals are 1, 5, or 30 minutes, fetch one minute after the archive time to allow for clock differences etc + var now = DateTime.Now; + + if ((source != null && wlLastArchiveFetchOk && ((now.Minute - 1) % wlStationArchiveInterval != 0)) || now.Second > 35) + { + cumulus.LogDebugMessage("GetWlLastArchive: Skipping"); + return; + } + + wlLastArchiveFetchOk = false; + + cumulus.LogMessage("GetWlLastArchive: Get WL.com last archive Data"); + + if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) + { + cumulus.LogWarningMessage("GetWlLastArchive: Missing WeatherLink API data in the configuration, aborting!"); + cumulus.LastUpdateTime = DateTime.Now; + return; + } + + if (cumulus.WllStationId < 10) + { + const string msg = "No WeatherLink API station ID in the configuration"; + cumulus.LogWarningMessage(msg); + cumulus.LogConsoleMessage("GetWlLastArchive: " + msg); + + } + + var unixDateTime = Utils.ToUnixTime(DateTime.Now); + var startTime = unixDateTime - weatherLinkArchiveInterval; + long endTime = unixDateTime; + + cumulus.LogDebugMessage($"GetWlLastArchive: Downloading the historic record from WL.com from: {Utils.FromUnixTime(startTime):s} to: {Utils.FromUnixTime(endTime):s}"); + + StringBuilder historicUrl = new StringBuilder("https://api.weatherlink.com/v2/historic/" + cumulus.WllStationId); + historicUrl.Append("?api-key=" + cumulus.WllApiKey); + historicUrl.Append("&start-timestamp=" + startTime.ToString()); + historicUrl.Append("&end-timestamp=" + endTime.ToString()); + + cumulus.LogDebugMessage($"WeatherLink URL = {historicUrl.ToString().Replace(cumulus.WllApiKey, "API_KEY")}"); + + WlHistory histObj; + + try + { + string responseBody; + int responseCode; + + var request = new HttpRequestMessage(HttpMethod.Get, historicUrl.ToString()); + request.Headers.Add("X-Api-Secret", cumulus.WllApiSecret); + + // we want to do this synchronously, so .Result + using (var response = await Cumulus.MyHttpClient.SendAsync(request)) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"GetWlLastArchive: WeatherLink API Historic Response code: {responseCode}"); + cumulus.LogDataMessage($"GetWlLastArchive: WeatherLink API Historic Response: {responseBody}"); + } + + if (responseCode != 200) + { + var error = responseBody.FromJson(); + cumulus.LogWarningMessage($"GetWlLastArchive: WeatherLink API Historic Error: {error.code}, {error.message}"); + cumulus.LogConsoleMessage($" - Error {error.code}: {error.message}", ConsoleColor.Red); + return; + } + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("GetWlLastArchive: WeatherLink API Historic: No data was returned. Check your Device Id."); + return; + } + else if (responseBody.StartsWith("{\"")) // basic sanity check + { + histObj = responseBody.FromJson(); + + if (histObj.sensors.Count == 0) + { + cumulus.LogMessage("GetWlLastArchive: No historic data available"); + return; + } + else + { + cumulus.LogMessage($"GetWlLastArchive: Found {histObj.sensors.Count} sensors to process"); + + foreach (var sensor in histObj.sensors) + { + int sensorType = sensor.sensor_type; + int dataStructureType = sensor.data_structure_type; + + if (sensor.data.Count > 0) + { + DecodeHistoric(dataStructureType, sensorType, sensor.data[sensor.data.Count - 1], true); + } + } + + if (lastRecordTime != DateTime.MinValue) + { + // Now we have the primary data, calculate the derived data + if (cumulus.StationOptions.CalculatedWC) + { + // DoWindChill does all the required checks and conversions + DoWindChill(OutdoorTemperature, lastRecordTime); + } + + DoApparentTemp(lastRecordTime); + DoFeelsLike(lastRecordTime); + DoHumidex(lastRecordTime); + DoCloudBaseHeatIndex(lastRecordTime); + + DoForecast(string.Empty, false); + + UpdateStatusPanel(lastRecordTime); + UpdateMQTT(); + + // SensorContactLost = localSensorContactLost; + + cumulus.BatteryLowAlarm.Triggered = TxBatText.Contains("LOW"); + + cumulus.LogDebugMessage("WL current: Last data update = " + lastRecordTime.ToString("s")); + + + //// syncronise the timer to read just after the next update + //var time = (DateTime.Now - lastDataUpdate).TotalSeconds % 60; + //// get difference to 60 seconds and add 10 seconds to allow the servers to update + //// allow a drift of 5 seconds (10 + 5 = 15) + //if (time > 15) + //{ + // tmrCurrent.Interval = (70 - time) * 1000; + // tmrCurrent.Stop(); + // tmrCurrent.Start(); + // cumulus.LogMessage($"WL current: Amending fetch timimg by {(time):F1} seconds"); + //}; + + } + + if (startingUp) + startingUp = false; + } + } + else // No idea what we got, dump it to the log + { + cumulus.LogErrorMessage("GetWlLastArchive: Invalid current response received"); + cumulus.LogMessage("Response body = " + responseBody.ToString()); + return; + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage("GetWlLastArchive: Exception: " + ex.Message); + if (ex.InnerException != null) + { + ex = Utils.GetOriginalException(ex); + cumulus.LogMessage($"GetWlLastArchive: Base exception - {ex.Message}"); + } + } + + wlLastArchiveFetchOk = true; + + return; + } + + /* + private void DecodeCurrent(List sensors) + { + try + { + var timestamp = DateTime.MinValue; + var lastDataUpdate = DateTime.MinValue; + var localSensorContactLost = false; + + foreach (var sensor in sensors) + { + switch (sensor.data_structure_type) + { + case 1: // VP2 ISS current record type A + case 2: // VP2 ISS record type B + { + cumulus.LogDebugMessage("WL current: Processing VP ISS Current Data"); + + // For some reason the API returns the data as an array, even though there is only one set of values? + var data = sensor.data[0].FromJsv(); + + timestamp = Utils.FromUnixTime(data.ts); + if (timestamp > lastDataUpdate) + lastDataUpdate = timestamp; + + // No battery info in VP2 records + // No RX state in VP2 records + + // Temperature & Humidity + //Available fields + // *"temp_out": 62.7, // most recent valid temperature **(°F)** + // *"hum_out":1.1, // most recent valid humidity **(%RH)** + // *"dew_point": -0.3, // **(°F)** + // *"heat_index": 5.5, // **(°F)** + // *"wind_chill": 6.0, // **(°F)** + // *"temp_in": 70.4, // **(°F)** + // *"hum_in": 45, // **(%RH)** + + + try + { + cumulus.LogDebugMessage($"WL current: using temp/hum data from TxId {data.tx_id}"); + if (data.hum_out.HasValue) + DoOutdoorHumidity(Convert.ToInt32(data.hum_out.Value), timestamp); + + if (data.temp_out.HasValue) + DoOutdoorTemp(ConvertTempFToUser(data.temp_out.Value), timestamp); + + if (data.dew_point.HasValue) + DoOutdoorDewpoint(ConvertTempFToUser(data.dew_point.Value), timestamp); + + if (data.wind_chill.HasValue) + { + // use wind chill from WLL + DoWindChill(ConvertTempFToUser(data.wind_chill.Value), timestamp); + } + //TODO: Wet Bulb? rec["wet_bulb"] - No, we already have humidity + //TODO: Heat Index? rec["heat_index"] - No, Cumulus always calculates HI + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing temperature values on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + + // Extra temperature/humidity - only 7 in VP records + try + { + if (data.temp_extra_1.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_1.Value), 1); + if (data.temp_extra_2.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_2.Value), 2); + if (data.temp_extra_3.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_3.Value), 3); + if (data.temp_extra_4.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_4.Value), 4); + if (data.temp_extra_5.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_5.Value), 5); + if (data.temp_extra_6.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_6.Value), 6); + if (data.temp_extra_7.HasValue) + DoExtraTemp(ConvertTempFToUser(data.temp_extra_7.Value), 7); + + if (data.hum_extra_1.HasValue) + DoExtraHum(data.hum_extra_1.Value, 1); + if (data.hum_extra_2.HasValue) + DoExtraHum(data.hum_extra_2.Value, 2); + if (data.hum_extra_3.HasValue) + DoExtraHum(data.hum_extra_3.Value, 3); + if (data.hum_extra_4.HasValue) + DoExtraHum(data.hum_extra_4.Value, 4); + if (data.hum_extra_5.HasValue) + DoExtraHum(data.hum_extra_5.Value, 5); + if (data.hum_extra_6.HasValue) + DoExtraHum(data.hum_extra_6.Value, 6); + if (data.hum_extra_7.HasValue) + DoExtraHum(data.hum_extra_7.Value, 7); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing Extra temperature/humidity values on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + + + // wind + try + { + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + var gust = ConvertWindMPHToUser(data.wind_gust_10_min ?? 0); + var avg = ConvertWindMPHToUser(data.wind_speed_10_min_avg ?? 0); + double wind = ConvertWindMPHToUser(data.wind_speed ?? 0); + int wdir = data.wind_dir ?? 0; + + DoWind(gust, wdir, avg, timestamp); + + RecentMaxGust = cumulus.Calib.WindGust.Calibrate(gust); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing wind speeds on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + + + // Rainfall + cumulus.LogDebugMessage($"WL current: Using rain data from TxId {data.tx_id}"); + + if (!data.rain_year_clicks.HasValue || !data.rain_rate_clicks.HasValue) + { + cumulus.LogDebugMessage("WL current: No rain values present!"); + } + else + { + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + // double check that the rainfall isn't out of date so we double count when it catches up + var rain = ConvertRainClicksToUser(data.rain_year_clicks.Value, cumulus.DavisOptions.RainGaugeType); + var rainrate = ConvertRainClicksToUser(data.rain_rate_clicks.Value, cumulus.DavisOptions.RainGaugeType); + + if (rain > 0 && rain < Raincounter) + { + cumulus.LogDebugMessage("WL current: The current yearly rainfall value is less than the value we had previously, ignoring it to avoid double counting"); + } + else + { + DoRain(rain, rainrate, timestamp); + } + } + + if (!data.rain_storm_clicks.HasValue) + { + cumulus.LogDebugMessage("WL current: No rain storm values present"); + } + else + { + try + { + StormRain = ConvertRainClicksToUser(data.rain_storm_clicks.Value, cumulus.DavisOptions.RainGaugeType) * cumulus.Calib.Rain.Mult; + if (data.rain_storm_start_date.HasValue) + StartOfStorm = Utils.FromUnixTime(data.rain_storm_start_date.Value); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing rain storm values on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + + + // UV + if (data.uv.HasValue) + { + try + { + cumulus.LogDebugMessage($"WL current: using UV data from TxId {data.tx_id}"); + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + DoUV(data.uv.Value, timestamp); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing UV value on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + + + // Solar + if (data.solar_rad.HasValue) + { + try + { + cumulus.LogDebugMessage($"WL current: using solar data from TxId {data.tx_id}"); + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + DoSolarRad(data.solar_rad.Value, timestamp); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing Solar value on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + + } + break; + + case 10: // WeatherLink Live ISS current record + case 23: // WeatherLink Console ISS current record + { + // For some reason the API returns the data as an array, even though there is only one set of values? + var data = sensor.data[0].FromJsv(); + + timestamp = Utils.FromUnixTime(data.ts); + if (timestamp > lastDataUpdate) + lastDataUpdate = timestamp; + + // need to look up lsid to get the transmitter id + //var tx_id = sensorList.Where(item => item.lsid == sensor.lsid).FirstOrDefault().tx_id.Value; + + cumulus.LogDebugMessage($"WL current: found ISS data on TxId {data.tx_id}"); + + // Battery + if (data.trans_battery_flag.HasValue) + SetTxBatteryStatus(data.tx_id, data.trans_battery_flag.Value); + + if (data.rx_state == 2) + { + localSensorContactLost = true; + cumulus.LogWarningMessage($"WL current: Warning: Sensor contact lost TxId {data.tx_id}; ignoring data from this ISS"); + continue; + } + + + // Temperature & Humidity + if (cumulus.WllPrimaryTempHum == data.tx_id) + { + //Available fields + //* "temp": 62.7, // most recent valid temperature **(°F)** + //* "hum":1.1, // most recent valid humidity **(%RH)** + //* "dew_point": -0.3, // **(°F)** + //* "wet_bulb":null, // **(°F)** + //* "heat_index": 5.5, // **(°F)** + //* "wind_chill": 6.0, // **(°F)** + //* "thw_index": 5.5, // **(°F)** + //* "thsw_index": 5.5, // **(°F)** + + + try + { + cumulus.LogDebugMessage($"WL current: using temp/hum data from TxId {data.tx_id}"); + if (data.hum.HasValue) + DoOutdoorHumidity(Convert.ToInt32(data.hum.Value), timestamp); + + if (data.temp.HasValue) + DoOutdoorTemp(ConvertTempFToUser(data.temp.Value), timestamp); + + if (data.dew_point.HasValue) + DoOutdoorDewpoint(ConvertTempFToUser(data.dew_point.Value), timestamp); + + if (data.wind_chill.HasValue) + { + // use wind chill from WLL + DoWindChill(ConvertTempFToUser(data.wind_chill.Value), timestamp); + } + + if (data.thsw_index.HasValue) + { + THSWIndex = ConvertTempFToUser(data.thsw_index.Value); + } + + //TODO: Wet Bulb? rec["wet_bulb"] - No, we already have humidity + //TODO: Heat Index? rec["heat_index"] - No, Cumulus always calculates HI + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing temperature values on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + else + { // Check for Extra temperature/humidity settings + for (var tempTxId = 1; tempTxId <= 8; tempTxId++) + { + if (cumulus.WllExtraTempTx[tempTxId] != data.tx_id) continue; + + try + { + if (cumulus.WllExtraTempTx[tempTxId] == data.tx_id) + { + if (!data.temp.HasValue || data.temp.Value == -99) + { + cumulus.LogDebugMessage($"WL current: no valid Extra temperature value found [{data.temp}] on TxId {data.tx_id}"); + } + else + { + cumulus.LogDebugMessage($"WL current: using extra temp data from TxId {data.tx_id}"); + + DoExtraTemp(ConvertTempFToUser(data.temp.Value), tempTxId); + } + + if (cumulus.WllExtraHumTx[tempTxId] && data.hum.HasValue) + { + DoExtraHum(data.hum.Value, tempTxId); + } + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing Extra temperature/humidity values on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + } + + // Wind + if (cumulus.WllPrimaryWind == data.tx_id) + { + + //* Available fields + //* "wind_speed_last":2, // most recent valid wind speed **(mph)** + //* "wind_dir_last":null, // most recent valid wind direction **(°degree)** + //* "wind_speed_avg_last_1_min":4 // average wind speed over last 1 min **(mph)** + //* "wind_dir_scalar_avg_last_1_min":15 // scalar average wind direction over last 1 min **(°degree)** + //* "wind_speed_avg_last_2_min":42606, // average wind speed over last 2 min **(mph)** + //* "wind_dir_scalar_avg_last_2_min": 170.7, // scalar average wind direction over last 2 min **(°degree)** + //* "wind_speed_hi_last_2_min":8, // maximum wind speed over last 2 min **(mph)** + //* "wind_dir_at_hi_speed_last_2_min":0.0, // gust wind direction over last 2 min **(°degree)** + //* "wind_speed_avg_last_10_min":42606, // average wind speed over last 10 min **(mph)** + //* "wind_dir_scalar_avg_last_10_min": 4822.5, // scalar average wind direction over last 10 min **(°degree)** + //* "wind_speed_hi_last_10_min":8, // maximum wind speed over last 10 min **(mph)** + //* "wind_dir_at_hi_speed_last_10_min":0.0, // gust wind direction over last 10 min **(°degree)** + + try + { + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + var gust = ConvertWindMPHToUser(data.wind_speed_hi_last_10_min ?? 0); + var avg = ConvertWindMPHToUser(data.wind_speed_avg_last_10_min ?? 0); + var last = ConvertWindMPHToUser(data.wind_speed_last ?? 0); + + // pesky null values from WLL when it is calm + int wdir = data.wind_dir_scalar_avg_last_1_min ?? 0; + double wind = ConvertWindMPHToUser(data.wind_speed_last ?? 0); + + DoWind(last, wdir, avg, timestamp); + + RecentMaxGust = cumulus.Calib.WindGust.Calibrate(gust); + + + + //if (cumulus.StationOptions.PeakGustMinutes >= 2) + //{ + // var gust = ConvertWindMPHToUser(data.wind_speed_hi_last_2_min ?? 0); + // var gustCal = cumulus.Calib.WindGust.Calibrate(gust); + // var gustDir = data.wind_dir_at_hi_speed_last_2_min ?? 0; + // var gustDirCal = gustDir == 0 ? 0 : (int) cumulus.Calib.WindDir.Calibrate(gustDir); + + // // See if the current speed is higher than the current max + // // We can then update the figure before the next data packet is read + + // cumulus.LogDebugMessage($"WL current: Checking recent gust using wind data from TxId {data.tx_id}"); + + // if (gustCal > HiLoToday.HighGust) + // { + // // Check for spikes, and set highs + // if (CheckHighGust(gustCal, gustDirCal, timestamp)) + // { + // cumulus.LogDebugMessage("Setting max gust from current value: " + gustCal.ToString(cumulus.WindFormat) + " was: " + RecentMaxGust.ToString(cumulus.WindFormat)); + // RecentMaxGust = gustCal; + // } + // } + //} + + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing wind speeds on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + + + // Rainfall + if (cumulus.WllPrimaryRain == data.tx_id) + { + + //* Available fields: + //* rec["rain_size"] - 0: Reserved, 1: 0.01", 2: 0.2mm, 3: 0.1mm, 4: 0.001" + //* rec["rain_rate_last"], rec["rain_rate_hi"] + //* rec["rainfall_last_15_min"], rec["rain_rate_hi_last_15_min"] + //* rec["rainfall_last_60_min"] + //* rec["rainfall_last_24_hr"] + //* rec["rainfall_daily"] + //* rec["rainfall_monthly"] + //* rec["rainfall_year"] + //* rec["rain_storm"], rec["rain_storm_start_at"] + //* rec["rain_storm_last"], rec["rain_storm_last_start_at"], rec["rain_storm_last_end_at"] + + + + cumulus.LogDebugMessage($"WL current: using storm rain data from TxId {data.tx_id}"); + + if (data.rain_size.HasValue) + { + switch (data.rain_size.Value) + { + case 1: + if (cumulus.DavisOptions.RainGaugeType != 1) + { + cumulus.LogMessage($"Setting Davis rain tipper size - was {cumulus.DavisOptions.RainGaugeType}, now 1 = 0.01 in"); + cumulus.DavisOptions.RainGaugeType = 1; + cumulus.WriteIniFile(); + } + break; + case 2: + if (cumulus.DavisOptions.RainGaugeType != 0) + { + cumulus.LogMessage($"Setting Davis rain tipper size - was {cumulus.DavisOptions.RainGaugeType}, now 0 = 0.2 mm"); + cumulus.DavisOptions.RainGaugeType = 0; + cumulus.WriteIniFile(); + } + break; + case 3: + if (cumulus.DavisOptions.RainGaugeType != 2) + { + cumulus.LogMessage($"Setting Davis rain tipper size - was {cumulus.DavisOptions.RainGaugeType}, now 2 = 0.1 mm"); + cumulus.DavisOptions.RainGaugeType = 2; + cumulus.WriteIniFile(); + } + break; + case 4: + if (cumulus.DavisOptions.RainGaugeType != 3) + { + cumulus.LogMessage($"Setting Davis rain tipper size - was {cumulus.DavisOptions.RainGaugeType}, now 1 = 0.001 in"); + cumulus.DavisOptions.RainGaugeType = 1; + cumulus.WriteIniFile(); + } + break; + + default: + cumulus.LogErrorMessage($"Error: Unknown Davis rain tipper size defined in data = {data.rain_size.Value}"); + break; + } + } + + // Rain data can be a bit out of date compared to the broadcasts (1 minute update), so only use storm data unless we are not receiving broadcasts + + cumulus.LogDebugMessage($"WL current: Using rain data from TxId {data.tx_id}"); + + if (!data.rainfall_year_clicks.HasValue || !data.rain_rate_last_clicks.HasValue || !data.rain_size.HasValue) + { + cumulus.LogDebugMessage("WL current: No rain values present!"); + } + else + { + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + // double check that the rainfall isn't out of date so we double count when it catches up + var rain = ConvertRainClicksToUser(data.rainfall_year_clicks.Value, data.rain_size.Value); + var rainrate = ConvertRainClicksToUser(data.rain_rate_last_clicks.Value, data.rain_size.Value); + + if (rain > 0 && rain < Raincounter) + { + cumulus.LogDebugMessage("WL current: The current yearly rainfall value is less than the value we had previously, ignoring it to avoid double counting"); + } + else + { + DoRain(rain, rainrate, timestamp); + } + } + + if (!data.rain_storm_clicks.HasValue || !data.rain_size.HasValue) + { + cumulus.LogDebugMessage("WL current: No rain storm values present"); + } + else + { + try + { + StormRain = ConvertRainClicksToUser(data.rain_storm_clicks.Value, data.rain_size.Value) * cumulus.Calib.Rain.Mult; + if (data.rain_storm_start_at.HasValue) + StartOfStorm = Utils.FromUnixTime(data.rain_storm_start_at.Value); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing rain storm values on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + } + + if (cumulus.WllPrimaryUV == data.tx_id && data.uv_index.HasValue) + { + try + { + cumulus.LogDebugMessage($"WL current: using UV data from TxId {data.tx_id}"); + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + DoUV(data.uv_index.Value, timestamp); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing UV value on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + + if (cumulus.WllPrimarySolar == data.tx_id && data.solar_rad.HasValue) + { + try + { + cumulus.LogDebugMessage($"WL current: using solar data from TxId {data.tx_id}"); + if (timestamp == DateTime.MinValue) + timestamp = Utils.FromUnixTime(data.ts); + + DoSolarRad(data.solar_rad.Value, timestamp); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing Solar value on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + } + break; + + case 12: // WeatherLink Live - Non-ISS & Baro + case 19: // WeatherLink Console - Baro + case 25: // WeatherLink Console - Non-ISS + case 21: // WeatherLink Console - Inside temp + // test if ISS or other data + switch (sensor.sensor_type) + { + case 242: + // Barometer data + cumulus.LogDebugMessage("WL current: found Baro data"); + + try + { + // For some reason the API returns the data as an array, even though there is only one set of values? + var data3 = sensor.data[0].FromJsv(); + + if (data3.bar_sea_level.HasValue) + { + DoPressure(ConvertPressINHGToUser(data3.bar_sea_level.Value), Utils.FromUnixTime(data3.ts)); + } + // Altimeter from absolute + if (data3.bar_absolute.HasValue) + { + StationPressure = ConvertPressINHGToUser(data3.bar_absolute.Value); + // Or do we use calibration? The VP2 code doesn't? + //StationPressure = ConvertPressINHGToUser(rec.Value("bar_absolute")) * cumulus.Calib.Press.Mult + cumulus.Calib.Press.Offset; + AltimeterPressure = ConvertPressMBToUser(StationToAltimeter(ConvertUserPressureToHPa(StationPressure), AltitudeM(cumulus.Altitude))); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage("WL current: Error processing baro data"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + break; + + case 243: // WLL + case 365: // WLC + // WeatherLink Live Inside temp data + cumulus.LogDebugMessage("WL current: found Indoor temp/hum data"); + + // For some reason the API returns the data as an array, even though there is only one set of values? + var data4 = sensor.data[0].FromJsv(); + + try + { + if (data4.temp_in.HasValue) + DoIndoorTemp(ConvertTempFToUser(data4.temp_in.Value)); + } + catch (Exception ex) + { + cumulus.LogErrorMessage("WL current: Error processing indoor temp data"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + + try + { + if (data4.hum_in.HasValue) + DoIndoorHumidity(Convert.ToInt32(data4.hum_in.Value)); + } + catch (Exception ex) + { + cumulus.LogErrorMessage("WL current: Error processing indoor humidity data"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + break; + + case 104: // Leaf/soil + + //* Available fields + //* "temp_1":null, // most recent valid soil temp slot 1 **(°F)** + //* "temp_2":null, // most recent valid soil temp slot 2 **(°F)** + //* "temp_3":null, // most recent valid soil temp slot 3 **(°F)** + //* "temp_4":null, // most recent valid soil temp slot 4 **(°F)** + //* "moist_soil_1":null, // most recent valid soil moisture slot 1 **(|cb|)** + //* "moist_soil_2":null, // most recent valid soil moisture slot 2 **(|cb|)** + //* "moist_soil_3":null, // most recent valid soil moisture slot 3 **(|cb|)** + //* "moist_soil_4":null, // most recent valid soil moisture slot 4 **(|cb|)** + //* "wet_leaf_1":null, // most recent valid leaf wetness slot 1 **(no unit)** + //* "wet_leaf_2":null, // most recent valid leaf wetness slot 2 **(no unit)** + //* + //* "rx_state":null, // configured radio receiver state **(no unit)** + //* "trans_battery_flag":null // transmitter battery status flag **(no unit)** + + + string idx = string.Empty; + // For some reason the API returns the data as an array, even though there is only one set of values? + var data = sensor.data[0].FromJsv(); + + // need to look up lsid to get the transmitter id + var tx_id = sensorList.Where(item => item.lsid == sensor.lsid).FirstOrDefault().tx_id.Value; + + cumulus.LogDebugMessage($"WL current: found Leaf/Soil data on TxId {tx_id}"); + + // Battery + if (data.trans_battery_flag.HasValue) + SetTxBatteryStatus(tx_id, data.trans_battery_flag.Value); + + if (data.rx_state == 2) + { + localSensorContactLost = true; + cumulus.LogWarningMessage($"Warning: Sensor contact lost TxId {tx_id}; ignoring data from this Leaf/Soil transmitter"); + continue; + } + + // For leaf wetness, soil temp/moisture we rely on user configuration, trap any errors + + // Leaf wetness + try + { + if (cumulus.WllExtraLeafTx1 == tx_id) + { + idx = "wet_leaf_" + cumulus.WllExtraLeafIdx1; + var val = (double?) data[idx]; + if (val.HasValue) + DoLeafWetness(val.Value, 1); + } + if (cumulus.WllExtraLeafTx2 == tx_id) + { + idx = "wet_leaf_" + cumulus.WllExtraLeafIdx2; + var val = (double?) data[idx]; + if (val.HasValue) + DoLeafWetness(val.Value, 2); + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"WL current: Error processing LeafWetness txid={tx_id}, idx={idx}"); + cumulus.LogDebugMessage($"LL current: Exception: {e.Message}"); + } + + // Soil moisture + if (cumulus.WllExtraSoilMoistureTx1 == tx_id) + { + idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx1; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilMoisture(val.Value, 1); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx1} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + if (cumulus.WllExtraSoilMoistureTx2 == tx_id) + { + idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx2; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilMoisture(val.Value, 2); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx2} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + if (cumulus.WllExtraSoilMoistureTx3 == tx_id) + { + idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx3; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilMoisture(val.Value, 3); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx3} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + if (cumulus.WllExtraSoilMoistureTx4 == tx_id) + { + idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx4; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilMoisture(val.Value, 4); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx4} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + + // SoilTemperature + if (cumulus.WllExtraSoilTempTx1 == tx_id) + { + idx = "temp_" + cumulus.WllExtraSoilTempIdx1; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilTemp(ConvertTempFToUser(val.Value), 1); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx1} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + if (cumulus.WllExtraSoilTempTx2 == tx_id) + { + idx = "temp_" + cumulus.WllExtraSoilTempIdx2; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilTemp(ConvertTempFToUser(val.Value), 2); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx2} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + if (cumulus.WllExtraSoilTempTx3 == tx_id) + { + idx = "temp_" + cumulus.WllExtraSoilTempIdx3; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilTemp(ConvertTempFToUser(val.Value), 3); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx3} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + if (cumulus.WllExtraSoilTempTx4 == tx_id) + { + idx = "temp_" + cumulus.WllExtraSoilTempIdx4; + try + { + var val = (double?) data[idx]; + if (val.HasValue) + DoSoilTemp(ConvertTempFToUser(val.Value), 4); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx4} on TxId {tx_id}"); + cumulus.LogDebugMessage($"WL current: Exception: {ex.Message}"); + } + } + break; + + // TODO: Extra Humidity? No type for this on WLL + + default: + cumulus.LogWarningMessage($"WL current: Error. Uknown data type found. Sensor Type={sensor.sensor_type}, Data Structure={sensor.data_structure_type}"); + break; + + } + break; + + case 15: // WeatherLink Live Health Data + { + var data = sensor.data[0].FromJsv(); + DecodeWllHealth(data, startingUp); + } + break; + + case 16: // AirLink current + cumulus.LogMessage("WL current: TODO - add AirLink Current Data processing"); + break; + + case 18: // Airlink Health + cumulus.LogMessage("WL current: TODO - add AirLink Health Data processing"); + break; + + case 27: // WeatherLink Console health + cumulus.LogMessage("WL current: TODO - add WeatherLink Console Health Data processing"); + break; + + + default: + cumulus.LogWarningMessage($"WL current: Error. Uknown data type found. Sensor Type={sensor.sensor_type}, Data Structure={sensor.data_structure_type}"); + break; + } + } + + // 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, timestamp); + } + else + { + // calculate wind chill from calibrated C temp and calibrated wind in KPH + DoWindChill(ConvertTempCToUser(MeteoLib.WindChill(ConvertUserTempToC(OutdoorTemperature), ConvertUserWindToKPH(WindAverage))), timestamp); + } + } + + DoApparentTemp(timestamp); + DoFeelsLike(timestamp); + DoHumidex(timestamp); + DoCloudBaseHeatIndex(timestamp); + + DoForecast(string.Empty, false); + + UpdateStatusPanel(timestamp); + UpdateMQTT(); + + SensorContactLost = localSensorContactLost; + + cumulus.BatteryLowAlarm.Triggered = TxBatText.Contains("LOW"); + + cumulus.LogDebugMessage("WL current: Last data update = " + lastDataUpdate.ToString("s")); + + + //// syncronise the timer to read just after the next update + //var time = (DateTime.Now - lastDataUpdate).TotalSeconds % 60; + //// get difference to 60 seconds and add 10 seconds to allow the servers to update + //// allow a drift of 5 seconds (10 + 5 = 15) + //if (time > 15) + //{ + // tmrCurrent.Interval = (70 - time) * 1000; + // tmrCurrent.Stop(); + // tmrCurrent.Start(); + // cumulus.LogMessage($"WL current: Amending fetch timimg by {(time):F1} seconds"); + //}; + + } + catch (Exception exp) + { + cumulus.LogDebugMessage("DecodeCurrent: Exception Caught!"); + cumulus.LogDebugMessage("Message :" + exp.Message); + } + } + */ + + private double ConvertRainClicksToUser(double clicks, int size) + { + // 0: Reserved, 1: 0.01", 2: 0.2mm, 3: 0.1mm, 4: 0.001" + switch (size) + { + case 1: + return ConvertRainINToUser(clicks * 0.01); + case 2: + return ConvertRainMMToUser(clicks * 0.2); + case 3: + return ConvertRainMMToUser(clicks * 0.1); + case 4: + return ConvertRainINToUser(clicks * 0.001); + default: + switch (cumulus.DavisOptions.RainGaugeType) + { + // Hmm, no valid tip size from WLL... + // One click is normally either 0.01 inches or 0.2 mm + // Try the setting in Cumulus.ini + // Rain gauge type not configured, assume from units + case -1 when cumulus.Units.Rain == 0: + return clicks * 0.2; + case -1: + return clicks * 0.01; + // Rain gauge is metric, convert to user unit + case 0: + return ConvertRainMMToUser(clicks * 0.2); + default: + return ConvertRainINToUser(clicks * 0.01); + } + } + } + + private void SetTxBatteryStatus(int txId, int status) + { + // Split the string + var delimiters = new[] { ' ', '-' }; + var sl = TxBatText.Split(delimiters); + + TxBatText = ""; + for (var i = 1; i <= 8; i++) + { + TxBatText += i; + if (i == txId) + { + TxBatText += (status == 0 ? "-OK " : "-LOW "); + } + else + { + TxBatText += "-" + sl[(i - 1) * 2 + 1] + " "; + } + } + TxBatText = TxBatText.Trim(); + } + + public override void startReadingHistoryData() + { + cumulus.LogMessage("WLL history: Reading history data from log files"); + LoadLastHoursFromDataLogs(cumulus.LastUpdateTime); + + cumulus.LogMessage("WLL history: Reading archive data from WeatherLink API"); + bw = new BackgroundWorker { WorkerSupportsCancellation = true }; + bw.DoWork += bw_ReadHistory; + bw.RunWorkerCompleted += bw_ReadHistoryCompleted; + bw.WorkerReportsProgress = true; + bw.RunWorkerAsync(); + } + + private void bw_ReadHistoryCompleted(object sender, RunWorkerCompletedEventArgs e) + { + cumulus.LogMessage("WLL history: WeatherLink API archive reading thread completed"); + if (e.Error != null) + { + cumulus.LogErrorMessage("WLL history: Archive reading thread apparently terminated with an error: " + e.Error.Message); + } + cumulus.LogMessage("WLL history: Updating highs and lows"); + //using (cumulusEntities dataContext = new cumulusEntities()) + //{ + // UpdateHighsAndLows(dataContext); + //} + cumulus.NormalRunning = true; + + CalcRecentMaxGust = savedCalculatePeakGust; + + StartLoop(); + DoDayResetIfNeeded(); + DoTrendValues(DateTime.Now); + cumulus.StartTimersAndSensors(); + } + + private void bw_ReadHistory(object sender, DoWorkEventArgs e) + { + BackgroundWorker worker = sender as BackgroundWorker; + + int archiveRun = 0; + //cumulus.LogDebugMessage("Lock: Station waiting for the lock"); + Cumulus.syncInit.Wait(); + //cumulus.LogDebugMessage("Lock: Station has the lock"); + + try + { + // set this temporarily, so speed is done from average and not peak gust from logger + cumulus.StationOptions.UseSpeedForAvgCalc = true; + + // same for gust values + savedCalculatePeakGust = CalcRecentMaxGust; + CalcRecentMaxGust = true; + + do + { + GetWlHistoricData(worker); + archiveRun++; + } while (archiveRun < maxArchiveRuns && worker.CancellationPending == false); + + // restore the setting + cumulus.StationOptions.UseSpeedForAvgCalc = false; + } + catch (Exception ex) + { + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); + } + + // force a calculation of the current gust and average wind speed so we do not get a zero values at startup + + + //cumulus.LogDebugMessage("Lock: Station releasing the lock"); + Cumulus.syncInit.Release(); + bwDoneEvent.Set(); + } + + private void GetWlHistoricData(BackgroundWorker worker) + { + cumulus.LogMessage("GetWlHistoricData: Get WL.com Historic Data"); + + if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) + { + cumulus.LogWarningMessage("GetWlHistoricData: Missing WeatherLink API data in the configuration, aborting!"); + cumulus.LastUpdateTime = DateTime.Now; + return; + } + + if (cumulus.WllStationId < 10) + { + const string msg = "No WeatherLink API station ID in the configuration"; + cumulus.LogWarningMessage(msg); + cumulus.LogConsoleMessage("GetWlHistoricData: " + msg); + + } + + var unixDateTime = Utils.ToUnixTime(DateTime.Now); + var startTime = Utils.ToUnixTime(cumulus.LastUpdateTime); + long endTime = unixDateTime; + int unix24hrs = 24 * 60 * 60; + + // The API call is limited to fetching 24 hours of data + if (unixDateTime - startTime > unix24hrs) + { + // only fetch 24 hours worth of data, and schedule another run to fetch the rest + endTime = startTime + unix24hrs; + maxArchiveRuns++; + } + + cumulus.LogConsoleMessage($"Downloading Historic Data from WL.com from: {cumulus.LastUpdateTime:s} to: {Utils.FromUnixTime(endTime):s}"); + cumulus.LogMessage($"GetWlHistoricData: Downloading Historic Data from WL.com from: {cumulus.LastUpdateTime:s} to: {Utils.FromUnixTime(endTime):s}"); + + StringBuilder historicUrl = new StringBuilder("https://api.weatherlink.com/v2/historic/" + cumulus.WllStationId); + historicUrl.Append("?api-key=" + cumulus.WllApiKey); + historicUrl.Append("&start-timestamp=" + startTime.ToString()); + historicUrl.Append("&end-timestamp=" + endTime.ToString()); + + cumulus.LogDebugMessage($"WeatherLink URL = {historicUrl.ToString().Replace(cumulus.WllApiKey, "API_KEY")}"); + + lastDataReadTime = cumulus.LastUpdateTime; + int luhour = lastDataReadTime.Hour; + + int rollHour = Math.Abs(cumulus.GetHourInc()); + + cumulus.LogMessage($"Roll over hour = {rollHour}"); + + bool rolloverdone = luhour == rollHour; + + bool midnightraindone = luhour == 0; + + WlHistory histObj; + int noOfRecs = 0; + WlHistorySensor sensorWithMostRecs; + + try + { + string responseBody; + int responseCode; + + var request = new HttpRequestMessage(HttpMethod.Get, historicUrl.ToString()); + request.Headers.Add("X-Api-Secret", cumulus.WllApiSecret); + + // we want to do this synchronously, so .Result + using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"GetWlHistoricData: WeatherLink API Historic Response code: {responseCode}"); + cumulus.LogDataMessage($"GetWlHistoricData: WeatherLink API Historic Response: {responseBody}"); + } + + if (responseCode != 200) + { + var historyError = responseBody.FromJson(); + cumulus.LogErrorMessage($"GetWlHistoricData: WeatherLink API Historic Error: {historyError.code}, {historyError.message}"); + cumulus.LogConsoleMessage($" - Error {historyError.code}: {historyError.message}", ConsoleColor.Red); + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); + return; + } + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("GetWlHistoricData: WeatherLink API Historic: No data was returned. Check your Device Id."); + cumulus.LogConsoleMessage(" - No historic data available"); + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); + return; + } + else if (responseBody.StartsWith("{\"")) // basic sanity check + { + histObj = responseBody.FromJson(); + + // get the sensor data + int idxOfSensorWithMostRecs = 0; + for (var i = 0; i < histObj.sensors.Count; i++) + { + // Find the WLL baro, or internal temp/hum sensors + if (histObj.sensors[i].sensor_type == 242 && histObj.sensors[i].data_structure_type == 13) + { + var recs = histObj.sensors[i].data.Count; + if (recs > noOfRecs) + { + noOfRecs = recs; + idxOfSensorWithMostRecs = i; + } + } + } + sensorWithMostRecs = histObj.sensors[idxOfSensorWithMostRecs]; + + if (noOfRecs == 0) + { + cumulus.LogMessage("GetWlHistoricData: No historic data available"); + cumulus.LogConsoleMessage(" - No historic data available"); + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); + return; + } + else + { + cumulus.LogMessage($"GetWlHistoricData: Found {noOfRecs} historic records to process"); + } + } + else // No idea what we got, dump it to the log + { + cumulus.LogErrorMessage("GetWlHistoricData: Invalid historic message received"); + cumulus.LogMessage("GetWlHistoricData: Received: " + responseBody); + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); + return; + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage("GetWlHistoricData: Exception: " + ex.Message); + if (ex.InnerException != null) + { + ex = Utils.GetOriginalException(ex); + cumulus.LogMessage($"GetWlHistoricData: Base exception - {ex.Message}"); + } + + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); + return; + } + + for (int dataIndex = 0; dataIndex < noOfRecs; dataIndex++) + { + if (worker.CancellationPending == true) + return; + + try + { + // Not all sensors may have the same number of records. We are using the WLL to create the historic data, the other sensors (AirLink) may have more or less records! + // For the additional sensors, check if they have the same number of records as the WLL. If they do great, we just process the next record. + // If the sensor has more or less historic records than the WLL, then we find the record (if any) that matches the WLL record timestamp + + var refData = sensorWithMostRecs.data[dataIndex].FromJsv(); + var timestamp = Utils.FromUnixTime(refData.ts); + + cumulus.LogMessage($"GetWlHistoricData: Processing record {timestamp:yyyy-MM-dd HH:mm}"); + + var h = timestamp.Hour; + + // if outside roll-over hour, roll-over yet to be done + if (h != rollHour) + { + rolloverdone = false; + } + + // Things that really "should" to be done before we reset the day because the roll-over data contains data for the previous day for these values + // Windrun + // Dominant wind bearing + // ET - if MX calculated + // Degree days + // Rainfall + + // In roll-over hour and roll-over not yet done + if ((h == rollHour) && !rolloverdone) + { + // do roll-over + cumulus.LogMessage("GetWlHistoricData: Day roll-over " + timestamp.ToShortTimeString()); + DayReset(timestamp); + rolloverdone = true; + } + + // Not in midnight hour, midnight rain yet to be done + if (h != 0) + { + midnightraindone = false; + } + + // In midnight hour and midnight rain (and sun) not yet done + if ((h == 0) && !midnightraindone) + { + ResetMidnightRain(timestamp); + ResetSunshineHours(timestamp); + ResetMidnightTemperatures(timestamp); + midnightraindone = true; + } + + DecodeHistoric(sensorWithMostRecs.data_structure_type, sensorWithMostRecs.sensor_type, sensorWithMostRecs.data[dataIndex]); + + foreach (var sensor in histObj.sensors) + { + if (worker.CancellationPending == true) + return; + + int sensorType = sensor.sensor_type; + int dataStructureType = sensor.data_structure_type; + int lsid = sensor.lsid; + + if (sensorType == 323 && cumulus.airLinkOut != null) // AirLink Outdoor + { + if (sensor.data.Count != noOfRecs) + { + var found = false; + foreach (var dataRec in sensor.data) + { + if (worker.CancellationPending == true) + return; + + var rec = dataRec.FromJsv(); + if (rec.ts == refData.ts) + { + // Pass AirLink historic record to the AirLink module to process + cumulus.airLinkOut.DecodeAlHistoric(dataStructureType, dataRec); + found = true; + break; + } + } + if (!found) + cumulus.LogDebugMessage("GetWlHistoricData: Warning. No outdoor AirLink data for this log interval !!"); + } + else + { + // Pass AirLink historic record to the AirLink module to process + cumulus.airLinkOut.DecodeAlHistoric(dataStructureType, sensor.data[dataIndex]); + } + } + else if (sensorType == 326 && cumulus.airLinkIn != null) // AirLink Indoor + { + if (sensor.data.Count != noOfRecs) + { + var found = false; + foreach (var dataRec in sensor.data) + { + if (worker.CancellationPending == true) + return; + + var rec = dataRec.FromJsv(); + + if (rec.ts == refData.ts) + { + // Pass AirLink historic record to the AirLink module to process + cumulus.airLinkIn.DecodeAlHistoric(dataStructureType, dataRec); + found = true; + break; + } + } + if (!found) + cumulus.LogDebugMessage("GetWlHistoricData: Warning. No indoor AirLink data for this log interval !!"); + } + else + { + // Pass AirLink historic record to the AirLink module to process + cumulus.airLinkIn.DecodeAlHistoric(dataStructureType, sensor.data[dataIndex]); + } + } + else if (sensorType != 504 && sensorType != 506 && lsid != sensorWithMostRecs.lsid) + { + if (sensor.data.Count > dataIndex) + { + DecodeHistoric(dataStructureType, sensorType, sensor.data[dataIndex]); + // sensor 504 (WLL info) does not always contain a full set of records, so grab the timestamp from a 'real' sensor + } + } + } + + + // Now we have the primary data, calculate the derived data + if (cumulus.StationOptions.CalculatedWC) + { + // DoWindChill does all the required checks and conversions + DoWindChill(OutdoorTemperature, timestamp); + } + + DoApparentTemp(timestamp); + DoFeelsLike(timestamp); + DoHumidex(timestamp); + DoCloudBaseHeatIndex(timestamp); + + // Log all the data + cumulus.DoLogFile(timestamp, false); + cumulus.LogMessage("GetWlHistoricData: Log file entry written"); + cumulus.MySqlRealtimeFile(999, false, timestamp); + cumulus.DoCustomIntervalLogs(timestamp); + + if (cumulus.StationOptions.LogExtraSensors) + { + cumulus.DoExtraLogFile(timestamp); + } + + if (cumulus.airLinkOut != null || cumulus.airLinkIn != null) + { + cumulus.DoAirLinkLogFile(timestamp); + } + + AddRecentDataWithAq(timestamp, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, + OutdoorHumidity, Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); + DoTrendValues(timestamp); + + if (cumulus.StationOptions.CalculatedET && timestamp.Minute == 0) + { + // Start of a new hour, and we want to calculate ET in Cumulus + CalculateEvaoptranspiration(timestamp); + } + + UpdateStatusPanel(timestamp); + cumulus.AddToWebServiceLists(timestamp); + + + if (!Program.service) + Console.Write("\r - processed " + (((double) dataIndex + 1) / noOfRecs).ToString("P0")); + cumulus.LogMessage($"GetWlHistoricData: {dataIndex + 1} of {noOfRecs} archive entries processed"); + } + catch (Exception ex) + { + cumulus.LogErrorMessage("GetWlHistoricData: Exception: " + ex.Message); + } + } + + if (!Program.service) + Console.WriteLine(""); // flush the progress line + } + + private void DecodeHistoric(int dataType, int sensorType, string json, bool current=false) + { + // The WLL sends the timestamp in Unix ticks, and in UTC + + try + { + switch (dataType) + { + case 3: // VP2 ISS archive revision A + case 4: // VP2 ISS archive revision B + { + var data = json.FromJsv(); + lastRecordTime = Utils.FromUnixTime(data.ts); + + CheckLoggingDataStopped(data.arch_int / 60); + + // Temperature & Humidity + /* + * Available fields + * deg_days_cool + * deg_days_heat + * dew_point_in + * dew_point_out + * heat_index_in + * heat_index_out + * hum_in + * hum_out + * temp_in + * temp_out + * temp_out_hi + * temp_out_lo + * wind_chill + */ + + try + { + // do humidity + if (data.hum_out.HasValue) + { + DoOutdoorHumidity(Convert.ToInt32(data.hum_out.Value), lastRecordTime); + } + else + { + cumulus.LogErrorMessage($"WL.com historic: Warning, no valid Humidity data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing Primary humidity value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + // do temperature after humidity as DoOutdoorTemp contains dewpoint calculation (if user selected) + try + { + if (data.temp_out.HasValue && data.temp_out == -99) + { + cumulus.LogErrorMessage($"WL.com historic: Warning, no valid Primary temperature value found [-99] on TxId {data.tx_id}"); + } + else + { + cumulus.LogDebugMessage($"WL.com historic: using temp/hum data from TxId {data.tx_id}"); + + // do high temp + if (data.temp_out_hi.HasValue) + { + DoOutdoorTemp(ConvertTempFToUser(data.temp_out_hi.Value), lastRecordTime); + } + else + { + cumulus.LogErrorMessage($"WL.com historic: Warning, no valid Temperature (high) data on TxId {data.tx_id}"); + } + + // do low temp + if (data.temp_out_lo.HasValue) + { + DoOutdoorTemp(ConvertTempFToUser(data.temp_out_lo.Value), lastRecordTime); + } + else + { + cumulus.LogErrorMessage($"WL.com historic: Warning, no valid Temperature (low) data on TxId {data.tx_id}"); + } + + // do last temp + if (data.temp_out.HasValue) + { + DoOutdoorTemp(ConvertTempFToUser(data.temp_out.Value), lastRecordTime); + + if (!current) + { + // set the values for daily average, arch_int is in seconds, but always whole minutes + tempsamplestoday += data.arch_int / 60; + TempTotalToday += ConvertTempFToUser(data.temp_out.Value) * data.arch_int / 60; + + // update chill hours + if (OutdoorTemperature < cumulus.ChillHourThreshold) + { + // add interval minutes to chill hours - arch_int in seconds + ChillHours += (data.arch_int / 3600.0); + } + + // update heating/cooling degree days + if (!current) + { + UpdateDegreeDays(data.arch_int / 60); + } + } + } + else + { + cumulus.LogErrorMessage($"WL.com historic: Warning, no valid Temperature data on TxId {data.tx_id}"); + } + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing Primary temperature value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + + try + { + // do last DP + if (data.dew_point_out.HasValue) + { + DoOutdoorDewpoint(ConvertTempFToUser(data.dew_point_out.Value), lastRecordTime); + } + else + { + cumulus.LogErrorMessage($"WL.com historic: Warning, no valid Dew Point data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing dew point value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + if (!cumulus.StationOptions.CalculatedWC) + { + // use wind chill from WLL - otherwise we calculate it at the end of processing the historic record when we have all the data + try + { + // do last WC + if (data.wind_chill.HasValue) + { + DoWindChill(ConvertTempFToUser(data.wind_chill.Value), lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Wind Chill data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing wind chill value on TxId {data.tx_id}. Error: {ex.Message}"); + } + } + + // Wind + /* + * Available fields + * + * wind_dir_of_hi + * wind_dir_of_prevail + * wind_num_samples + * wind_run + * wind_speed_avg + * wind_speed_hi + * + */ + + try + { + if (data.wind_speed_hi.HasValue && data.wind_dir_of_hi.HasValue && data.wind_speed_avg.HasValue) + { + var gust = ConvertWindMPHToUser(data.wind_speed_hi.Value); + var spd = ConvertWindMPHToUser(data.wind_speed_avg.Value); + // dir is a direction code: 0=N, 1=NNE, ... 14=NW, 15=NNW - convert to degress + var dir = (int) ((data.wind_dir_of_hi ?? 0) * 22.5); + cumulus.LogDebugMessage($"WL.com historic: using wind data from TxId {data.tx_id}"); + DoWind(gust, dir, spd, lastRecordTime); + RecentMaxGust = cumulus.Calib.WindGust.Calibrate(gust); + //AddValuesToRecentWind(spd, spd, recordTs.AddSeconds(-data.arch_int), recordTs); + } + else + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Wind data on TxId {data.tx_id}"); + } + + if (data.wind_speed_avg.HasValue) + { + WindAverage = cumulus.Calib.WindSpeed.Calibrate(ConvertWindMPHToUser(data.wind_speed_avg.Value)); + + if (!current) + { + // add in 'archivePeriod' minutes worth of wind speed to windrun + int interval = data.arch_int / 60; + WindRunToday += ((WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0); + } + } + else + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Wind data (avg) on TxId {data.tx_id}"); + } + + if (current) + { + // we do not have the latest value, so set a pseudo value between average and gust + WindLatest = random.Next((int) WindAverage, (int) RecentMaxGust); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing wind values on TxId {data.tx_id}. Error: {ex.Message}"); + } + + // Rainfall + /* + * Available fields: + * + * rain_rate_hi_clicks + * rain_rate_hi_in + * rain_rate_hi_mm + * rainfall_clicks + * rainfall_in + * rainfall_mm + */ + + + // The WL API v2 does not provide any running totals for rainfall :( + // So we will have to add the interval data to the running total and hope it all works out! + + try + { + if (data.rainfall_clicks != null && data.rain_rate_hi_clicks.HasValue) + { + cumulus.LogDebugMessage($"WL.com historic: using rain data from TxId {data.tx_id}"); + + var rain = ConvertRainClicksToUser(data.rainfall_clicks.Value, cumulus.DavisOptions.RainGaugeType); + var rainrate = ConvertRainClicksToUser(data.rain_rate_hi_clicks.Value, cumulus.DavisOptions.RainGaugeType); + if (rain > 0) + { + cumulus.LogDebugMessage($"WL.com historic: Adding rain {rain.ToString(cumulus.RainFormat)}"); + } + rain += Raincounter; + + if (rainrate < 0) + { + rainrate = 0; + } + + DoRain(rain, rainrate, lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Rain data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing rain data on TxId {data.tx_id}. Error:{ex.Message}"); + } + + // Pressure + /* + bar + abs_press + bar_noaa + bar_alt + */ + // log the current value + cumulus.LogDebugMessage("WL.com historic: found Baro data"); + try + { + if (data.bar != null) + { + // leave it at current value + DoPressure(ConvertPressINHGToUser((double) data.bar), lastRecordTime); + } + else + { + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data"); + } + + // Altimeter from absolute + if (data.abs_press != null) + { + StationPressure = ConvertPressINHGToUser((double) data.abs_press); + // Or do we use calibration? The VP2 code doesn't? + //StationPressure = ConvertPressINHGToUser(data.Value("bar_absolute")) * cumulus.Calib.Press.Mult + cumulus.Calib.Press.Offset; + AltimeterPressure = ConvertPressMBToUser(StationToAltimeter(ConvertUserPressureToHPa(StationPressure), AltitudeM(cumulus.Altitude))); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing baro reading. Error: {ex.Message}"); + } + + + // UV + /* + * Available fields + * "uv_dose" + * "uv_index_avg" + * "uv_index_hi" + */ + try + { + if (data.uv_index_avg.HasValue) + { + cumulus.LogDebugMessage($"WL.com historic: using UV data from TxId {data.tx_id}"); + + DoUV(data.uv_index_avg.Value, lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid UV data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing UV value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + // Solar + /* + * Available fields + * "solar_energy" + * "solar_rad_avg" + * "solar_rad_hi" + * "et" (inches) - ET field is populated in the ISS archive records, which may not be the same as the solar + */ + try + { + if (data.solar_rad_avg.HasValue) + { + cumulus.LogDebugMessage($"WL.com historic: using solar data from TxId {data.tx_id}"); + DoSolarRad(data.solar_rad_avg.Value, lastRecordTime); + + if (!current) + { + // add in archive period worth of sunshine, if sunny - arch_int in seconds + if ((SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100.00) && (SolarRad >= cumulus.SolarOptions.SolarMinimum)) + { + SunshineHours += (data.arch_int / 3600.0); + } + } + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Solar data on TxId {data.tx_id}"); + } + + if (data.et.HasValue && !cumulus.StationOptions.CalculatedET) + { + // wl.com ET is only available in record the start of each hour. + // The number is the total for the one hour period. + // This is unlike the existing VP2 when the ET is an annual running total + // So we try and mimic the VP behaviour + var newET = AnnualETTotal + ConvertRainINToUser(data.et.Value); + cumulus.LogDebugMessage($"WLL DecodeHistoric: Adding {ConvertRainINToUser(data.et.Value):F3} to ET"); + DoET(newET, lastRecordTime); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing Solar value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + string idx = ""; + + // Leaf Wetness + cumulus.LogDebugMessage($"WL.com historic: found Leaf/Soil data on TxId {data.tx_id}"); + try + { + + if (data.wet_leaf_1.HasValue) + { + DoLeafWetness(data.wet_leaf_1.Value, 1); + } + if (data.wet_leaf_2.HasValue) + { + DoLeafWetness(data.wet_leaf_2.Value, 2); + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, LeafWetness txid={data.tx_id}: {e.Message}"); + } + + + // Soil Moisture + try + { + for (var i = 1; i <= 4; i++) + { + idx = "moist_soil_" + i; + + if (data[idx] != null) + { + DoSoilMoisture((double) data[idx], i); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, SoilMoisture txid={data.tx_id}, idx={idx}: {e.Message}"); + } + + + // Soil Temperature + try + { + for (var i = 1; i <= 4; i++) + { + idx = "temp_soil_" + i; + + if (data[idx] != null) + { + DoSoilTemp((double) data[idx], i); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, SoilTemp txid={data.tx_id}, idx={idx}: {e.Message}"); + } + + // Extra Temperature + try + { + for (var i = 1; i <= 4; i++) + { + idx = "temp_extra_" + i; + + if (data[idx] != null) + { + DoExtraTemp((double) data[idx], i); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, ExtraTemp txid={data.tx_id}, idx={idx}: {e.Message}"); + } + + // Extra Humidity + try + { + for (var i = 1; i <= 4; i++) + { + idx = "hum_extra_" + i; + + if (data[idx] != null) + { + DoExtraHum((int) data[idx], i); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, ExtraHum txid={data.tx_id}, idx={idx}: {e.Message}"); + } + } + break; + + case 11: // WLL ISS data + case 24: // WL console ISS data + { + var data = json.FromJsv(); + lastRecordTime = Utils.FromUnixTime(data.ts); + + CheckLoggingDataStopped(data.arch_int / 60); + + // Temperature & Humidity + if (cumulus.WllPrimaryTempHum == data.tx_id) + { + /* + * Available fields + * "cooling_degree_days" + * "dew_point_hi_at" + * "dew_point_hi" + * "dew_point_last" + * "dew_point_lo_at" + * "dew_point_lo" + * "heat_index_hi_at" + * "heat_index_hi" + * "heat_index_last" + * "heating_degree_days" + * "hum_hi_at" + * "hum_hi" + * "hum_last" + * "hum_lo_at" + * "hum_lo" + * "temp_avg" + * "temp_hi_at" + * "temp_last" + * "temp_lo_at" + * "temp_lo" + * "temp_max" + * "wind_chill_last" + * "wind_chill_lo_at" + * "wind_chill_lo" + */ + + DateTime ts; + + try + { + // do high humidity + if (data.hum_hi_at != 0 && data.hum_hi != null) + { + ts = Utils.FromUnixTime(data.hum_hi_at); + DoOutdoorHumidity(Convert.ToInt32(data.hum_hi), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Humidity (high) data on TxId {data.tx_id}"); + } + + // do low humidity + if (data.hum_lo_at != 0 && data.hum_lo != null) + { + ts = Utils.FromUnixTime(data.hum_lo_at); + DoOutdoorHumidity(Convert.ToInt32(data.hum_lo), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Humidity (low) data on TxId {data.tx_id}"); + } + + if (data.hum_last != null) + { + // do current humidity + DoOutdoorHumidity(Convert.ToInt32(data.hum_last), lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Humidity data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing Primary humidity value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + // do temperature after humidity as DoOutdoorTemp contains dewpoint calculation (if user selected) + try + { + if (data.temp_last == -99) + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Primary temperature value found [-99] on TxId {data.tx_id}"); + } + else + { + cumulus.LogDebugMessage($"WL.com historic: using temp/hum data from TxId {data.tx_id}"); + + // do high temp + if (data.temp_hi_at != 0 && data.temp_hi != null) + { + ts = Utils.FromUnixTime(data.temp_hi_at); + DoOutdoorTemp(ConvertTempFToUser((double) data.temp_hi), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Temperature (high) data on TxId {data.tx_id}"); + } + + // do low temp + if (data.temp_lo_at != 0 && data.temp_lo != null) + { + ts = Utils.FromUnixTime(data.temp_lo_at); + DoOutdoorTemp(ConvertTempFToUser((double) data.temp_lo), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Temperature (low) data on TxId {data.tx_id}"); + } + + // do last temp + if (data.temp_last != null) + { + DoOutdoorTemp(ConvertTempFToUser((double) data.temp_last), lastRecordTime); + + if (!current) + { + // set the values for daily average, arch_int is in seconds, but always whole minutes + tempsamplestoday += data.arch_int / 60; + TempTotalToday += ConvertTempFToUser(data.temp_avg) * data.arch_int / 60; + + // update chill hours + if (OutdoorTemperature < cumulus.ChillHourThreshold) + { + // add interval minutes to chill hours - arch_int in seconds + ChillHours += (data.arch_int / 3600.0); + } + + // update heating/cooling degree days + UpdateDegreeDays(data.arch_int / 60); + } + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Temperature data on TxId {data.tx_id}"); + } + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing Primary temperature value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + + try + { + // do high DP + if (data.dew_point_hi_at != 0 && data.dew_point_hi != null) + { + ts = Utils.FromUnixTime(data.dew_point_hi_at); + DoOutdoorDewpoint(ConvertTempFToUser((double) data.dew_point_hi), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Dew Point (high) data on TxId {data.tx_id}"); + } + + // do low DP + if (data.dew_point_lo_at != 0 && data.dew_point_lo != null) + { + ts = Utils.FromUnixTime(data.dew_point_lo_at); + DoOutdoorDewpoint(ConvertTempFToUser((double) data.dew_point_lo), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Dew Point (low) data on TxId {data.tx_id}"); + } + + // do last DP + if (data.dew_point_last != null) + { + DoOutdoorDewpoint(ConvertTempFToUser((double) data.dew_point_last), lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Dew Point data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing dew point value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + if (!cumulus.StationOptions.CalculatedWC) + { + // use wind chill from WLL - otherwise we calculate it at the end of processing the historic record when we have all the data + try + { + // do low WC + if (data.wind_chill_lo_at != 0 && data.wind_chill_lo != null) + { + ts = Utils.FromUnixTime(data.wind_chill_lo_at); + DoWindChill(ConvertTempFToUser((double) data.wind_chill_lo), ts); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Wind Chill (low) data on TxId {data.tx_id}"); + } + + // do last WC + if (data.wind_chill_last != null) + { + DoWindChill(ConvertTempFToUser((double) data.wind_chill_last), lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Wind Chill data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing wind chill value on TxId {data.tx_id}. Error: {ex.Message}"); + } + } + } + else + { // Check for Extra temperature/humidity settings + for (var tempTxId = 1; tempTxId <= 8; tempTxId++) + { + if (cumulus.WllExtraTempTx[tempTxId] != data.tx_id) continue; + + try + { + if (data.temp_last == -99 || data.temp_last == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Extra temperature value on TxId {data.tx_id}"); + } + else + { + cumulus.LogDebugMessage($"WL.com historic: using extra temp data from TxId {data.tx_id}"); + + DoExtraTemp(ConvertTempFToUser((double) data.temp_last), tempTxId); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing extra temp value on TxId {data.tx_id}"); + cumulus.LogDebugMessage($"WL.com historic: Exception {ex.Message}"); + } + + if (!cumulus.WllExtraHumTx[tempTxId]) continue; + + try + { + if (data.hum_last != null) + { + DoExtraHum((double) data.hum_last, tempTxId); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing extra humidity value on TxId {data.tx_id}. Error: {ex.Message}"); + } + } + } + + // Wind + if (cumulus.WllPrimaryWind == data.tx_id) + { + /* + * Available fields + * "wind_dir_of_prevail" + * "wind_run" + * "wind_speed_avg" + * "wind_speed_hi_at" + * "wind_speed_hi_dir" + * "wind_speed_hi" + */ + + try + { + if (data.wind_speed_hi != null && data.wind_speed_hi_dir != null && data.wind_speed_avg != null) + { + var gust = ConvertWindMPHToUser((double) data.wind_speed_hi); + var spd = ConvertWindMPHToUser((double) data.wind_speed_avg); + 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); + } + else + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Wind data on TxId {data.tx_id}"); + } + + if (data.wind_speed_avg != null) + { + WindAverage = cumulus.Calib.WindSpeed.Calibrate(ConvertWindMPHToUser((double) data.wind_speed_avg)); + + if (!current) + { + // add in 'archivePeriod' minutes worth of wind speed to windrun + int interval = data.arch_int / 60; + WindRunToday += ((WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0); + } + } + else + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Wind data (avg) on TxId {data.tx_id}"); + } + + if (current) + { + // we do not have the latest value, so set a pseudo value between average and gust + WindLatest = random.Next((int) WindAverage, (int) RecentMaxGust); + } + + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing wind values on TxId {data.tx_id}. Error: {ex.Message}"); + } + } + + // Rainfall + if (cumulus.WllPrimaryRain == data.tx_id) + { + /* + * Available fields: + * "rain_rate_hi_at" + * "rain_rate_hi_clicks" + * "rain_rate_hi_in" + * "rain_rate_hi_mm" + * "rain_size" + * "rainfall_clicks" + * "rainfall_in" + * "rainfall_mm" + */ + + + // The WL API v2 does not provide any running totals for rainfall, only :( + // So we will have to add the interval data to the running total and hope it all works out! + + try + { + if (data.rain_rate_hi_at != 0 && data.rainfall_clicks != null && data.rain_rate_hi_clicks != null) + { + cumulus.LogDebugMessage($"WL.com historic: using rain data from TxId {data.tx_id}"); + + var rain = ConvertRainClicksToUser((double) data.rainfall_clicks, data.rain_size); + var rainrate = ConvertRainClicksToUser((double) data.rain_rate_hi_clicks, data.rain_size); + if (rain > 0) + { + cumulus.LogDebugMessage($"WL.com historic: Adding rain {rain.ToString(cumulus.RainFormat)}"); + } + rain += Raincounter; + + if (rainrate < 0) + { + rainrate = 0; + } + + DoRain(rain, rainrate, lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Rain data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing rain data on TxId {data.tx_id}. Error:{ex.Message}"); + } + + } + + // UV + if (cumulus.WllPrimaryUV == data.tx_id) + { + /* + * Available fields + * "uv_dose" + * "uv_index_avg" + * "uv_index_hi_at" + * "uv_index_hi" + * "uv_volt_last" + */ + try + { + if (data.uv_index_avg != null) + { + cumulus.LogDebugMessage($"WL.com historic: using UV data from TxId {data.tx_id}"); + + DoUV((double) data.uv_index_avg, lastRecordTime); + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid UV data on TxId {data.tx_id}"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing UV value on TxId {data.tx_id}. Error: {ex.Message}"); + } + + } + + // Solar + if (cumulus.WllPrimarySolar == data.tx_id) + { + /* + * Available fields + * "solar_energy" + * "solar_rad_avg" + * "solar_rad_hi_at" + * "solar_rad_hi" + * "solar_rad_volt_last" + * "solar_volt_last" + * "et" (inches) - ET field is populated in the ISS archive records, which may not be the same as the solar + */ + try + { + if (data.solar_rad_avg != null) + { + cumulus.LogDebugMessage($"WL.com historic: using solar data from TxId {data.tx_id}"); + DoSolarRad((int) data.solar_rad_avg, lastRecordTime); + + if (!current) + { + // add in archive period worth of sunshine, if sunny - arch_int in seconds + if ((SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100.00) && (SolarRad >= cumulus.SolarOptions.SolarMinimum)) + { + SunshineHours += (data.arch_int / 3600.0); + } + } + } + else + { + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Solar data on TxId {data.tx_id}"); + } + + if (data.et != null && !cumulus.StationOptions.CalculatedET) + { + // wl.com ET is only available in record the start of each hour. + // The number is the total for the one hour period. + // This is unlike the existing VP2 when the ET is an annual running total + // So we try and mimic the VP behaviour + var newET = AnnualETTotal + ConvertRainINToUser((double) data.et); + cumulus.LogDebugMessage($"WLL DecodeHistoric: Adding {ConvertRainINToUser((double) data.et):F3} to ET"); + DoET(newET, lastRecordTime); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing Solar value on TxId {data.tx_id}. Error: {ex.Message}"); + } + } + } + break; + + case 13: // WeatherLink Live Non-ISS data + case 26: + switch (sensorType) + { + case 56: // Soil + Leaf + var data = json.FromJsv(); + + string idx = ""; + /* + * Leaf Wetness + * Available fields + * "wet_leaf_at_1" + * "wet_leaf_hi_1" + * "wet_leaf_hi_2": + * "wet_leaf_hi_at_1" + * "wet_leaf_hi_at_2" + * "wet_leaf_last_1" + * "wet_leaf_last_2" + * "wet_leaf_last_volt_1" + * "wet_leaf_last_volt_2" + * "wet_leaf_lo_1" + * "wet_leaf_lo_2" + * "wet_leaf_lo_at_2" + * "wet_leaf_min_1" + * "wet_leaf_min_2" + */ + + cumulus.LogDebugMessage($"WL.com historic: found Leaf/Soil data on TxId {data.tx_id}"); + + // We are relying on user configuration here, trap any errors + try + { + if (cumulus.WllExtraLeafTx1 == data.tx_id) + { + idx = "wet_leaf_last_" + cumulus.WllExtraLeafIdx1; + if (data[idx] != null) + DoLeafWetness((double) data[idx], 1); + } + if (cumulus.WllExtraLeafTx2 == data.tx_id) + { + idx = "wet_leaf_last_" + cumulus.WllExtraLeafIdx2; + if (data[idx] != null) + DoLeafWetness((double) data[idx], 2); + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, LeafWetness txid={data.tx_id}, idx={idx}: {e.Message}"); + } + /* + * Soil Moisture + * Available fields + * "moist_soil_hi_1" + * "moist_soil_hi_2" + * "moist_soil_hi_3" + * "moist_soil_hi_4" + * "moist_soil_hi_at_1" + * "moist_soil_hi_at_2" + * "moist_soil_hi_at_3" + * "moist_soil_hi_at_4" + * "moist_soil_last_1" + * "moist_soil_last_2" + * "moist_soil_last_3" + * "moist_soil_last_4" + * "moist_soil_last_volt_1" + * "moist_soil_last_volt_2" + * "moist_soil_last_volt_3" + * "moist_soil_last_volt_4" + * "moist_soil_lo_1" + * "moist_soil_lo_2" + * "moist_soil_lo_3" + * "moist_soil_lo_4" + * "moist_soil_lo_at_1" + * "moist_soil_lo_at_2" + * "moist_soil_lo_at_3" + * "moist_soil_lo_at_4" + */ + + try + { + if (cumulus.WllExtraSoilMoistureTx1 == data.tx_id) + { + idx = "moist_soil_last_" + cumulus.WllExtraSoilMoistureIdx1; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid soil moisture #{cumulus.WllExtraSoilMoistureIdx1} on TxId {data.tx_id}"); + } + else + { + DoSoilMoisture((double) data[idx], 1); + } + } + if (cumulus.WllExtraSoilMoistureTx2 == data.tx_id) + { + idx = "moist_soil_last_" + cumulus.WllExtraSoilMoistureIdx2; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid soil moisture #{cumulus.WllExtraSoilMoistureIdx2} on TxId {data.tx_id}"); + } + else + { + DoSoilMoisture((double) data[idx], 2); + } + } + if (cumulus.WllExtraSoilMoistureTx3 == data.tx_id) + { + idx = "moist_soil_last_" + cumulus.WllExtraSoilMoistureIdx3; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid soil moisture #{cumulus.WllExtraSoilMoistureIdx3} on TxId {data.tx_id}"); + } + else + { + DoSoilMoisture((double) data[idx], 3); + } + } + if (cumulus.WllExtraSoilMoistureTx4 == data.tx_id) + { + idx = "moist_soil_last_" + cumulus.WllExtraSoilMoistureIdx4; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid soil moisture #{cumulus.WllExtraSoilMoistureIdx4} on TxId {data.tx_id}"); + } + else + { + DoSoilMoisture((double) data[idx], 4); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, SoilMoisture txid={data.tx_id}, idx={idx}: {e.Message}"); + } + + /* + * Soil Temperature + * Available fields + * "temp_hi_1" + * "temp_hi_2" + * "temp_hi_3" + * "temp_hi_4" + * "temp_hi_at_1" + * "temp_hi_at_2" + * "temp_hi_at_3" + * "temp_hi_at_4" + * "temp_last_1" + * "temp_last_2" + * "temp_last_3" + * "temp_last_4" + * "temp_last_volt_1" + * "temp_last_volt_2" + * "temp_last_volt_3" + * "temp_last_volt_4" + * "temp_lo_1" + * "temp_lo_2" + * "temp_lo_3" + * "temp_lo_4" + * "temp_lo_at_1" + * "temp_lo_at_2" + * "temp_lo_at_3" + * "temp_lo_at_4" + */ + + try + { + if (cumulus.WllExtraSoilTempTx1 == data.tx_id) + { + idx = "temp_last_" + cumulus.WllExtraSoilTempIdx1; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid extra soil temp #{cumulus.WllExtraSoilTempIdx1} on TxId {data.tx_id}"); + } + else + { + DoSoilTemp(ConvertTempFToUser((double) data[idx]), 1); + } + } + if (cumulus.WllExtraSoilTempTx2 == data.tx_id) + { + idx = "temp_last_" + cumulus.WllExtraSoilTempIdx2; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid extra soil temp #{cumulus.WllExtraSoilTempIdx2} on TxId {data.tx_id}"); + } + else + { + DoSoilTemp(ConvertTempFToUser((double) data[idx]), 2); + } + } + if (cumulus.WllExtraSoilTempTx3 == data.tx_id) + { + idx = "temp_last_" + cumulus.WllExtraSoilTempIdx3; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid extra soil temp #{cumulus.WllExtraSoilTempIdx3} on TxId {data.tx_id}"); + } + else + { + DoSoilTemp(ConvertTempFToUser((double) data[idx]), 3); + } + } + if (cumulus.WllExtraSoilTempTx4 == data.tx_id) + { + idx = "temp_last_" + cumulus.WllExtraSoilTempIdx4; + if (data[idx] == null) + { + cumulus.LogDebugMessage($"WL.com historic: Warning, no valid extra soil temp #{cumulus.WllExtraSoilTempIdx4} on TxId {data.tx_id}"); + } + else + { + DoSoilTemp(ConvertTempFToUser((double) data[idx]), 4); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, SoilTemp txid={data.tx_id}, idx={idx}: {e.Message}"); + } + + break; + + case 242: // Baro + /* + * Available fields + * "bar_absolute" + * "bar_hi_at" + * "bar_sea_level" + * "arch_int" + * "bar_lo" + * "bar_hi" + * "bar_lo_at" + */ + // log the current value + cumulus.LogDebugMessage("WL.com historic: found Baro data"); + try + { + var data13baro = json.FromJsv(); + DateTime ts; + // check the high + if (data13baro.bar_hi_at != 0 && data13baro.bar_hi != null) + { + ts = Utils.FromUnixTime(data13baro.bar_hi_at); + DoPressure(ConvertPressINHGToUser((double) data13baro.bar_hi), ts); + } + else + { + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data (high)"); + } + // check the low + if (data13baro.bar_lo_at != 0 && data13baro.bar_lo != null) + { + ts = Utils.FromUnixTime(data13baro.bar_lo_at); + DoPressure(ConvertPressINHGToUser((double) data13baro.bar_lo), ts); + } + else + { + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data (high)"); + } + + if (data13baro.bar_sea_level != null) + { + // leave it at current value + ts = Utils.FromUnixTime(data13baro.ts); + DoPressure(ConvertPressINHGToUser((double) data13baro.bar_sea_level), ts); + } + else + { + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data (high)"); + } + + // Altimeter from absolute + if (data13baro.bar_absolute != null) + { + StationPressure = ConvertPressINHGToUser((double) data13baro.bar_absolute); + // Or do we use calibration? The VP2 code doesn't? + //StationPressure = ConvertPressINHGToUser(data.Value("bar_absolute")) * cumulus.Calib.Press.Mult + cumulus.Calib.Press.Offset; + AltimeterPressure = ConvertPressMBToUser(StationToAltimeter(ConvertUserPressureToHPa(StationPressure), AltitudeM(cumulus.Altitude))); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing baro reading. Error: {ex.Message}"); + } + break; + + case 243: // Inside temp/hum + /* + * Available fields + * "dew_point_in" + * "heat_index_in" + * "hum_in_hi" + * "hum_in_hi_at" + * "hum_in_last" + * "hum_in_lo" + * "hum_in_lo_at" + * "temp_in_hi" + * "temp_in_hi_at" + * "temp_in_last" + * "temp_in_lo" + * "temp_in_lo_at" + */ + cumulus.LogDebugMessage("WL.com historic: found inside temp/hum data"); + + var data13temp = json.FromJsv(); + try + { + if (data13temp.temp_in_last != null) + { + DoIndoorTemp(ConvertTempFToUser((double) data13temp.temp_in_last)); + } + else + { + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Inside Temperature"); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing temp-in reading. Error: {ex.Message}]"); + } + + + try + { + if (data13temp.hum_in_last != null) + { + DoIndoorHumidity(Convert.ToInt32(data13temp.hum_in_last)); + } + else + { + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Inside Humidity"); + } + } + catch (Exception ex) + { + cumulus.LogDebugMessage($"WLL current: Error processing humidity-in. Error: {ex.Message}]"); + } + + break; + + default: + cumulus.LogDebugMessage($"WL.com historic: Not processing sensor type [{sensorType}]!"); + break; + + } + break; + + case 15: // WLL Health + { + var data = json.FromJsv(); + DecodeWllHealth(data, false); + } + break; + + case 17: // AirLink + cumulus.LogDebugMessage("TODO: Decode AirLink"); + break; + + case 18: // AirLink Health + cumulus.LogDebugMessage("TODO: Decode AirLink Health"); + break; + + default: + cumulus.LogDebugMessage($"WL.com historic: found an unknown data structure type [{dataType}]!"); + break; + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"Error, DecodeHistoric, DataType={dataType}, SensorType={sensorType}: " + e.Message); + } + } + + private void DecodeWllHealth(WlHealthDataType15 data, bool startingup) + { + /* WLL Device + * + * Available fields + * "battery_voltage" + * "bgn" - historic only + * "bluetooth_version" - historic only + * "bootloader_version" + * "dns_type_used" - historic only + * "espressif_version" + * "firmware_version" + * "health_version" + * "input_voltage" + * "ip_address_type" + * "ip_v4_address" + * "ip_v4_gateway" + * "ip_v4_netmask" + * "link_uptime" + * "local_api_queries" + * "network_error" + * "network_type": + * "radio_version" + * "rapid_records_sent" + * "rx_bytes" + * "touchpad_wakeups" + * "tx_bytes" + * "uptime" + * "wifi_rssi" + * "ts" - historic only + */ + + cumulus.LogDebugMessage("WL Health - found health data for WLL device"); + + try + { + var dat = Utils.FromUnixTime(data.firmware_version); + DavisFirmwareVersion = dat.ToUniversalTime().ToString("yyyy-MM-dd"); + + var battV = data.battery_voltage / 1000.0; + ConBatText = battV.ToString("F2"); + // Allow voltage to drop to 1.35V per cell before triggering the alarm. This should leave a good reserve without changing them too often + // 1.35 * 4 = 5.4 + if (battV < 5.4) + { + wllVoltageLow = true; + cumulus.LogWarningMessage($"WLL WARNING: Backup battery voltage is low = {battV:0.##}V"); + } + else + { + wllVoltageLow = false; + cumulus.LogDebugMessage($"WLL Battery Voltage = {battV:0.##}V"); + } + var inpV = data.input_voltage / 1000.0; + ConSupplyVoltageText = inpV.ToString("F2"); + if (inpV < 4.0) + { + cumulus.LogWarningMessage($"WLL WARNING: Input voltage is low = {inpV:0.##}V"); + } + else + { + cumulus.LogDebugMessage($"WLL Input Voltage = {inpV:0.##}V"); + } + var upt = TimeSpan.FromSeconds(data.uptime); + var uptStr = string.Format("{0}d:{1:D2}h:{2:D2}m:{3:D2}s", + (int) upt.TotalDays, + upt.Hours, + upt.Minutes, + upt.Seconds); + cumulus.LogDebugMessage("WLL Uptime = " + uptStr); + + // Only present if WiFi attached + if (data.wifi_rssi.HasValue) + { + DavisTxRssi[0] = data.wifi_rssi.Value; + cumulus.LogDebugMessage("WLL WiFi RSSI = " + DavisTxRssi[0] + "dB"); + } + + upt = TimeSpan.FromSeconds(data.link_uptime); + uptStr = string.Format("{0}d:{1:D2}h:{2:D2}m:{3:D2}s", + (int) upt.TotalDays, + upt.Hours, + upt.Minutes, + upt.Seconds); + cumulus.LogDebugMessage("WLL Link Uptime = " + uptStr); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"WL.com historic: Error processing WLL health. Error: {ex.Message}"); + DavisFirmwareVersion = "???"; + } + + if (startingup) + { + cumulus.LogMessage("WLL FW version = " + DavisFirmwareVersion); + } + else + { + cumulus.LogDebugMessage("WLL FW version = " + DavisFirmwareVersion); + } + } + + private void DecodeISSHealth(WlHistorySensor sensor) + { + /* ISS & Non-ISS have the same health fields + * Available fields of interest to health + * "afc": -1 + * "error_packets": 0 + * "good_packets_streak": 602 + * "reception": 100 + * "resynchs": 0 + * "rssi": -60 + * "supercap_volt_last": null + * "trans_battery_flag": 0 + * "trans_battery": null + * "tx_id": 2 + */ + + try + { + string type; + if (sensor.sensor_type == 37 || sensor.sensor_type == 84 || sensor.sensor_type == 85) + type = "Vue"; + else + type = sensor.data_structure_type == 11 ? "ISS" : "Soil-Leaf/Temperature"; + + var data = sensor.data.Last().FromJsv(); + + cumulus.LogDebugMessage($"XMTR Health - found health data for {type} device TxId = {data.tx_id}"); + + // Check battery state 0=Good, 1=Low + SetTxBatteryStatus(data.tx_id, data.trans_battery_flag); + if (data.trans_battery_flag == 1) + { + cumulus.LogWarningMessage($"XMTR WARNING: Battery voltage is low in TxId {data.tx_id}"); + } + else + { + cumulus.LogDebugMessage($"XMTR Health: {type} {data.tx_id}: Battery state is OK"); + } + + //DavisTotalPacketsReceived[txid] = ; // Do not have a value for this + DavisTotalPacketsMissed[data.tx_id] = data.error_packets; + DavisNumCRCerrors[data.tx_id] = data.error_packets; + DavisNumberOfResynchs[data.tx_id] = data.resynchs; + DavisMaxInARow[data.tx_id] = data.good_packets_streak; + DavisReceptionPct[data.tx_id] = data.reception; + DavisTxRssi[data.tx_id] = data.rssi; + + var logMsg = $"XMTR Health: {type} {data.tx_id}: Errors={DavisTotalPacketsMissed[data.tx_id]}, CRCs={DavisNumCRCerrors[data.tx_id]}, Resyncs={DavisNumberOfResynchs[data.tx_id]}, Streak={DavisMaxInARow[data.tx_id]}, %={DavisReceptionPct[data.tx_id]}, RSSI={DavisTxRssi[data.tx_id]}"; + logMsg += data.supercap_volt_last != null ? $", Supercap={data.supercap_volt_last:F2}V" : ""; + logMsg += data.solar_volt_last != null ? $", Supercap={data.solar_volt_last:F2}V" : ""; + cumulus.LogDebugMessage(logMsg); + + // Is there any ET in this record? + if (sensor.data_structure_type == 11 && data.et != null) + { + // wl.com ET is only available in record the start of each hour. + // The number is the total for the one hour period. + // This is unlike the existing VP2 when the ET is an annual running total + // So we try and mimic the VP behaviour + var newET = AnnualETTotal + ConvertRainINToUser((double) data.et); + cumulus.LogDebugMessage($"XMTR Health: Adding {ConvertRainINToUser((double) data.et):F3} to ET"); + DoET(newET, DateTime.Now); + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"XMTR Health: Error processing transmitter health. Error: {ex.Message}"); + } + } + + /* + private void HealthTimerTick(object source, ElapsedEventArgs e) + { + // Only run every 15 minutes + // We run at :01, :16, :31, :46 to allow time for wl.com to generate the stats + if (DateTime.Now.Minute % 15 == 1) + { + GetWlHistoricHealth(); + } + } + */ + + // Extracts health information from the last archive record + private void GetWlHistoricHealth() + { + cumulus.LogMessage("WL Health: Get WL.com Historic Data"); + + if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) + { + cumulus.LogWarningMessage("WL Health: Missing WeatherLink API data in the cumulus.ini file, aborting!"); + return; + } + + if (cumulus.WllStationId < 10) + { + const string msg = "No WeatherLink API station ID in the cumulus.ini file"; + cumulus.LogConsoleMessage("GetWlHistoricHealth: " + msg); + cumulus.LogWarningMessage($"WL Health: {msg}, aborting!"); + return; + } + + var unixDateTime = Utils.ToUnixTime(DateTime.Now); + var startTime = unixDateTime - weatherLinkArchiveInterval; + long endTime = unixDateTime; + + cumulus.LogDebugMessage($"WL Health: Downloading the historic record from WL.com from: {Utils.FromUnixTime(startTime):s} to: {Utils.FromUnixTime(endTime):s}"); + + StringBuilder historicUrl = new StringBuilder("https://api.weatherlink.com/v2/historic/" + cumulus.WllStationId); + historicUrl.Append("?api-key=" + cumulus.WllApiKey); + historicUrl.Append("&start-timestamp=" + startTime.ToString()); + historicUrl.Append("&end-timestamp=" + endTime.ToString()); + + cumulus.LogDebugMessage($"WL Health: WeatherLink URL = {historicUrl.ToString().Replace(cumulus.WllApiKey, "API_KEY")}"); + + try + { + WlHistory histObj; + string responseBody; + int responseCode; + + var request = new HttpRequestMessage(HttpMethod.Get, historicUrl.ToString()); + request.Headers.Add("X-Api-Secret", cumulus.WllApiSecret); + + // we want to do this synchronously, so .Result + using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"WL Health: WeatherLink API Response code: {responseCode}"); + cumulus.LogDataMessage($"WL Health: WeatherLink API Response: {responseBody}"); + } + + if (responseCode != 200) + { + var errObj = responseBody.FromJson(); + cumulus.LogWarningMessage($"WL Health: WeatherLink API Error: {errObj.code}, {errObj.message}"); + // Get wl.com status + GetSystemStatus(); + return; + } + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("WL Health: WeatherLink API: No data was returned. Check your Device Id."); + cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); + // Get wl.com status + GetSystemStatus(); + return; + } + + if (!responseBody.StartsWith("{\"")) // basic sanity check + { + // No idea what we got, dump it to the log + cumulus.LogErrorMessage("WL Health: Invalid historic message received"); + cumulus.LogDataMessage("WL Health: Received: " + responseBody); + return; + } + + histObj = responseBody.FromJson(); + + // get the sensor data + if (histObj.sensors.Count == 0) + { + cumulus.LogMessage("WL Health: No historic data available"); + return; + } + else + { + cumulus.LogDebugMessage($"WL Health: Found {histObj.sensors.Count} sensor records to process"); + } + + + try + { + // Sensor types we are interested in... + // 504 = WLL Health - Now in current + // 506 = AirLink Health - Now in current + + // Get the LSID of the health station associated with each device + //var wllHealthLsid = GetWlHistoricHealthLsid(cumulus.WllParentId, 504); + var alInHealthLsid = GetWlHistoricHealthLsid(cumulus.airLinkInLsid, 506); + var alOutHealthLsid = GetWlHistoricHealthLsid(cumulus.airLinkOutLsid, 506); + + foreach (var sensor in histObj.sensors) + { + var sensorType = sensor.sensor_type; + var dataStructureType = sensor.data_structure_type; + var lsid = sensor.lsid; + + if (sensorType >= 23 && sensorType < 100) + { + DecodeISSHealth(sensor); + } + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage("WLL Health: exception: " + ex.Message); + } + cumulus.BatteryLowAlarm.Triggered = TxBatText.Contains("LOW") || wllVoltageLow; + } + catch (Exception ex) + { + cumulus.LogErrorMessage("WLL Health: exception: " + ex.Message); + } + + } + + // Finds all stations associated with this API + // Return true if only 1 result is found, else return false + private void GetAvailableStationIds(bool logToConsole = false) + { + if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) + { + cumulus.LogWarningMessage("WLLStations: Missing WeatherLink API data in the cumulus.ini file, aborting!"); + return; + } + + var stationsUrl = "https://api.weatherlink.com/v2/stations?api-key=" + cumulus.WllApiKey; + + cumulus.LogDebugMessage($"WLLStations: URL = {stationsUrl.ToString().Replace(cumulus.WllApiKey, "API_KEY")}"); + + try + { + string responseBody; + int responseCode; + + var request = new HttpRequestMessage(HttpMethod.Get, stationsUrl.ToString()); + request.Headers.Add("X-Api-Secret", cumulus.WllApiSecret); + + // We want to do this synchronously + using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + var resp = System.Text.RegularExpressions.Regex.Replace(responseBody, "user_email\":\"[^\"]*\"", "user_email\":\"<>\""); + cumulus.LogDebugMessage($"WLLStations: WeatherLink API Response: {responseCode}: {resp}"); + } + + if (responseCode != 200) + { + var errObj = responseBody.FromJson(); + cumulus.LogErrorMessage($"WLLStations: WeatherLink API Error: {errObj.code} - {errObj.message}"); + return; + } + + var stationsObj = responseBody.FromJson(); + + foreach (var station in stationsObj.stations) + { + cumulus.LogMessage($"WLLStations: Found WeatherLink station id = {station.station_id}, name = {station.station_name}"); + if (stationsObj.stations.Count > 1 && logToConsole) + { + cumulus.LogConsoleMessage($" - Found WeatherLink station id = {station.station_id}, name = {station.station_name}, active = {station.active}"); + } + if (station.station_id == cumulus.WllStationId) + { + cumulus.LogDebugMessage($"WLLStations: Setting WLL parent ID = {station.gateway_id}"); + cumulus.WllParentId = station.gateway_id; + + if (station.recording_interval != cumulus.logints[cumulus.DataLogInterval]) + { + cumulus.LogWarningMessage($"WLLStations: - Cumulus log interval {cumulus.logints[cumulus.DataLogInterval]} does not match this WeatherLink stations log interval {station.recording_interval}"); + } + + wlStationArchiveInterval = station.recording_interval; + DataTimeoutMins = wlStationArchiveInterval + 3; + } + } + if (stationsObj.stations.Count > 1 && cumulus.WllStationId < 10) + { + if (logToConsole) + cumulus.LogConsoleMessage(" - Enter the required station id from the above list into your WLL configuration to enable history downloads."); + } + else if (stationsObj.stations.Count == 1 && cumulus.WllStationId != stationsObj.stations[0].station_id) + { + cumulus.LogMessage($"WLLStations: Only found 1 WeatherLink station, using id = {stationsObj.stations[0].station_id}"); + cumulus.WllStationId = stationsObj.stations[0].station_id; + // And save it to the config file + cumulus.WriteIniFile(); + + cumulus.LogDebugMessage($"WLLStations: Setting WLL parent ID = {stationsObj.stations[0].gateway_id}"); + cumulus.WllParentId = stationsObj.stations[0].gateway_id; + wlStationArchiveInterval = stationsObj.stations[0].recording_interval; + return; + } + } + catch (Exception ex) + { + cumulus.LogDebugMessage("WLLStations: WeatherLink API exception: " + ex.Message); + } + return; + } + + private void GetAvailableSensors() + { + if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) + { + cumulus.LogMessage("GetAvailableSensors: WeatherLink API data is missing in the configuration, aborting!"); + return; + } + + if (cumulus.WllStationId < 10) + { + cumulus.LogMessage("GetAvailableSensors: No WeatherLink API station ID has been configured, aborting!"); + return; + } + + var stationsUrl = "https://api.weatherlink.com/v2/sensors?api-key=" + cumulus.WllApiKey; + + cumulus.LogDebugMessage($"GetAvailableSensors: URL = {stationsUrl.Replace(cumulus.WllApiKey, "API_KEY")}"); + + WlSensorList sensorsObj; + + try + { + string responseBody; + int responseCode; + var request = new HttpRequestMessage(HttpMethod.Get, stationsUrl); + request.Headers.Add("X-Api-Secret", cumulus.WllApiSecret); + + // We want to do this synchronously + using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"GetAvailableSensors: WeatherLink API Response: {responseCode}: {responseBody}"); + } + + if (responseCode != 200) + { + var errObj = responseBody.FromJson(); + cumulus.LogErrorMessage($"GetAvailableSensors: WeatherLink API Error: {errObj.code} - {errObj.message}"); + return; + } + + sensorsObj = responseBody.FromJson(); + } + catch (Exception ex) + { + cumulus.LogDebugMessage("GetAvailableSensors: WeatherLink API exception: " + ex.Message); + return; + } + + sensorList = sensorsObj.sensors; + + foreach (var sensor in sensorList) + { + try + { + cumulus.LogDebugMessage($"GetAvailableSensors: Found WeatherLink Sensor type={sensor.sensor_type}, lsid={sensor.lsid}, station_id={sensor.station_id}, name={sensor.product_name}, parentId={sensor.parent_device_id}, parent={sensor.parent_device_name}"); + + if (sensor.sensor_type == 323 && sensor.station_id == cumulus.AirLinkOutStationId) + { + cumulus.LogDebugMessage($"GetAvailableSensors: Setting AirLink Outdoor LSID to {sensor.lsid}"); + cumulus.airLinkOutLsid = sensor.lsid; + } + else if (sensor.sensor_type == 326 && sensor.station_id == cumulus.AirLinkInStationId) + { + cumulus.LogDebugMessage($"GetAvailableSensors: Setting AirLink Indoor LSID to {sensor.lsid}"); + cumulus.airLinkInLsid = sensor.lsid; + } + } + catch (Exception ex) + { + cumulus.LogDebugMessage("GetAvailableSensors: Processing sensors exception: " + ex.Message); + } + } + + return; + } + + private int GetWlHistoricHealthLsid(int id, int type) + { + try + { + var sensor = sensorList.FirstOrDefault(i => i.lsid == id || i.parent_device_id == id); + if (sensor != null) + { + var health = sensorList.FirstOrDefault(i => i.parent_device_id == sensor.parent_device_id && i.sensor_type == type); + if (health != null) + { + return health.lsid; + } + } + } + catch + { } + return 0; + } + + private void GetSystemStatus() + { + WlComSystemStatus status; + try + { + string responseBody; + int responseCode; + + cumulus.LogDebugMessage("GetSystemStatus: Getting WeatherLink.com system status"); + + // we want to do this synchronously, so .Result + using (var response = Cumulus.MyHttpClient.GetAsync("https://0886445102835570.hostedstatus.com/1.0/status/600712dea9c1290530967bc6").Result) + { + responseBody = response.Content.ReadAsStringAsync().Result; + responseCode = (int) response.StatusCode; + cumulus.LogDebugMessage($"GetSystemStatus: WeatherLink.com system status Response code: {responseCode}"); + cumulus.LogDataMessage($"GetSystemStatus: WeatherLink.com system status Response: {responseBody}"); + } + + if (responseCode != 200) + { + cumulus.LogWarningMessage($"GetSystemStatus: WeatherLink.com system status Error: {responseCode}"); + cumulus.LogConsoleMessage($" - Error {responseCode}"); + return; + } + + status = responseBody.FromJson(); + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("GetSystemStatus: WeatherLink.com system status: No data was returned."); + return; + } + else if (status != null) + { + string msg; + if (status.result.status_overall.status_code != 100) + { + msg = status.ToString(true); + cumulus.LogWarningMessage(msg); + Console.WriteLine(msg); + } + else + { + msg = status.ToString(false); + cumulus.LogDebugMessage(msg); + } + } + else + { + cumulus.LogWarningMessage("GetSystemStatus: Something went wrong!"); + } + + } + catch (Exception ex) + { + cumulus.LogErrorMessage("GetSystemStatus: Exception: " + ex); + } + + return; + } + + private void CheckLoggingDataStopped(int mins) + { + if (mins != wlStationArchiveInterval || DataTimeoutMins != mins + 3) + { + wlStationArchiveInterval = mins; + DataTimeoutMins = wlStationArchiveInterval + 3; + } + } + } +} diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index d3776378..36b36cf0 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -1,12 +1,12 @@ using System; using System.ComponentModel; using System.Diagnostics; +using System.Globalization; using System.IO.Ports; +using System.Linq; using System.Net.Sockets; using System.Text; using System.Threading; -using System.Globalization; -using System.Linq; namespace CumulusMX { @@ -101,15 +101,15 @@ public DavisStation(Cumulus cumulus) : base(cumulus) { if (DavisFirmwareVersion == "???" && cumulus.DavisOptions.UseLoop2) { - cumulus.LogMessage("Unable to determine the firmware version, LOOP2 may not be supported"); + cumulus.LogWarningMessage("Unable to determine the firmware version, LOOP2 may not be supported"); } - else if((float.Parse(DavisFirmwareVersion, CultureInfo.InvariantCulture.NumberFormat) < (float)1.9) && cumulus.DavisOptions.UseLoop2) + else if ((float.Parse(DavisFirmwareVersion, CultureInfo.InvariantCulture.NumberFormat) < (float) 1.9) && cumulus.DavisOptions.UseLoop2) { - cumulus.LogMessage("LOOP2 is enabled in Cumulus.ini but this firmware version does not support it. Consider disabling it in Cumulus.ini"); + cumulus.LogWarningMessage("LOOP2 is enabled in Cumulus.ini but this firmware version does not support it. Consider disabling it in Cumulus.ini"); cumulus.LogConsoleMessage("Your console firmware version does not support LOOP2. Consider disabling it in Cumulus.ini", ConsoleColor.Yellow); } } - catch(Exception ex) + catch (Exception ex) { cumulus.LogDebugMessage("Error parsing firmware string for version number: " + ex.Message); } @@ -138,7 +138,7 @@ public DavisStation(Cumulus cumulus) : base(cumulus) { if (cumulus.StationOptions.SyncTime) { - cumulus.LogMessage($"Console clock: Console is {(int)timeDiff} seconds adrift, resetting it..."); + cumulus.LogWarningMessage($"Console clock: Console is {(int) timeDiff} seconds adrift, resetting it..."); SetTime(); // Pause whilst the console sorts itself out @@ -154,22 +154,22 @@ public DavisStation(Cumulus cumulus) : base(cumulus) } else { - cumulus.LogMessage("Console clock: Failed to read console time"); + cumulus.LogWarningMessage("Console clock: Failed to read console time"); } } else { - cumulus.LogMessage($"Console clock: Console is {(int)timeDiff} seconds adrift but automatic setting is disabled - you should set the clock manually."); + cumulus.LogWarningMessage($"Console clock: Console is {(int) timeDiff} seconds adrift but automatic setting is disabled - you should set the clock manually."); } } else { - cumulus.LogMessage($"Console clock: Accurate to +/- 30 seconds, no need to set it (diff={(int)nowTime.Subtract(consoleclock).TotalSeconds}s)"); + cumulus.LogMessage($"Console clock: Accurate to +/- 30 seconds, no need to set it (diff={(int) nowTime.Subtract(consoleclock).TotalSeconds}s)"); } } else { - cumulus.LogMessage("Console clock: Failed to read console time"); + cumulus.LogWarningMessage("Console clock: Failed to read console time"); } @@ -259,11 +259,11 @@ private string GetFirmwareVersion() } catch (TimeoutException) { - cumulus.LogMessage("GetFirmwareVersion: Timed out waiting for a response"); + cumulus.LogErrorMessage("GetFirmwareVersion: Timed out waiting for a response"); } catch (Exception ex) { - cumulus.LogMessage("GetFirmwareVersion: Error - " + ex.Message); + cumulus.LogErrorMessage("GetFirmwareVersion: Error - " + ex.Message); cumulus.LogDebugMessage("GetFirmwareVersion: Attempting to reconnect to logger"); InitSerial(); cumulus.LogDebugMessage("GetFirmwareVersion: Reconnected to logger"); @@ -300,16 +300,16 @@ private string GetFirmwareVersion() { if (ex.Message.Contains("did not properly respond after a period of time")) { - cumulus.LogMessage("GetFirmwareVersion: Timed out waiting for a response"); + cumulus.LogWarningMessage("GetFirmwareVersion: Timed out waiting for a response"); } else { - cumulus.LogMessage("GetFirmwareVersion: Error - " + ex.Message); + cumulus.LogErrorMessage("GetFirmwareVersion: Error - " + ex.Message); } } catch (Exception ex) { - cumulus.LogMessage("GetFirmwareVersion: Error - " + ex.Message); + cumulus.LogErrorMessage("GetFirmwareVersion: Error - " + ex.Message); cumulus.LogDebugMessage("GetFirmwareVersion: Attempting to reconnect to logger"); InitTCP(); cumulus.LogDebugMessage("GetFirmwareVersion: Reconnected to logger"); @@ -345,7 +345,7 @@ private void CheckLoggerInterval() if (!WaitForACK(comport)) { - cumulus.LogMessage("CheckLoggerInterval: No ACK in response to requesting logger interval"); + cumulus.LogWarningMessage("CheckLoggerInterval: No ACK in response to requesting logger interval"); return; } @@ -354,17 +354,17 @@ private void CheckLoggerInterval() { // Read the current character var ch = comport.ReadChar(); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; - } while (bytesRead < 3) ; + } while (bytesRead < 3); } catch (TimeoutException) { - cumulus.LogMessage("CheckLoggerInterval: Timed out waiting for a response"); + cumulus.LogWarningMessage("CheckLoggerInterval: Timed out waiting for a response"); } catch (Exception ex) { - cumulus.LogMessage("CheckLoggerInterval: Error - " + ex.Message); + cumulus.LogErrorMessage("CheckLoggerInterval: Error - " + ex.Message); awakeStopWatch.Stop(); } } @@ -384,7 +384,7 @@ private void CheckLoggerInterval() if (!WaitForACK(stream)) { - cumulus.LogMessage("CheckLoggerInterval: No ACK in response to requesting logger interval"); + cumulus.LogWarningMessage("CheckLoggerInterval: No ACK in response to requesting logger interval"); return; } @@ -392,7 +392,7 @@ private void CheckLoggerInterval() { // Read the current character var ch = stream.ReadByte(); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; //cumulus.LogMessage("Received " + ch.ToString("X2")); } while (bytesRead < 3); @@ -401,17 +401,17 @@ private void CheckLoggerInterval() { if (ex.Message.Contains("did not properly respond after a period")) { - cumulus.LogMessage("CheckLoggerInterval: Timed out waiting for a response"); + cumulus.LogWarningMessage("CheckLoggerInterval: Timed out waiting for a response"); } else { - cumulus.LogMessage("CheckLoggerInterval: Error - " + ex.Message); + cumulus.LogErrorMessage("CheckLoggerInterval: Error - " + ex.Message); awakeStopWatch.Stop(); } } catch (Exception ex) { - cumulus.LogMessage("CheckLoggerInterval: Error - " + ex.Message); + cumulus.LogErrorMessage("CheckLoggerInterval: Error - " + ex.Message); awakeStopWatch.Stop(); } } @@ -427,7 +427,7 @@ private void CheckLoggerInterval() loggerInterval = readBuffer[0]; var msg = $"** WARNING: Your station logger interval {loggerInterval} mins does not match your Cumulus MX logging interval {cumulus.logints[cumulus.DataLogInterval]} mins"; cumulus.LogConsoleMessage(msg); - cumulus.LogMessage("CheckLoggerInterval: " + msg); + cumulus.LogWarningMessage("CheckLoggerInterval: " + msg); if (cumulus.DavisOptions.SetLoggerInterval) { @@ -452,7 +452,7 @@ private void SetLoggerInterval(int interval) if (!WaitForACK(comport)) { - cumulus.LogMessage("SetLoggerInterval: No ACK in response to setting logger interval"); + cumulus.LogWarningMessage("SetLoggerInterval: No ACK in response to setting logger interval"); return; } @@ -462,11 +462,11 @@ private void SetLoggerInterval(int interval) } catch (TimeoutException) { - cumulus.LogMessage("SetLoggerInterval: Timed out waiting for a response"); + cumulus.LogWarningMessage("SetLoggerInterval: Timed out waiting for a response"); } catch (Exception ex) { - cumulus.LogMessage("SetLoggerInterval: Error - " + ex.Message); + cumulus.LogErrorMessage("SetLoggerInterval: Error - " + ex.Message); awakeStopWatch.Stop(); } } @@ -486,7 +486,7 @@ private void SetLoggerInterval(int interval) if (!WaitForACK(stream)) { - cumulus.LogMessage("SetLoggerInterval: No ACK in response to setting logger interval"); + cumulus.LogWarningMessage("SetLoggerInterval: No ACK in response to setting logger interval"); return; } @@ -498,17 +498,17 @@ private void SetLoggerInterval(int interval) { if (ex.Message.Contains("did not properly respond after a period")) { - cumulus.LogMessage("SetLoggerInterval: Timed out waiting for a response"); + cumulus.LogWarningMessage("SetLoggerInterval: Timed out waiting for a response"); } else { - cumulus.LogMessage("SetLoggerInterval: Error - " + ex.Message); + cumulus.LogErrorMessage("SetLoggerInterval: Error - " + ex.Message); awakeStopWatch.Stop(); } } catch (Exception ex) { - cumulus.LogMessage("SetLoggerInterval: Error - " + ex.Message); + cumulus.LogErrorMessage("SetLoggerInterval: Error - " + ex.Message); awakeStopWatch.Stop(); } } @@ -544,18 +544,18 @@ private string GetReceptionStats() // Read the current character ch = comport.ReadChar(); response += Convert.ToChar(ch); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; } while (ch != CR); } } catch (TimeoutException) { - cumulus.LogMessage("GetReceptionStats: Timed out waiting for a response"); + cumulus.LogWarningMessage("GetReceptionStats: Timed out waiting for a response"); } catch (Exception ex) { - cumulus.LogMessage("GetReceptionStats: Error - " + ex.Message); + cumulus.LogErrorMessage("GetReceptionStats: Error - " + ex.Message); cumulus.LogDebugMessage("GetReceptionStats: Attempting to reconnect to logger"); InitSerial(); cumulus.LogDebugMessage("GetReceptionStats: Reconnected to logger"); @@ -583,7 +583,7 @@ private string GetReceptionStats() // Read the current character ch = stream.ReadByte(); response += Convert.ToChar(ch); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; } while (ch != CR); } @@ -592,11 +592,11 @@ private string GetReceptionStats() { if (ex.Message.Contains("did not properly respond after a period")) { - cumulus.LogMessage("GetReceptionStats: Timed out waiting for a response"); + cumulus.LogWarningMessage("GetReceptionStats: Timed out waiting for a response"); } else { - cumulus.LogMessage("GetReceptionStats: Error - " + ex.Message); + cumulus.LogErrorMessage("GetReceptionStats: Error - " + ex.Message); cumulus.LogDebugMessage("GetReceptionStats: Attempting to reconnect to logger"); InitTCP(); cumulus.LogDebugMessage("GetReceptionStats: Reconnected to logger"); @@ -604,7 +604,7 @@ private string GetReceptionStats() } catch (Exception ex) { - cumulus.LogMessage("GetReceptionStats: Error - " + ex.Message); + cumulus.LogErrorMessage("GetReceptionStats: Error - " + ex.Message); cumulus.LogDebugMessage("GetReceptionStats: Attempting to reconnect to logger"); InitTCP(); cumulus.LogDebugMessage("GetReceptionStats: Reconnected to logger"); @@ -645,7 +645,7 @@ private TcpClient OpenTcpPort() } catch (Exception ex) { - cumulus.LogMessage("OpenTcpPort: Error - " + ex.Message); + cumulus.LogErrorMessage("OpenTcpPort: Error - " + ex.Message); } } @@ -757,7 +757,7 @@ private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) cumulus.LogMessage("Logger archive reading thread completed"); if (e.Error != null) { - cumulus.LogMessage("Archive reading thread apparently terminated with an error: "+e.Error.Message); + cumulus.LogErrorMessage("Archive reading thread apparently terminated with an error: " + e.Error.Message); } //cumulus.LogMessage("Updating highs and lows"); //using (cumulusEntities dataContext = new cumulusEntities()) @@ -798,7 +798,7 @@ private void bw_DoWork(object sender, DoWorkEventArgs e) try { // set this temporarily, so speed is done from average and not peak gust from logger - cumulus.StationOptions.UseSpeedForAvgCalc = true; + //cumulus.StationOptions.UseSpeedForAvgCalc = true; do { GetArchiveData(); @@ -806,12 +806,12 @@ private void bw_DoWork(object sender, DoWorkEventArgs e) } while (archiveRun < maxArchiveRuns); // restore the setting - cumulus.StationOptions.UseSpeedForAvgCalc = false; + //cumulus.StationOptions.UseSpeedForAvgCalc = false; } catch (Exception ex) { - cumulus.LogMessage("Exception occurred reading archive data: "+ex.Message); + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); } //cumulus.LogDebugMessage("Lock: Station releasing the lock"); Cumulus.syncInit.Release(); @@ -921,7 +921,7 @@ public override void Start() } else { - cumulus.LogMessage("Console clock: Failed to read console time"); + cumulus.LogWarningMessage("Console clock: Failed to read console time"); } if (Math.Abs(nowTime.Subtract(consoleclock).TotalSeconds) >= 30) @@ -939,12 +939,12 @@ public override void Start() } else { - cumulus.LogMessage("Console clock: Failed to read console time"); + cumulus.LogWarningMessage("Console clock: Failed to read console time"); } } else { - cumulus.LogMessage($"Console clock: Accurate to +/- 30 seconds, no need to set it (diff={(int)nowTime.Subtract(consoleclock).TotalSeconds}s)"); + cumulus.LogMessage($"Console clock: Accurate to +/- 30 seconds, no need to set it (diff={(int) nowTime.Subtract(consoleclock).TotalSeconds}s)"); } clockSetNeeded = false; @@ -975,7 +975,7 @@ public override void Start() } catch (Exception ex) { - cumulus.LogMessage($"Failed to open the comm port ({cumulus.ComportName}). Error - {ex.Message}"); + cumulus.LogErrorMessage($"Failed to open the comm port ({cumulus.ComportName}). Error - {ex.Message}"); } if (comport == null || !comport.IsOpen) { @@ -1040,7 +1040,7 @@ public override void Start() catch (Exception ex) { // any others, log them and carry on - cumulus.LogMessage("Davis Start: Exception - " + ex.Message); + cumulus.LogErrorMessage("Davis Start: Exception - " + ex.Message); } } @@ -1092,7 +1092,7 @@ private void SendBarRead() // Read the current character var ch = comport.ReadChar(); response += Convert.ToChar(ch); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; } while (bytesRead < 7); @@ -1129,7 +1129,7 @@ private void SendBarRead() // Read the current character var ch = stream.ReadByte(); response += Convert.ToChar(ch); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; //cumulus.LogMessage("Received " + ch.ToString("X2")); } while (stream.DataAvailable); @@ -1218,7 +1218,7 @@ private bool SendLoopCommand(SerialPort serialPort, string commandString) if (stop) return false; - cumulus.LogMessage("SendLoopCommand: Error sending LOOP command [" + commandString.Replace("\n", "") + "]: " + ex.Message); + cumulus.LogErrorMessage("SendLoopCommand: Error sending LOOP command [" + commandString.Replace("\n", "") + "]: " + ex.Message); cumulus.LogDebugMessage("SendLoopCommand: Attempting to reconnect to station"); InitSerial(); cumulus.LogDebugMessage("SendLoopCommand: Reconnected to station"); @@ -1266,7 +1266,7 @@ private bool SendLoopCommand(TcpClient tcpPort, string commandString) while (!foundAck && passCount < maxPasses && !stop) { // send the LOOP n command - cumulus.LogDebugMessage("SendLoopCommand: Sending command - " + commandString.Replace("\n","") + ", attempt " + passCount); + cumulus.LogDebugMessage("SendLoopCommand: Sending command - " + commandString.Replace("\n", "") + ", attempt " + passCount); stream.Write(Encoding.ASCII.GetBytes(commandString), 0, commandString.Length); cumulus.LogDebugMessage("SendLoopCommand: Wait for ACK"); @@ -1279,7 +1279,7 @@ private bool SendLoopCommand(TcpClient tcpPort, string commandString) if (foundAck) return true; // Failed to get a response from the loop command after all the retries, try resetting the connection - cumulus.LogDebugMessage($"SendLoopCommand: Failed to get a response after {passCount-1} attempts, reconnecting the station"); + cumulus.LogDebugMessage($"SendLoopCommand: Failed to get a response after {passCount - 1} attempts, reconnecting the station"); InitTCP(); cumulus.LogDebugMessage("SendLoopCommand: Reconnected to station"); } @@ -1287,7 +1287,7 @@ private bool SendLoopCommand(TcpClient tcpPort, string commandString) { if (stop) return false; - cumulus.LogMessage("SendLoopCommand: Error sending LOOP command [" + commandString.Replace("\n", "") + "]: " + ex.Message); + cumulus.LogErrorMessage("SendLoopCommand: Error sending LOOP command [" + commandString.Replace("\n", "") + "]: " + ex.Message); cumulus.LogDebugMessage("SendLoopCommand: Attempting to reconnect to station"); InitTCP(); cumulus.LogDebugMessage("SendLoopCommand: Reconnected to station"); @@ -1346,19 +1346,19 @@ private void GetAndProcessLoopData(int number) tmrComm.Stop(); if (comport.BytesToRead < loopDataLength) { - cumulus.LogMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {comport.BytesToRead}"); + cumulus.LogErrorMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {comport.BytesToRead}"); } comport.Read(loopString, 0, loopDataLength); } catch (TimeoutException) { - cumulus.LogMessage($"LOOP: {i + 1} - Timed out waiting for LOOP data"); + cumulus.LogWarningMessage($"LOOP: {i + 1} - Timed out waiting for LOOP data"); return; } catch (Exception ex) { - cumulus.LogMessage("LOOP: Exception - " + ex); + cumulus.LogErrorMessage("LOOP: Exception - " + ex); cumulus.LogDebugMessage("LOOP: Attempting to reconnect to station"); InitSerial(); cumulus.LogDebugMessage("LOOP: Reconnected to station"); @@ -1386,7 +1386,7 @@ private void GetAndProcessLoopData(int number) } catch (Exception ex) { - cumulus.LogMessage("LOOP: Periodic disconnect error: " + ex.Message); + cumulus.LogErrorMessage("LOOP: Periodic disconnect error: " + ex.Message); } finally { @@ -1394,7 +1394,7 @@ private void GetAndProcessLoopData(int number) } // Wait - Thread.Sleep(cumulus.DavisOptions.PeriodicDisconnectInterval *1000); + Thread.Sleep(cumulus.DavisOptions.PeriodicDisconnectInterval * 1000); cumulus.LogDebugMessage("LOOP: Attempting reconnect to logger"); InitTCP(); @@ -1415,7 +1415,7 @@ private void GetAndProcessLoopData(int number) if (socket.Available < loopDataLength) { - cumulus.LogMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {socket.Available}"); + cumulus.LogErrorMessage($"LOOP: {i + 1} - Expected data not received, expected 99 bytes, got {socket.Available}"); } // Read the first 99 bytes of the buffer into the array @@ -1425,11 +1425,11 @@ private void GetAndProcessLoopData(int number) { if (ex.Message.Contains("did not properly respond after a period")) { - cumulus.LogMessage("LOOP: Timed out waiting for LOOP data"); + cumulus.LogWarningMessage("LOOP: Timed out waiting for LOOP data"); } else { - cumulus.LogMessage("LOOP: Receive error - " + ex); + cumulus.LogErrorMessage("LOOP: Receive error - " + ex); cumulus.LogMessage("LOOP: Reconnecting to station"); InitTCP(); cumulus.LogMessage("LOOP: Reconnected to station"); @@ -1438,7 +1438,7 @@ private void GetAndProcessLoopData(int number) } catch (Exception ex) { - cumulus.LogMessage("LOOP: Receive error - " + ex); + cumulus.LogErrorMessage("LOOP: Receive error - " + ex); cumulus.LogMessage("LOOP: Reconnecting to station"); InitTCP(); cumulus.LogMessage("LOOP: Reconnected to station"); @@ -1499,7 +1499,7 @@ private void GetAndProcessLoopData(int number) if (!CrcOk(loopString)) { - cumulus.LogMessage($"LOOP: {i + 1} - Packet CRC invalid"); + cumulus.LogErrorMessage($"LOOP: {i + 1} - Packet CRC invalid"); continue; } @@ -1858,7 +1858,7 @@ private void GetAndProcessLoop2Data(int number) if (comport.BytesToRead < loopDataLength) { - cumulus.LogMessage($"LOOP2: Expected data not received, expected 99 bytes, got {comport.BytesToRead}"); + cumulus.LogWarningMessage($"LOOP2: Expected data not received, expected 99 bytes, got {comport.BytesToRead}"); } // Read the data from the buffer into the array @@ -1866,12 +1866,12 @@ private void GetAndProcessLoop2Data(int number) } catch (TimeoutException) { - cumulus.LogMessage("LOOP2: Timed out waiting for LOOP2 data"); + cumulus.LogWarningMessage("LOOP2: Timed out waiting for LOOP2 data"); continue; } catch (Exception ex) { - cumulus.LogMessage("LOOP2: Error - " + ex); + cumulus.LogErrorMessage("LOOP2: Error - " + ex); cumulus.LogDebugMessage("LOOP2: Attempting to reconnect to logger"); InitSerial(); cumulus.LogDebugMessage("LOOP2: Reconnected to logger"); @@ -1891,7 +1891,7 @@ private void GetAndProcessLoop2Data(int number) if (socket.Available < loopDataLength) { - cumulus.LogMessage($"LOOP2: Expected data not received, expected 99 bytes got {socket.Available}"); + cumulus.LogWarningMessage($"LOOP2: Expected data not received, expected 99 bytes got {socket.Available}"); } // Read the first 99 bytes of the buffer into the array socket.GetStream().Read(loopString, 0, loopDataLength); @@ -1997,8 +1997,9 @@ private void GetAndProcessLoop2Data(int number) if (loopData.WindGust10Min < 200 && cumulus.StationOptions.PeakGustMinutes >= 10) { // Extract 10-min gust and see if it is higher than we have recorded. - var gust10min = cumulus.Calib.WindSpeed.Calibrate(ConvertWindMPHToUser(loopData.WindGust10Min)); - var gustdir = loopData.WindGustDir; + var rawGust10min = ConvertWindMPHToUser(loopData.WindGust10Min); + var gust10min = cumulus.Calib.WindGust.Calibrate(rawGust10min); + var gustdir = (int)cumulus.Calib.WindDir.Calibrate(loopData.WindGustDir); cumulus.LogDebugMessage("LOOP2: 10-min gust: " + gust10min.ToString(cumulus.WindFormat)); @@ -2008,8 +2009,8 @@ private void GetAndProcessLoop2Data(int number) RecentMaxGust = gust10min; // add to recent values so normal calculation includes this value - WindRecent[nextwind].Gust = gust10min; - WindRecent[nextwind].Speed = WindAverage; + WindRecent[nextwind].Gust = rawGust10min; + WindRecent[nextwind].Speed = -1; WindRecent[nextwind].Timestamp = now; nextwind = (nextwind + 1) % MaxWindRecent; } @@ -2039,9 +2040,9 @@ private void GetArchiveData() const int NAK = 0x21; const int ESC = 0x1b; const int maxPasses = 4; - byte[] ACKstring = {ACK}; - byte[] NAKstring = {NAK}; - byte[] ESCstring = {ESC}; + byte[] ACKstring = { ACK }; + byte[] NAKstring = { NAK }; + byte[] ESCstring = { ESC }; const int pageSize = 267; const int recordSize = 52; bool ack; @@ -2072,7 +2073,7 @@ private void GetArchiveData() } // construct date and time of last record read - int vantageDateStamp = nextLoggerTime.Day + nextLoggerTime.Month*32 + (nextLoggerTime.Year - 2000) * 512; + int vantageDateStamp = nextLoggerTime.Day + nextLoggerTime.Month * 32 + (nextLoggerTime.Year - 2000) * 512; int vantageTimeStamp = (100 * nextLoggerTime.Hour + nextLoggerTime.Minute); cumulus.LogMessage($"GetArchiveData: Last Archive Date: {nextLoggerTime}"); @@ -2088,7 +2089,7 @@ private void GetArchiveData() if (!WakeVP(comport)) { - cumulus.LogMessage("GetArchiveData: Unable to wake VP"); + cumulus.LogWarningMessage("GetArchiveData: Unable to wake VP"); } // send the command @@ -2101,7 +2102,7 @@ private void GetArchiveData() ack = WaitForACK(comport); if (!ack) { - cumulus.LogMessage("GetArchiveData: No Ack in response to DMPAFT"); + cumulus.LogWarningMessage("GetArchiveData: No Ack in response to DMPAFT"); retries++; } } while (!ack && retries < 2); @@ -2117,7 +2118,7 @@ private void GetArchiveData() { if (!WakeVP(socket)) { - cumulus.LogMessage("GetArchiveData: Unable to wake VP"); + cumulus.LogWarningMessage("GetArchiveData: Unable to wake VP"); } cumulus.LogMessage("GetArchiveData: Sending DMPAFT"); @@ -2127,14 +2128,14 @@ private void GetArchiveData() ack = WaitForACK(stream); if (!ack) { - cumulus.LogMessage("GetArchiveData: No Ack in response to DMPAFT"); + cumulus.LogWarningMessage("GetArchiveData: No Ack in response to DMPAFT"); retries++; } } while (!ack && retries < 2); } catch (Exception ex) { - cumulus.LogMessage("GetArchiveData: Error sending LOOP command [DMPAFT]: " + ex.Message); + cumulus.LogErrorMessage("GetArchiveData: Error sending LOOP command [DMPAFT]: " + ex.Message); cumulus.LogDebugMessage("GetArchiveData: Attempting to reconnect to station"); InitTCP(); cumulus.LogDebugMessage("GetArchiveData: Reconnected to station"); @@ -2145,14 +2146,14 @@ private void GetArchiveData() if (!ack) { - cumulus.LogMessage("GetArchiveData: No Ack in response to DMPAFT, giving up"); + cumulus.LogErrorMessage("GetArchiveData: No Ack in response to DMPAFT, giving up"); return; } cumulus.LogMessage("GetArchiveData: Received response to DMPAFT, sending start date and time"); // Construct date time string to send next - byte[] data = {(byte) (vantageDateStamp%256), (byte) (vantageDateStamp/256), (byte) (vantageTimeStamp%256), (byte) (vantageTimeStamp/256), 0, 0}; + byte[] data = { (byte) (vantageDateStamp % 256), (byte) (vantageDateStamp / 256), (byte) (vantageTimeStamp % 256), (byte) (vantageTimeStamp / 256), 0, 0 }; // calculate and insert CRC @@ -2161,8 +2162,8 @@ private void GetArchiveData() Array.Copy(data, datacopy, 4); int crc = calculateCRC(datacopy); - data[4] = (byte) (crc/256); - data[5] = (byte) (crc%256); + data[4] = (byte) (crc / 256); + data[5] = (byte) (crc % 256); cumulus.LogDataMessage("GetArchiveData: Sending: " + BitConverter.ToString(data)); @@ -2174,7 +2175,7 @@ private void GetArchiveData() // wait for the ACK, this can take a while if it is going to dump a large number of records if (!WaitForACK(comport, 5000)) { - cumulus.LogMessage("GetArchiveData: No ACK in response to sending date and time"); + cumulus.LogWarningMessage("GetArchiveData: No ACK in response to sending date and time"); return; } @@ -2203,7 +2204,7 @@ private void GetArchiveData() if (!WaitForACK(stream, 5000)) { - cumulus.LogMessage("GetArchiveData: No ACK in response to sending date and time"); + cumulus.LogWarningMessage("GetArchiveData: No ACK in response to sending date and time"); return; } @@ -2221,8 +2222,8 @@ private void GetArchiveData() } // extract number of pages and offset into first page - int numPages = (data[1]*256) + data[0]; - int offset = (data[3]*256) + data[2]; + int numPages = (data[1] * 256) + data[0]; + int offset = (data[3] * 256) + data[2]; //int bytesToRead = numPages*pageSize; //int dataOffset = (offset*recordSize) + 1; byte[] buff = new byte[pageSize]; @@ -2236,7 +2237,7 @@ private void GetArchiveData() // keep track of how many records processed for percentage display // but there may be some old entries in the last page - int numtodo = (numPages*5) - offset; + int numtodo = (numPages * 5) - offset; int numdone = 0; if (numtodo == 0) @@ -2286,7 +2287,7 @@ private void GetArchiveData() if (tmrComm.timedout) { - cumulus.LogMessage("GetArchiveData: The station has stopped sending archive data, ending attempts"); + cumulus.LogErrorMessage("GetArchiveData: The station has stopped sending archive data, ending attempts"); if (!Program.service) Console.WriteLine(""); // flush the progress line return; @@ -2318,7 +2319,7 @@ private void GetArchiveData() if (responsePasses == 20) { - cumulus.LogMessage("The station has stopped sending archive data"); + cumulus.LogErrorMessage("The station has stopped sending archive data"); if (!Program.service) Console.WriteLine(""); // flush the progress line return; @@ -2343,7 +2344,7 @@ private void GetArchiveData() // if we still got bad data after maxPasses, give up if (badCRC) { - cumulus.LogMessage("GetArchiveData: Bad CRC"); + cumulus.LogWarningMessage("GetArchiveData: Bad CRC"); if (isSerial) comport.Write(ESCstring, 0, 1); else @@ -2494,7 +2495,8 @@ private void GetArchiveData() bearing = 0; } - DoWind(wind, (int)(bearing * 22.5), avgwind, timestamp); + AddValuesToRecentWind(avgwind, avgwind, timestamp.AddMinutes(-interval), timestamp); + DoWind(wind, (int) (bearing * 22.5), avgwind, timestamp); if (ConvertUserWindToMS(WindAverage) < 1.5) { @@ -2507,7 +2509,7 @@ private void GetArchiveData() } // update dominant wind bearing - CalculateDominantWindBearing((int)(bearing * 22.5), WindAverage, interval); + CalculateDominantWindBearing((int) (bearing * 22.5), WindAverage, interval); } DoApparentTemp(timestamp); @@ -2693,7 +2695,7 @@ private void GetArchiveData() numdone++; if (!Program.service) - Console.Write("\r - processed " + ((double)numdone / (double)numtodo).ToString("P0")); + Console.Write("\r - processed " + ((double) numdone / (double) numtodo).ToString("P0")); cumulus.LogMessage(numdone + " archive entries processed"); //bw.ReportProgress(numdone*100/numtodo, "processing"); @@ -2882,12 +2884,12 @@ private bool WakeVP(SerialPort serialPort) return (true); } - cumulus.LogMessage("WakeVP: *** VP2 Not woken"); + cumulus.LogWarningMessage("WakeVP: *** VP2 Not woken"); return (false); } catch (Exception ex) { - cumulus.LogMessage("WakeVP: Error - " + ex); + cumulus.LogErrorMessage("WakeVP: Error - " + ex); return (false); } } @@ -3035,7 +3037,7 @@ private bool WakeVP(TcpClient thePort) Thread.Sleep(1000); } - cumulus.LogMessage("WakeVP: *** Console Not woken"); + cumulus.LogWarningMessage("WakeVP: *** Console Not woken"); return (false); } catch (Exception ex) @@ -3097,7 +3099,7 @@ private void InitSerial() { cumulus.LogConsoleMessage("Error opening serial port - " + ex.Message, ConsoleColor.Red, true); cumulus.LogConsoleMessage("Will retry in 30 seconds..."); - cumulus.LogMessage("InitSerial: Error opening port - " + ex.Message); + cumulus.LogErrorMessage("InitSerial: Error opening port - " + ex.Message); } if (comport == null || !comport.IsOpen) @@ -3135,7 +3137,7 @@ private void InitSerial() { // Read the current byte var ch = comport.ReadByte(); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; } while (comport.BytesToRead > 0 || bytesRead < 8); @@ -3168,7 +3170,7 @@ private void InitSerial() } catch (Exception ex) { - cumulus.LogMessage("InitSerial: Error - " + ex.Message); + cumulus.LogErrorMessage("InitSerial: Error - " + ex.Message); } } @@ -3182,7 +3184,7 @@ private void InitTCP() if (socket != null && socket.Connected) socket.Close(); } - catch {} + catch { } cumulus.LogMessage("InitTCP: Connecting to the station"); @@ -3234,12 +3236,12 @@ private void InitTCP() var ch = stream.ReadByte(); if (idx < buffer1.Length) { - buffer1[idx++] = (byte)ch; + buffer1[idx++] = (byte) ch; } else { Array.Copy(buffer1, 1, buffer2, 0, buffer1.Length); - buffer2[9] = (byte)ch; + buffer2[9] = (byte) ch; Array.Copy(buffer2, buffer1, buffer1.Length); } Thread.Sleep(50); @@ -3264,7 +3266,7 @@ private void InitTCP() } catch (Exception ex) { - cumulus.LogMessage("InitTCP: Error - " + ex.Message); + cumulus.LogErrorMessage("InitTCP: Error - " + ex.Message); } } @@ -3280,7 +3282,7 @@ private bool WaitForOK(SerialPort serialPort) try { // Read the current character - readBuffer.Append((char)serialPort.ReadChar()); + readBuffer.Append((char) serialPort.ReadChar()); } catch (TimeoutException) { @@ -3314,7 +3316,7 @@ private bool WaitForOK(NetworkStream stream) try { // Read the current character - readBuffer.Append((char)stream.ReadByte()); + readBuffer.Append((char) stream.ReadByte()); } catch (System.IO.IOException ex) { @@ -3511,7 +3513,7 @@ private DateTime GetTime() if (!WaitForACK(comport)) { - cumulus.LogMessage("getTime: No ACK"); + cumulus.LogWarningMessage("getTime: No ACK"); return DateTime.MinValue; } @@ -3520,19 +3522,19 @@ private DateTime GetTime() { // Read the current character var ch = comport.ReadChar(); - readBuffer[bytesRead] = (byte)ch; + readBuffer[bytesRead] = (byte) ch; bytesRead++; //cumulus.LogMessage("Received " + ch.ToString("X2")); } while (bytesRead < 8); } catch (TimeoutException) { - cumulus.LogMessage("getTime: Timed out waiting for a response"); + cumulus.LogWarningMessage("getTime: Timed out waiting for a response"); return DateTime.MinValue; } catch (Exception ex) { - cumulus.LogMessage("getTime: Error - " + ex.Message); + cumulus.LogErrorMessage("getTime: Error - " + ex.Message); return DateTime.MinValue; } } @@ -3564,15 +3566,15 @@ private DateTime GetTime() do { // Read the current character - readBuffer[bytesRead] = (byte)stream.ReadByte(); + readBuffer[bytesRead] = (byte) stream.ReadByte(); bytesRead++; - } while (bytesRead < 8) ; + } while (bytesRead < 8); } catch (System.IO.IOException ex) { if (ex.Message.Contains("did not properly respond after a period")) { - cumulus.LogMessage("getTime: Timed out waiting for a response"); + cumulus.LogWarningMessage("getTime: Timed out waiting for a response"); } else { @@ -3591,7 +3593,7 @@ private DateTime GetTime() cumulus.LogDataMessage("getTime: Received - " + BitConverter.ToString(readBuffer.Take(bytesRead).ToArray())); if (bytesRead != 8) { - cumulus.LogMessage("getTime: Expected 8 bytes, got " + bytesRead); + cumulus.LogWarningMessage("getTime: Expected 8 bytes, got " + bytesRead); } // CRC doesn't seem to compute? //else if (!crcOK(buffer)) @@ -3606,7 +3608,7 @@ private DateTime GetTime() } catch (Exception) { - cumulus.LogMessage("getTime: Error in time format"); + cumulus.LogWarningMessage("getTime: Error in time format"); } } return DateTime.MinValue; @@ -3632,7 +3634,7 @@ private void SetTime() // wait for the ACK if (!WaitForACK(comport)) { - cumulus.LogMessage("SetTime: No ACK to SETTIME - Not setting the time"); + cumulus.LogWarningMessage("SetTime: No ACK to SETTIME - Not setting the time"); return; } } @@ -3653,7 +3655,7 @@ private void SetTime() // wait for the ACK if (!WaitForACK(stream)) { - cumulus.LogMessage("SetTime: No ACK to SETTIME - Not setting the time"); + cumulus.LogWarningMessage("SetTime: No ACK to SETTIME - Not setting the time"); return; } } @@ -3669,12 +3671,12 @@ private void SetTime() byte[] writeBuffer = new byte[8]; - writeBuffer[0] = (byte)now.Second; - writeBuffer[1] = (byte)now.Minute; - writeBuffer[2] = (byte)now.Hour; - writeBuffer[3] = (byte)now.Day; - writeBuffer[4] = (byte)now.Month; - writeBuffer[5] = (byte)(now.Year - 1900); + writeBuffer[0] = (byte) now.Second; + writeBuffer[1] = (byte) now.Minute; + writeBuffer[2] = (byte) now.Hour; + writeBuffer[3] = (byte) now.Day; + writeBuffer[4] = (byte) now.Month; + writeBuffer[5] = (byte) (now.Year - 1900); // calculate and insert CRC @@ -3683,8 +3685,8 @@ private void SetTime() Array.Copy(writeBuffer, datacopy, 6); int crc = calculateCRC(datacopy); - writeBuffer[6] = (byte)(crc / 256); - writeBuffer[7] = (byte)(crc % 256); + writeBuffer[6] = (byte) (crc / 256); + writeBuffer[7] = (byte) (crc % 256); try { @@ -3703,7 +3705,7 @@ private void SetTime() } else { - cumulus.LogMessage("SetTime: Error, console time set failed"); + cumulus.LogWarningMessage("SetTime: Error, console time set failed"); } } else if (stream != null) @@ -3716,7 +3718,7 @@ private void SetTime() } else { - cumulus.LogMessage("SetTime: Error, console time set failed"); + cumulus.LogWarningMessage("SetTime: Error, console time set failed"); } } } diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs index 4562ba9e..082b1ac6 100644 --- a/CumulusMX/DavisWllStation.cs +++ b/CumulusMX/DavisWllStation.cs @@ -1,21 +1,22 @@ -using ServiceStack; -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Linq; using System.Net; +using System.Net.Http; using System.Net.Sockets; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Timers; -using Tmds.MDns; + +using ServiceStack; + using Swan; -using System.Web; -using System.Net.Http; -using ServiceStack.Text; + +using Tmds.MDns; namespace CumulusMX { @@ -128,7 +129,7 @@ public DavisWllStation(Cumulus cumulus) : base(cumulus) if (string.IsNullOrEmpty(cumulus.WllApiKey) && string.IsNullOrEmpty(cumulus.WllApiSecret)) { // The basic API details have not been supplied - cumulus.LogMessage("WLL - No WeatherLink.com API configuration supplied, just going to work locally"); + cumulus.LogWarningMessage("WLL - No WeatherLink.com API configuration supplied, just going to work locally"); cumulus.LogMessage("WLL - Cannot start historic downloads or retrieve health data"); cumulus.LogConsoleMessage("*** No WeatherLink.com API details supplied. Cannot start historic downloads or retrieve health data", ConsoleColor.DarkCyan); useWeatherLinkDotCom = false; @@ -138,12 +139,12 @@ public DavisWllStation(Cumulus cumulus) : base(cumulus) // One of the API details is missing if (string.IsNullOrEmpty(cumulus.WllApiKey)) { - cumulus.LogMessage("WLL - Missing WeatherLink.com API Key"); + cumulus.LogWarningMessage("WLL - Missing WeatherLink.com API Key"); cumulus.LogConsoleMessage("*** Missing WeatherLink.com API Key. Cannot start historic downloads or retrieve health data", ConsoleColor.Yellow); } else { - cumulus.LogMessage("WLL - Missing WeatherLink.com API Secret"); + cumulus.LogWarningMessage("WLL - Missing WeatherLink.com API Secret"); cumulus.LogConsoleMessage("*** Missing WeatherLink.com API Secret. Cannot start historic downloads or retrieve health data", ConsoleColor.Yellow); } useWeatherLinkDotCom = false; @@ -174,7 +175,7 @@ public DavisWllStation(Cumulus cumulus) : base(cumulus) if (useWeatherLinkDotCom && cumulus.WllStationId < 10) { // API details supplied, but Station Id is still invalid - do not start the station up. - cumulus.LogMessage("WLL - The WeatherLink.com API is enabled, but no Station Id has been configured, not starting the station. Please correct this and restart Cumulus"); + cumulus.LogErrorMessage("WLL - The WeatherLink.com API is enabled, but no Station Id has been configured, not starting the station. Please correct this and restart Cumulus"); cumulus.LogConsoleMessage("The WeatherLink.com API is enabled, but no Station Id has been configured. Please correct this and restart Cumulus", ConsoleColor.Yellow); return; } @@ -451,7 +452,7 @@ private async void GetWllRealtime(object source, ElapsedEventArgs e) } else { - cumulus.LogMessage($"GetWllRealtime: Invalid IP address: {ip}"); + cumulus.LogErrorMessage($"GetWllRealtime: Invalid IP address: {ip}"); } retry = 0; } @@ -524,13 +525,13 @@ private async void GetWllCurrent(object source, ElapsedEventArgs e) // less chatty, only ouput the error on the third attempt if (retry == 3) { - cumulus.LogMessage("GetWllCurrent: Error processing WLL response"); + cumulus.LogErrorMessage("GetWllCurrent: Error processing WLL response"); ex = Utils.GetOriginalException(ex); - cumulus.LogMessage($"GetWllCurrent: Base exception - {ex.Message}"); + cumulus.LogErrorMessage($"GetWllCurrent: Base exception - {ex.Message}"); if (!DataStopped) { - cumulus.LogMessage($"ERROR: No current data received from the WLL, DataStopped triggered"); + cumulus.LogErrorMessage($"ERROR: No current data received from the WLL, DataStopped triggered"); DataStopped = true; DataStoppedTime = DateTime.Now; @@ -552,7 +553,7 @@ private async void GetWllCurrent(object source, ElapsedEventArgs e) } else { - cumulus.LogMessage($"GetWllCurrent: Invalid IP address: {ip}"); + cumulus.LogErrorMessage($"GetWllCurrent: Invalid IP address: {ip}"); } } @@ -591,7 +592,7 @@ private void DecodeBroadcast(string broadcastJson, IPEndPoint from) } catch (Exception ex) { - cumulus.LogMessage($"WLL broadcast: Error in wind speed found on TxId {rec.txid}"); + cumulus.LogErrorMessage($"WLL broadcast: Error in wind speed found on TxId {rec.txid}"); cumulus.LogDebugMessage($"WLL broadcast: Exception: {ex.Message}"); } } @@ -627,7 +628,7 @@ private void DecodeBroadcast(string broadcastJson, IPEndPoint from) } catch (Exception ex) { - cumulus.LogMessage($"WLL broadcast: no valid rainfall found on TxId {rec.txid}"); + cumulus.LogWarningMessage($"WLL broadcast: no valid rainfall found on TxId {rec.txid}"); cumulus.LogDebugMessage($"WLL broadcast: Exception: {ex.Message}"); } } @@ -645,14 +646,14 @@ private void DecodeBroadcast(string broadcastJson, IPEndPoint from) } else if (broadcastJson.StartsWith("STR_BCAST")) { - var msg = broadcastJson.Replace(((char)0x00), '.').Replace(((char) 0x1c), '.'); - cumulus.LogMessage($"WLL broadcast: Received spurious message from printer utility(?) at IP address {from.Address.ToString()} starting with \"STR_BCAST\""); + var msg = broadcastJson.Replace(((char) 0x00), '.').Replace(((char) 0x1c), '.'); + cumulus.LogWarningMessage($"WLL broadcast: Received spurious message from printer utility(?) at IP address {from.Address.ToString()} starting with \"STR_BCAST\""); cumulus.LogDataMessage("WLL broadcast: Message = " + msg); } else { multicastsBad++; - var msg = string.Format("WLL broadcast: Invalid payload in message from {3}. Percentage good packets {0:F2}% - ({1},{2})", (multicastsGood / (float)(multicastsBad + multicastsGood) * 100), multicastsBad, multicastsGood, from.Address.ToString()); + var msg = string.Format("WLL broadcast: Invalid payload in message from {3}. Percentage good packets {0:F2}% - ({1},{2})", (multicastsGood / (float) (multicastsBad + multicastsGood) * 100), multicastsBad, multicastsGood, from.Address.ToString()); cumulus.LogMessage(msg); cumulus.LogMessage("WLL broadcast: Received: " + broadcastJson); } @@ -662,8 +663,8 @@ private void DecodeBroadcast(string broadcastJson, IPEndPoint from) cumulus.LogDebugMessage("DecodeBroadcast(): Exception Caught!"); cumulus.LogDebugMessage("Message :" + exp.Message); multicastsBad++; - var msg = string.Format("WLL broadcast: Error processing broadcast. Percentage good packets {0:F2}% - ({1},{2})", (multicastsGood / (float)(multicastsBad + multicastsGood) * 100), multicastsBad, multicastsGood); - cumulus.LogMessage(msg); + var msg = string.Format("WLL broadcast: Error processing broadcast. Percentage good packets {0:F2}% - ({1},{2})", (multicastsGood / (float) (multicastsBad + multicastsGood) * 100), multicastsBad, multicastsGood); + cumulus.LogErrorMessage(msg); cumulus.LogMessage($"WLL broadcast: Received from {from.Address.ToString()}: " + broadcastJson); } } @@ -704,7 +705,7 @@ private void DecodeCurrent(string currentJson) if (data1.rx_state == 2) { localSensorContactLost = true; - cumulus.LogMessage($"Warning: Sensor contact lost TxId {data1.txid}; ignoring data from this ISS"); + cumulus.LogWarningMessage($"Warning: Sensor contact lost TxId {data1.txid}; ignoring data from this ISS"); continue; } @@ -751,7 +752,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing temperature values on TxId {data1.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing temperature values on TxId {data1.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -784,7 +785,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing Extra temperature/humidity values on TxId {data1.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing Extra temperature/humidity values on TxId {data1.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -824,7 +825,6 @@ private void DecodeCurrent(string currentJson) else currentAvgWindSpd = ConvertWindMPHToUser(data1.wind_speed_avg_last_10_min ?? 0); - // pesky null values from WLL when it is calm int wdir = data1.wind_dir_last ?? 0; double wind = ConvertWindMPHToUser(data1.wind_speed_last ?? 0); @@ -836,32 +836,37 @@ private void DecodeCurrent(string currentJson) CalcRecentMaxGust = true; } - if (cumulus.StationOptions.PeakGustMinutes >= 2) + double gust; + int gustDir; + if (cumulus.StationOptions.PeakGustMinutes <= 10) + { + gust = ConvertWindMPHToUser(data1.wind_speed_hi_last_2_min ?? 0); + gustDir = data1.wind_dir_at_hi_speed_last_2_min ?? 0; + } + else { - var gust = ConvertWindMPHToUser(data1.wind_speed_hi_last_2_min ?? 0); - var gustCal = cumulus.Calib.WindGust.Calibrate(gust); - var gustDir = data1.wind_dir_at_hi_speed_last_2_min ?? 0; - var gustDirCal = gustDir == 0 ? 0 : (int) cumulus.Calib.WindDir.Calibrate(gustDir); + gust = ConvertWindMPHToUser(data1.wind_speed_hi_last_10_min ?? 0); + gustDir = data1.wind_dir_at_hi_speed_last_10_min ?? 0; + } - // See if the current speed is higher than the current max - // We can then update the figure before the next data packet is read + var gustCal = cumulus.Calib.WindGust.Calibrate(gust); + var gustDirCal = gustDir == 0 ? 0 : (int) cumulus.Calib.WindDir.Calibrate(gustDir); - cumulus.LogDebugMessage($"WLL current: Checking recent gust using wind data from TxId {data1.txid}"); + // See if the current speed is higher than the current max + // We can then update the figure before the next data packet is read - if (gustCal > HiLoToday.HighGust) - { - // Check for spikes, and set highs - if (CheckHighGust(gustCal, gustDirCal, dateTime)) - { - cumulus.LogDebugMessage("Setting max gust from current value: " + gustCal.ToString(cumulus.WindFormat) + " was: " + RecentMaxGust.ToString(cumulus.WindFormat)); - RecentMaxGust = gustCal; - } - } + cumulus.LogDebugMessage($"WLL current: Checking recent gust using wind data from TxId {data1.txid}"); + + // Check for spikes, and set highs + if (CheckHighGust(gustCal, gustDirCal, dateTime)) + { + cumulus.LogDebugMessage("Setting max gust from current value: " + gustCal.ToString(cumulus.WindFormat) + " was: " + RecentMaxGust.ToString(cumulus.WindFormat)); + RecentMaxGust = gustCal; } } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing wind speeds on TxId {data1.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing wind speeds on TxId {data1.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -923,6 +928,11 @@ private void DecodeCurrent(string currentJson) cumulus.WriteIniFile(); } break; + + default: + cumulus.LogErrorMessage($"Error: Unknown Davis rain tipper size defined in data = {data1.rain_size.Value}"); + break; + } } @@ -971,7 +981,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing rain storm values on TxId {data1.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing rain storm values on TxId {data1.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -986,7 +996,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing UV value on TxId {data1.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing UV value on TxId {data1.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1000,7 +1010,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing Solar value on TxId {data1.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing Solar value on TxId {data1.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1034,7 +1044,7 @@ private void DecodeCurrent(string currentJson) if (data2.rx_state == 2) { localSensorContactLost = true; - cumulus.LogMessage($"Warning: Sensor contact lost TxId {data2.txid}; ignoring data from this Leaf/Soil transmitter"); + cumulus.LogWarningMessage($"Warning: Sensor contact lost TxId {data2.txid}; ignoring data from this Leaf/Soil transmitter"); continue; } @@ -1046,21 +1056,21 @@ private void DecodeCurrent(string currentJson) if (cumulus.WllExtraLeafTx1 == data2.txid) { idx = "wet_leaf_" + cumulus.WllExtraLeafIdx1; - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoLeafWetness(val.Value, 1); } if (cumulus.WllExtraLeafTx2 == data2.txid) { idx = "wet_leaf_" + cumulus.WllExtraLeafIdx2; - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoLeafWetness(val.Value, 2); } } catch (Exception e) { - cumulus.LogMessage($"WLL current: Error processing LeafWetness txid={data2.txid}, idx={idx}"); + cumulus.LogErrorMessage($"WLL current: Error processing LeafWetness txid={data2.txid}, idx={idx}"); cumulus.LogDebugMessage($"WLL current: Exception: {e.Message}"); } @@ -1070,13 +1080,13 @@ private void DecodeCurrent(string currentJson) idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx1; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilMoisture(val.Value, 1); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx1} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx1} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1085,13 +1095,13 @@ private void DecodeCurrent(string currentJson) idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx2; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilMoisture(val.Value, 2); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx2} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx2} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1100,13 +1110,13 @@ private void DecodeCurrent(string currentJson) idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx3; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilMoisture(val.Value, 3); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx3} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx3} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1115,13 +1125,13 @@ private void DecodeCurrent(string currentJson) idx = "moist_soil_" + cumulus.WllExtraSoilMoistureIdx4; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilMoisture(val.Value, 4); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx4} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing soil moisture #{cumulus.WllExtraSoilMoistureIdx4} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1132,13 +1142,13 @@ private void DecodeCurrent(string currentJson) idx = "temp_" + cumulus.WllExtraSoilTempIdx1; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilTemp(ConvertTempFToUser(val.Value), 1); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx1} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx1} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1147,13 +1157,13 @@ private void DecodeCurrent(string currentJson) idx = "temp_" + cumulus.WllExtraSoilTempIdx2; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilTemp(ConvertTempFToUser(val.Value), 2); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx2} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx2} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1162,13 +1172,13 @@ private void DecodeCurrent(string currentJson) idx = "temp_" + cumulus.WllExtraSoilTempIdx3; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilTemp(ConvertTempFToUser(val.Value), 3); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx3} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx3} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1177,13 +1187,13 @@ private void DecodeCurrent(string currentJson) idx = "temp_" + cumulus.WllExtraSoilTempIdx4; try { - var val = (double?)data2[idx]; + var val = (double?) data2[idx]; if (val.HasValue) DoSoilTemp(ConvertTempFToUser(val.Value), 4); } catch (Exception ex) { - cumulus.LogMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx4} on TxId {data2.txid}"); + cumulus.LogErrorMessage($"WLL current: Error processing extra soil temp #{cumulus.WllExtraSoilTempIdx4} on TxId {data2.txid}"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } } @@ -1218,7 +1228,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage("WLL current: Error processing baro data"); + cumulus.LogErrorMessage("WLL current: Error processing baro data"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } @@ -1244,7 +1254,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage("WLL current: Error processing indoor temp data"); + cumulus.LogErrorMessage("WLL current: Error processing indoor temp data"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } @@ -1255,7 +1265,7 @@ private void DecodeCurrent(string currentJson) } catch (Exception ex) { - cumulus.LogMessage("WLL current: Error processing indoor humidity data"); + cumulus.LogErrorMessage("WLL current: Error processing indoor humidity data"); cumulus.LogDebugMessage($"WLL current: Exception: {ex.Message}"); } @@ -1396,7 +1406,7 @@ private static bool CheckIpValid(string strIp) return arrOctets.All(strOctet => byte.TryParse(strOctet, out result)); } - private void SetTxBatteryStatus(int txId, uint status) + private void SetTxBatteryStatus(int txId, int status) { // Split the string var delimiters = new[] { ' ', '-' }; @@ -1440,7 +1450,7 @@ private void bw_ReadHistoryCompleted(object sender, RunWorkerCompletedEventArgs cumulus.LogMessage("WLL history: WeatherLink API archive reading thread completed"); if (e.Error != null) { - cumulus.LogMessage("WLL history: Archive reading thread apparently terminated with an error: " + e.Error.Message); + cumulus.LogErrorMessage("WLL history: Archive reading thread apparently terminated with an error: " + e.Error.Message); } cumulus.LogMessage("WLL history: Updating highs and lows"); //using (cumulusEntities dataContext = new cumulusEntities()) @@ -1502,7 +1512,7 @@ private void bw_ReadHistory(object sender, DoWorkEventArgs e) } catch (Exception ex) { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); } // force a calculation of the current gust and average wind speed so we do not get a zero values at startup @@ -1527,7 +1537,7 @@ private void GetWlHistoricData(BackgroundWorker worker) if (cumulus.WllStationId < 10) { const string msg = "No WeatherLink API station ID in the configuration"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); cumulus.LogConsoleMessage("GetWlHistoricData: " + msg); } @@ -1585,7 +1595,7 @@ private void GetWlHistoricData(BackgroundWorker worker) using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDebugMessage($"GetWlHistoricData: WeatherLink API Historic Response code: {responseCode}"); cumulus.LogDataMessage($"GetWlHistoricData: WeatherLink API Historic Response: {responseBody}"); } @@ -1593,23 +1603,23 @@ private void GetWlHistoricData(BackgroundWorker worker) if (responseCode != 200) { var historyError = responseBody.FromJson(); - cumulus.LogMessage($"GetWlHistoricData: WeatherLink API Historic Error: {historyError.code}, {historyError.message}"); + cumulus.LogWarningMessage($"GetWlHistoricData: WeatherLink API Historic Error: {historyError.code}, {historyError.message}"); cumulus.LogConsoleMessage($" - Error {historyError.code}: {historyError.message}", ConsoleColor.Red); cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); return; } - histObj = responseBody.FromJson(); - if (responseBody == "{}") { - cumulus.LogMessage("GetWlHistoricData: WeatherLink API Historic: No data was returned. Check your Device Id."); + cumulus.LogWarningMessage("GetWlHistoricData: WeatherLink API Historic: No data was returned. Check your Device Id."); cumulus.LogConsoleMessage(" - No historic data available"); cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); return; } else if (responseBody.StartsWith("{\"")) // basic sanity check { + histObj = responseBody.FromJson(); + // get the sensor data int idxOfSensorWithMostRecs = 0; for (var i = 0; i < histObj.sensors.Count; i++) @@ -1641,7 +1651,7 @@ private void GetWlHistoricData(BackgroundWorker worker) } else // No idea what we got, dump it to the log { - cumulus.LogMessage("GetWlHistoricData: Invalid historic message received"); + cumulus.LogErrorMessage("GetWlHistoricData: Invalid historic message received"); cumulus.LogMessage("GetWlHistoricData: Received: " + responseBody); cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); return; @@ -1649,7 +1659,7 @@ private void GetWlHistoricData(BackgroundWorker worker) } catch (Exception ex) { - cumulus.LogMessage("GetWlHistoricData: Exception: " + ex.Message); + cumulus.LogErrorMessage("GetWlHistoricData: Exception: " + ex.Message); if (ex.InnerException != null) { ex = Utils.GetOriginalException(ex); @@ -1674,6 +1684,8 @@ private void GetWlHistoricData(BackgroundWorker worker) var refData = sensorWithMostRecs.data[dataIndex].FromJsv(); var timestamp = Utils.FromUnixTime(refData.ts); + cumulus.LogMessage($"GetWlHistoricData: Processing record {timestamp:yyyy-MM-dd HH:mm}"); + var h = timestamp.Hour; // if outside roll-over hour, roll-over yet to be done @@ -1835,12 +1847,12 @@ private void GetWlHistoricData(BackgroundWorker worker) if (!Program.service) - Console.Write("\r - processed " + (((double)dataIndex + 1) / noOfRecs).ToString("P0")); + Console.Write("\r - processed " + (((double) dataIndex + 1) / noOfRecs).ToString("P0")); cumulus.LogMessage($"GetWlHistoricData: {dataIndex + 1} of {noOfRecs} archive entries processed"); } catch (Exception ex) { - cumulus.LogMessage("GetWlHistoricData: Exception: " + ex.Message); + cumulus.LogErrorMessage("GetWlHistoricData: Exception: " + ex.Message); } } @@ -1903,7 +1915,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Humidity (high) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Humidity (high) data on TxId {data11.tx_id}"); } // do low humidity @@ -1914,7 +1926,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Humidity (low) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Humidity (low) data on TxId {data11.tx_id}"); } if (data11.hum_last != null) @@ -1924,12 +1936,12 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Humidity data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Humidity data on TxId {data11.tx_id}"); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing Primary humidity value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing Primary humidity value on TxId {data11.tx_id}. Error: {ex.Message}"); } // do temperature after humidity as DoOutdoorTemp contains dewpoint calculation (if user selected) @@ -1937,7 +1949,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { if (data11.temp_last == -99) { - cumulus.LogMessage($"WL.com historic: Warning, no valid Primary temperature value found [-99] on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Primary temperature value found [-99] on TxId {data11.tx_id}"); } else { @@ -1947,28 +1959,28 @@ private void DecodeHistoric(int dataType, int sensorType, string json) if (data11.temp_hi_at != 0 && data11.temp_hi != null) { ts = Utils.FromUnixTime(data11.temp_hi_at); - DoOutdoorTemp(ConvertTempFToUser((double)data11.temp_hi), ts); + DoOutdoorTemp(ConvertTempFToUser((double) data11.temp_hi), ts); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Temperature (high) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Temperature (high) data on TxId {data11.tx_id}"); } // do low temp if (data11.temp_lo_at != 0 && data11.temp_lo != null) { ts = Utils.FromUnixTime(data11.temp_lo_at); - DoOutdoorTemp(ConvertTempFToUser((double)data11.temp_lo), ts); + DoOutdoorTemp(ConvertTempFToUser((double) data11.temp_lo), ts); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Temperature (low) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Temperature (low) data on TxId {data11.tx_id}"); } // do last temp if (data11.temp_last != null) { - DoOutdoorTemp(ConvertTempFToUser((double)data11.temp_last), recordTs); + DoOutdoorTemp(ConvertTempFToUser((double) data11.temp_last), recordTs); // set the values for daily average, arch_int is in seconds, but always whole minutes tempsamplestoday += data11.arch_int / 60; @@ -1986,13 +1998,13 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Temperature data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Temperature data on TxId {data11.tx_id}"); } } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing Primary temperature value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing Primary temperature value on TxId {data11.tx_id}. Error: {ex.Message}"); } @@ -2002,37 +2014,37 @@ private void DecodeHistoric(int dataType, int sensorType, string json) if (data11.dew_point_hi_at != 0 && data11.dew_point_hi != null) { ts = Utils.FromUnixTime(data11.dew_point_hi_at); - DoOutdoorDewpoint(ConvertTempFToUser((double)data11.dew_point_hi), ts); + DoOutdoorDewpoint(ConvertTempFToUser((double) data11.dew_point_hi), ts); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Dew Point (high) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Dew Point (high) data on TxId {data11.tx_id}"); } // do low DP if (data11.dew_point_lo_at != 0 && data11.dew_point_lo != null) { ts = Utils.FromUnixTime(data11.dew_point_lo_at); - DoOutdoorDewpoint(ConvertTempFToUser((double)data11.dew_point_lo), ts); + DoOutdoorDewpoint(ConvertTempFToUser((double) data11.dew_point_lo), ts); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Dew Point (low) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Dew Point (low) data on TxId {data11.tx_id}"); } // do last DP if (data11.dew_point_last != null) { - DoOutdoorDewpoint(ConvertTempFToUser((double)data11.dew_point_last), recordTs); + DoOutdoorDewpoint(ConvertTempFToUser((double) data11.dew_point_last), recordTs); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Dew Point data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Dew Point data on TxId {data11.tx_id}"); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing dew point value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing dew point value on TxId {data11.tx_id}. Error: {ex.Message}"); } if (!cumulus.StationOptions.CalculatedWC) @@ -2044,26 +2056,26 @@ private void DecodeHistoric(int dataType, int sensorType, string json) if (data11.wind_chill_lo_at != 0 && data11.wind_chill_lo != null) { ts = Utils.FromUnixTime(data11.wind_chill_lo_at); - DoWindChill(ConvertTempFToUser((double)data11.wind_chill_lo), ts); + DoWindChill(ConvertTempFToUser((double) data11.wind_chill_lo), ts); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Wind Chill (low) data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Wind Chill (low) data on TxId {data11.tx_id}"); } // do last WC if (data11.wind_chill_last != null) { - DoWindChill(ConvertTempFToUser((double)data11.wind_chill_last), recordTs); + DoWindChill(ConvertTempFToUser((double) data11.wind_chill_last), recordTs); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Wind Chill data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Wind Chill data on TxId {data11.tx_id}"); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing wind chill value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing wind chill value on TxId {data11.tx_id}. Error: {ex.Message}"); } } } @@ -2083,12 +2095,12 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { cumulus.LogDebugMessage($"WL.com historic: using extra temp data from TxId {data11.tx_id}"); - DoExtraTemp(ConvertTempFToUser((double)data11.temp_last), tempTxId); + DoExtraTemp(ConvertTempFToUser((double) data11.temp_last), tempTxId); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing extra temp value on TxId {data11.tx_id}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing extra temp value on TxId {data11.tx_id}"); cumulus.LogDebugMessage($"WL.com historic: Exception {ex.Message}"); } @@ -2098,12 +2110,12 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { if (data11.hum_last != null) { - DoExtraHum((double)data11.hum_last, tempTxId); + DoExtraHum((double) data11.hum_last, tempTxId); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing extra humidity value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing extra humidity value on TxId {data11.tx_id}. Error: {ex.Message}"); } } } @@ -2130,6 +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); } else { @@ -2138,7 +2151,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) if (data11.wind_speed_avg != null) { - WindAverage = cumulus.Calib.WindSpeed.Calibrate(ConvertWindMPHToUser((double)data11.wind_speed_avg)); + WindAverage = cumulus.Calib.WindSpeed.Calibrate(ConvertWindMPHToUser((double) data11.wind_speed_avg)); // add in 'archivePeriod' minutes worth of wind speed to windrun int interval = data11.arch_int / 60; @@ -2151,7 +2164,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing wind values on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing wind values on TxId {data11.tx_id}. Error: {ex.Message}"); } } @@ -2180,8 +2193,8 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { cumulus.LogDebugMessage($"WL.com historic: using rain data from TxId {data11.tx_id}"); - var rain = ConvertRainClicksToUser((double)data11.rainfall_clicks, data11.rain_size); - var rainrate = ConvertRainClicksToUser((double)data11.rain_rate_hi_clicks, data11.rain_size); + var rain = ConvertRainClicksToUser((double) data11.rainfall_clicks, data11.rain_size); + var rainrate = ConvertRainClicksToUser((double) data11.rain_rate_hi_clicks, data11.rain_size); if (rain > 0) { cumulus.LogDebugMessage($"WL.com historic: Adding rain {rain.ToString(cumulus.RainFormat)}"); @@ -2197,12 +2210,12 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Rain data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Rain data on TxId {data11.tx_id}"); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing rain data on TxId {data11.tx_id}. Error:{ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing rain data on TxId {data11.tx_id}. Error:{ex.Message}"); } } @@ -2224,16 +2237,16 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { cumulus.LogDebugMessage($"WL.com historic: using UV data from TxId {data11.tx_id}"); - DoUV((double)data11.uv_index_avg, recordTs); + DoUV((double) data11.uv_index_avg, recordTs); } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid UV data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid UV data on TxId {data11.tx_id}"); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing UV value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing UV value on TxId {data11.tx_id}. Error: {ex.Message}"); } } @@ -2256,7 +2269,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) if (data11.solar_rad_avg != null) { cumulus.LogDebugMessage($"WL.com historic: using solar data from TxId {data11.tx_id}"); - DoSolarRad((int)data11.solar_rad_avg, recordTs); + DoSolarRad((int) data11.solar_rad_avg, recordTs); // add in archive period worth of sunshine, if sunny - arch_int in seconds if ((SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100.00) && (SolarRad >= cumulus.SolarOptions.SolarMinimum)) @@ -2266,7 +2279,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage($"WL.com historic: Warning, no valid Solar data on TxId {data11.tx_id}"); + cumulus.LogWarningMessage($"WL.com historic: Warning, no valid Solar data on TxId {data11.tx_id}"); } if (data11.et != null && !cumulus.StationOptions.CalculatedET) @@ -2275,14 +2288,14 @@ private void DecodeHistoric(int dataType, int sensorType, string json) // The number is the total for the one hour period. // This is unlike the existing VP2 when the ET is an annual running total // So we try and mimic the VP behaviour - var newET = AnnualETTotal + ConvertRainINToUser((double)data11.et); - cumulus.LogDebugMessage($"WLL DecodeHistoric: Adding {ConvertRainINToUser((double)data11.et):F3} to ET"); + var newET = AnnualETTotal + ConvertRainINToUser((double) data11.et); + cumulus.LogDebugMessage($"WLL DecodeHistoric: Adding {ConvertRainINToUser((double) data11.et):F3} to ET"); DoET(newET, recordTs); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing Solar value on TxId {data11.tx_id}. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing Solar value on TxId {data11.tx_id}. Error: {ex.Message}"); } } @@ -2323,18 +2336,18 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { idx = "wet_leaf_last_" + cumulus.WllExtraLeafIdx1; if (data13[idx] != null) - DoLeafWetness((double)data13[idx], 1); + DoLeafWetness((double) data13[idx], 1); } if (cumulus.WllExtraLeafTx2 == data13.tx_id) { idx = "wet_leaf_last_" + cumulus.WllExtraLeafIdx2; if (data13[idx] != null) - DoLeafWetness((double)data13[idx], 2); + DoLeafWetness((double) data13[idx], 2); } } catch (Exception e) { - cumulus.LogMessage($"Error, DecodeHistoric, LeafWetness txid={data13.tx_id}, idx={idx}: {e.Message}"); + cumulus.LogErrorMessage($"Error, DecodeHistoric, LeafWetness txid={data13.tx_id}, idx={idx}: {e.Message}"); } /* * Soil Moisture @@ -2376,7 +2389,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilMoisture((double)data13[idx], 1); + DoSoilMoisture((double) data13[idx], 1); } } if (cumulus.WllExtraSoilMoistureTx2 == data13.tx_id) @@ -2388,7 +2401,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilMoisture((double)data13[idx], 2); + DoSoilMoisture((double) data13[idx], 2); } } if (cumulus.WllExtraSoilMoistureTx3 == data13.tx_id) @@ -2400,7 +2413,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilMoisture((double)data13[idx], 3); + DoSoilMoisture((double) data13[idx], 3); } } if (cumulus.WllExtraSoilMoistureTx4 == data13.tx_id) @@ -2412,13 +2425,13 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilMoisture((double)data13[idx], 4); + DoSoilMoisture((double) data13[idx], 4); } } } catch (Exception e) { - cumulus.LogMessage($"Error, DecodeHistoric, SoilMoisture txid={data13.tx_id}, idx={idx}: {e.Message}"); + cumulus.LogErrorMessage($"Error, DecodeHistoric, SoilMoisture txid={data13.tx_id}, idx={idx}: {e.Message}"); } /* @@ -2461,7 +2474,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilTemp(ConvertTempFToUser((double)data13[idx]), 1); + DoSoilTemp(ConvertTempFToUser((double) data13[idx]), 1); } } if (cumulus.WllExtraSoilTempTx2 == data13.tx_id) @@ -2473,7 +2486,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilTemp(ConvertTempFToUser((double)data13[idx]), 2); + DoSoilTemp(ConvertTempFToUser((double) data13[idx]), 2); } } if (cumulus.WllExtraSoilTempTx3 == data13.tx_id) @@ -2485,7 +2498,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilTemp(ConvertTempFToUser((double)data13[idx]), 3); + DoSoilTemp(ConvertTempFToUser((double) data13[idx]), 3); } } if (cumulus.WllExtraSoilTempTx4 == data13.tx_id) @@ -2497,13 +2510,13 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - DoSoilTemp(ConvertTempFToUser((double)data13[idx]), 4); + DoSoilTemp(ConvertTempFToUser((double) data13[idx]), 4); } } } catch (Exception e) { - cumulus.LogMessage($"Error, DecodeHistoric, SoilTemp txid={data13.tx_id}, idx={idx}: {e.Message}"); + cumulus.LogErrorMessage($"Error, DecodeHistoric, SoilTemp txid={data13.tx_id}, idx={idx}: {e.Message}"); } break; @@ -2529,38 +2542,38 @@ private void DecodeHistoric(int dataType, int sensorType, string json) if (data13baro.bar_hi_at != 0 && data13baro.bar_hi != null) { ts = Utils.FromUnixTime(data13baro.bar_hi_at); - DoPressure(ConvertPressINHGToUser((double)data13baro.bar_hi), ts); + DoPressure(ConvertPressINHGToUser((double) data13baro.bar_hi), ts); } else { - cumulus.LogMessage("WL.com historic: Warning, no valid Baro data (high)"); + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data (high)"); } // check the low if (data13baro.bar_lo_at != 0 && data13baro.bar_lo != null) { ts = Utils.FromUnixTime(data13baro.bar_lo_at); - DoPressure(ConvertPressINHGToUser((double)data13baro.bar_lo), ts); + DoPressure(ConvertPressINHGToUser((double) data13baro.bar_lo), ts); } else { - cumulus.LogMessage("WL.com historic: Warning, no valid Baro data (high)"); + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data (high)"); } if (data13baro.bar_sea_level != null) { // leave it at current value ts = Utils.FromUnixTime(data13baro.ts); - DoPressure(ConvertPressINHGToUser((double)data13baro.bar_sea_level), ts); + DoPressure(ConvertPressINHGToUser((double) data13baro.bar_sea_level), ts); } else { - cumulus.LogMessage("WL.com historic: Warning, no valid Baro data (high)"); + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Baro data (high)"); } // Altimeter from absolute if (data13baro.bar_absolute != null) { - StationPressure = ConvertPressINHGToUser((double)data13baro.bar_absolute); + StationPressure = ConvertPressINHGToUser((double) data13baro.bar_absolute); // Or do we use calibration? The VP2 code doesn't? //StationPressure = ConvertPressINHGToUser(data.Value("bar_absolute")) * cumulus.Calib.Press.Mult + cumulus.Calib.Press.Offset; AltimeterPressure = ConvertPressMBToUser(StationToAltimeter(ConvertUserPressureToHPa(StationPressure), AltitudeM(cumulus.Altitude))); @@ -2568,7 +2581,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing baro reading. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing baro reading. Error: {ex.Message}"); } break; @@ -2595,16 +2608,16 @@ private void DecodeHistoric(int dataType, int sensorType, string json) { if (data13temp.temp_in_last != null) { - DoIndoorTemp(ConvertTempFToUser((double)data13temp.temp_in_last)); + DoIndoorTemp(ConvertTempFToUser((double) data13temp.temp_in_last)); } else { - cumulus.LogMessage("WL.com historic: Warning, no valid Inside Temperature"); + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Inside Temperature"); } } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing temp-in reading. Error: {ex.Message}]"); + cumulus.LogErrorMessage($"WL.com historic: Error processing temp-in reading. Error: {ex.Message}]"); } @@ -2616,7 +2629,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } else { - cumulus.LogMessage("WL.com historic: Warning, no valid Inside Humidity"); + cumulus.LogWarningMessage("WL.com historic: Warning, no valid Inside Humidity"); } } catch (Exception ex) @@ -2643,7 +2656,7 @@ private void DecodeHistoric(int dataType, int sensorType, string json) } catch (Exception e) { - cumulus.LogMessage($"Error, DecodeHistoric, DataType={dataType}, SensorType={sensorType}: " + e.Message); + cumulus.LogErrorMessage($"Error, DecodeHistoric, DataType={dataType}, SensorType={sensorType}: " + e.Message); } } @@ -2698,7 +2711,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) try { - var data15 = sensor.data.Last().FromJsv(); + var data15 = sensor.data.Last().FromJsv(); var dat = Utils.FromUnixTime(data15.firmware_version); DavisFirmwareVersion = dat.ToUniversalTime().ToString("yyyy-MM-dd"); @@ -2710,7 +2723,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) if (battV < 5.4) { wllVoltageLow = true; - cumulus.LogMessage($"WLL WARNING: Backup battery voltage is low = {battV:0.##}V"); + cumulus.LogWarningMessage($"WLL WARNING: Backup battery voltage is low = {battV:0.##}V"); } else { @@ -2721,7 +2734,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) ConSupplyVoltageText = inpV.ToString("F2"); if (inpV < 4.0) { - cumulus.LogMessage($"WLL WARNING: Input voltage is low = {inpV:0.##}V"); + cumulus.LogWarningMessage($"WLL WARNING: Input voltage is low = {inpV:0.##}V"); } else { @@ -2729,7 +2742,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) } var upt = TimeSpan.FromSeconds(data15.uptime); var uptStr = string.Format("{0}d:{1:D2}h:{2:D2}m:{3:D2}s", - (int)upt.TotalDays, + (int) upt.TotalDays, upt.Hours, upt.Minutes, upt.Seconds); @@ -2744,7 +2757,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) upt = TimeSpan.FromSeconds(data15.link_uptime); uptStr = string.Format("{0}d:{1:D2}h:{2:D2}m:{3:D2}s", - (int)upt.TotalDays, + (int) upt.TotalDays, upt.Hours, upt.Minutes, upt.Seconds); @@ -2752,7 +2765,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) } catch (Exception ex) { - cumulus.LogMessage($"WL.com historic: Error processing WLL health. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WL.com historic: Error processing WLL health. Error: {ex.Message}"); DavisFirmwareVersion = "???"; } @@ -2787,9 +2800,9 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) if (sensor.sensor_type == 37 || sensor.sensor_type == 84 || sensor.sensor_type == 85) type = "Vue"; else - type= sensor.data_structure_type == 11 ? "ISS" : "Soil/Leaf"; + type = sensor.data_structure_type == 11 ? "ISS" : "Soil/Leaf"; - var data = sensor.data.Last().FromJsv(); + var data = sensor.data.Last().FromJsv(); cumulus.LogDebugMessage($"WLL Health - found health data for {type} device TxId = {data.tx_id}"); @@ -2800,7 +2813,7 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) SetTxBatteryStatus(data.tx_id, data.trans_battery_flag); if (data.trans_battery_flag == 1) { - cumulus.LogMessage($"WLL WARNING: Battery voltage is low in TxId {data.tx_id}"); + cumulus.LogWarningMessage($"WLL WARNING: Battery voltage is low in TxId {data.tx_id}"); } else { @@ -2827,14 +2840,14 @@ private void DecodeWlApiHealth(WlHistorySensor sensor, bool startingup) // The number is the total for the one hour period. // This is unlike the existing VP2 when the ET is an annual running total // So we try and mimic the VP behaviour - var newET = AnnualETTotal + ConvertRainINToUser((double)data.et); - cumulus.LogDebugMessage($"WLL Health: Adding {ConvertRainINToUser((double)data.et):F3} to ET"); + var newET = AnnualETTotal + ConvertRainINToUser((double) data.et); + cumulus.LogDebugMessage($"WLL Health: Adding {ConvertRainINToUser((double) data.et):F3} to ET"); DoET(newET, DateTime.Now); } } catch (Exception ex) { - cumulus.LogMessage($"WLL Health: Error processing transmitter health. Error: {ex.Message}"); + cumulus.LogErrorMessage($"WLL Health: Error processing transmitter health. Error: {ex.Message}"); } } } @@ -2847,7 +2860,7 @@ private void HealthTimerTick(object source, ElapsedEventArgs e) if (DateTime.Now.Minute % 15 == 1) { GetWlHistoricHealth(); - var msg = string.Format("WLL: Percentage good packets received from WLL {0:F2}% - ({1},{2})", (multicastsGood / (float)(multicastsBad + multicastsGood) * 100), multicastsBad, multicastsGood); + var msg = string.Format("WLL: Percentage good packets received from WLL {0:F2}% - ({1},{2})", (multicastsGood / (float) (multicastsBad + multicastsGood) * 100), multicastsBad, multicastsGood); cumulus.LogMessage(msg); } } @@ -2859,7 +2872,7 @@ private void GetWlHistoricHealth() if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) { - cumulus.LogMessage("WLL Health: Missing WeatherLink API data in the cumulus.ini file, aborting!"); + cumulus.LogWarningMessage("WLL Health: Missing WeatherLink API data in the cumulus.ini file, aborting!"); return; } @@ -2867,7 +2880,7 @@ private void GetWlHistoricHealth() { const string msg = "No WeatherLink API station ID in the cumulus.ini file"; cumulus.LogConsoleMessage("GetWlHistoricHealth: " + msg); - cumulus.LogMessage($"WLL Health: {msg}, aborting!"); + cumulus.LogWarningMessage($"WLL Health: {msg}, aborting!"); return; } @@ -2897,14 +2910,14 @@ private void GetWlHistoricHealth() using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDataMessage($"WLL Health: WeatherLink API Response: {responseCode} - {responseBody}"); } if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"WLL Health: WeatherLink API Error: {errObj.code}, {errObj.message}"); + cumulus.LogWarningMessage($"WLL Health: WeatherLink API Error: {errObj.code}, {errObj.message}"); // Get wl.com status GetSystemStatus(); return; @@ -2912,7 +2925,7 @@ private void GetWlHistoricHealth() if (responseBody == "{}") { - cumulus.LogMessage("WLL Health: WeatherLink API: No data was returned. Check your Device Id."); + cumulus.LogWarningMessage("WLL Health: WeatherLink API: No data was returned. Check your Device Id."); cumulus.LastUpdateTime = Utils.FromUnixTime(endTime); // Get wl.com status GetSystemStatus(); @@ -2922,7 +2935,7 @@ private void GetWlHistoricHealth() if (!responseBody.StartsWith("{\"")) // basic sanity check { // No idea what we got, dump it to the log - cumulus.LogMessage("WLL Health: Invalid historic message received"); + cumulus.LogErrorMessage("WLL Health: Invalid historic message received"); cumulus.LogDataMessage("WLL Health: Received: " + responseBody); return; } @@ -3011,13 +3024,13 @@ private void GetWlHistoricHealth() } catch (Exception ex) { - cumulus.LogMessage("WLL Health: exception: " + ex.Message); + cumulus.LogErrorMessage("WLL Health: exception: " + ex.Message); } cumulus.BatteryLowAlarm.Triggered = TxBatText.Contains("LOW") || wllVoltageLow; } catch (Exception ex) { - cumulus.LogMessage("WLL Health: exception: " + ex.Message); + cumulus.LogErrorMessage("WLL Health: exception: " + ex.Message); } } @@ -3030,7 +3043,7 @@ private void GetAvailableStationIds(bool logToConsole = false) if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) { - cumulus.LogMessage("WLLStations: Missing WeatherLink API data in the cumulus.ini file, aborting!"); + cumulus.LogWarningMessage("WLLStations: Missing WeatherLink API data in the cumulus.ini file, aborting!"); return; } @@ -3050,7 +3063,7 @@ private void GetAvailableStationIds(bool logToConsole = false) using (var response = Cumulus.MyHttpClient.SendAsync(request).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; var resp = System.Text.RegularExpressions.Regex.Replace(responseBody, "user_email\":\"[^\"]*\"", "user_email\":\"<>\""); cumulus.LogDebugMessage($"WLLStations: WeatherLink API Response: {responseCode}: {resp}"); } @@ -3058,7 +3071,7 @@ private void GetAvailableStationIds(bool logToConsole = false) if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"WLLStations: WeatherLink API Error: {errObj.code} - {errObj.message}"); + cumulus.LogMessage($"WLLStations: WeatherLink API Error: {errObj.code} - {errObj.message}, Cumulus.LogLevel.Warning"); return; } @@ -3112,13 +3125,13 @@ private void GetAvailableSensors() if (cumulus.WllApiKey == string.Empty || cumulus.WllApiSecret == string.Empty) { - cumulus.LogMessage("GetAvailableSensors: WeatherLink API data is missing in the configuration, aborting!"); + cumulus.LogWarningMessage("GetAvailableSensors: WeatherLink API data is missing in the configuration, aborting!"); return; } if (cumulus.WllStationId < 10) { - cumulus.LogMessage("GetAvailableSensors: No WeatherLink API station ID has been configured, aborting!"); + cumulus.LogWarningMessage("GetAvailableSensors: No WeatherLink API station ID has been configured, aborting!"); return; } @@ -3146,7 +3159,7 @@ private void GetAvailableSensors() if (responseCode != 200) { var errObj = responseBody.FromJson(); - cumulus.LogMessage($"GetAvailableSensors: WeatherLink API Error: {errObj.code} - {errObj.message}"); + cumulus.LogWarningMessage($"GetAvailableSensors: WeatherLink API Error: {errObj.code} - {errObj.message}"); return; } @@ -3203,7 +3216,7 @@ private void BroadcastTimeout(object source, ElapsedEventArgs e) } else { - cumulus.LogMessage($"ERROR: No broadcast data received from the WLL for {tmrBroadcastWatchdog.Interval / 1000} seconds"); + cumulus.LogWarningMessage($"ERROR: No broadcast data received from the WLL for {tmrBroadcastWatchdog.Interval / 1000} seconds"); if (cumulus.WllTriggerDataStoppedOnBroadcast && !DataStopped) { DataStoppedTime = DateTime.Now; @@ -3249,14 +3262,14 @@ private void GetSystemStatus() using (var response = Cumulus.MyHttpClient.GetAsync("https://0886445102835570.hostedstatus.com/1.0/status/600712dea9c1290530967bc6").Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDebugMessage($"GetSystemStatus: WeatherLink.com system status Response code: {responseCode}"); cumulus.LogDataMessage($"GetSystemStatus: WeatherLink.com system status Response: {responseBody}"); } if (responseCode != 200) { - cumulus.LogMessage($"GetSystemStatus: WeatherLink.com system status Error: {responseCode}"); + cumulus.LogWarningMessage($"GetSystemStatus: WeatherLink.com system status Error: {responseCode}"); cumulus.LogConsoleMessage($" - Error {responseCode}"); return; } @@ -3265,7 +3278,7 @@ private void GetSystemStatus() if (responseBody == "{}") { - cumulus.LogMessage("GetSystemStatus: WeatherLink.com system status: No data was returned."); + cumulus.LogWarningMessage("GetSystemStatus: WeatherLink.com system status: No data was returned."); return; } else if (status != null) @@ -3274,7 +3287,7 @@ private void GetSystemStatus() if (status.result.status_overall.status_code != 100) { msg = status.ToString(true); - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); Console.WriteLine(msg); } else @@ -3285,13 +3298,13 @@ private void GetSystemStatus() } else { - cumulus.LogMessage("GetSystemStatus: Something went wrong!"); + cumulus.LogWarningMessage("GetSystemStatus: Something went wrong!"); } } catch (Exception ex) { - cumulus.LogMessage("GetSystemStatus: Exception: " + ex); + cumulus.LogErrorMessage("GetSystemStatus: Exception: " + ex); } return; @@ -3386,7 +3399,7 @@ private class WllCurrentType1 public int? solar_rad { get; set; } public double? uv_index { get; set; } public int? rx_state { get; set; } - public uint? trans_battery_flag { get; set; } + public int? trans_battery_flag { get; set; } public int? rainfall_daily { get; set; } public int? rainfall_monthly { get; set; } public int? rainfall_year { get; set; } @@ -3411,7 +3424,7 @@ private class WllCurrentType2 public double? wet_leaf_1 { get; set; } public double? wet_leaf_2 { get; set; } public int rx_state { get; set; } - public uint? trans_battery_flag { get; set; } + public int? trans_battery_flag { get; set; } public object this[string name] { get diff --git a/CumulusMX/DisplaySettings.cs b/CumulusMX/DisplaySettings.cs index 9eb17da6..08e5fa8e 100644 --- a/CumulusMX/DisplaySettings.cs +++ b/CumulusMX/DisplaySettings.cs @@ -1,33 +1,23 @@ using System; -using System.Collections.Generic; -using System.Drawing; using System.IO; -using System.Linq; using System.Net; -using System.Text; -using System.Threading.Tasks; + using EmbedIO; -using Org.BouncyCastle.Utilities.Collections; + using ServiceStack.Text; -using static Swan.Terminal; + namespace CumulusMX { internal class DisplaySettings { private readonly Cumulus cumulus; - private WeatherStation station; internal DisplaySettings(Cumulus cumulus) { this.cumulus = cumulus; } - internal void SetStation(WeatherStation station) - { - this.station = station; - } - internal string GetAlpacaFormData() { @@ -46,7 +36,7 @@ internal string GetAlpacaFormData() InTemp = cumulus.GraphOptions.Visible.InTemp.Val, HeatIndex = cumulus.GraphOptions.Visible.HeatIndex.Val, DewPoint = cumulus.GraphOptions.Visible.DewPoint.Val, - WindChill =cumulus.GraphOptions.Visible.WindChill.Val, + WindChill = cumulus.GraphOptions.Visible.WindChill.Val, AppTemp = cumulus.GraphOptions.Visible.AppTemp.Val, FeelsLike = cumulus.GraphOptions.Visible.FeelsLike.Val, Humidex = cumulus.GraphOptions.Visible.Humidex.Val, @@ -234,7 +224,7 @@ internal string GetAlpacaFormData() { UV = cumulus.GraphOptions.Colour.UV, Solar = cumulus.GraphOptions.Colour.Solar, - CurrentSolarMax= cumulus.GraphOptions.Colour.SolarTheoretical, + CurrentSolarMax = cumulus.GraphOptions.Colour.SolarTheoretical, Sunshine = cumulus.GraphOptions.Colour.Sunshine }; @@ -357,7 +347,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Station Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Station Data: " + json); context.Response.StatusCode = 500; return msg; @@ -461,7 +451,7 @@ internal string UpdateConfig(IHttpContext context) cumulus.GraphOptions.Colour.MinPress = settings.Graphs.colour.dailypress.Min; cumulus.GraphOptions.Colour.MaxOutHum = settings.Graphs.colour.dailyhum.Max; - cumulus.GraphOptions.Colour.MinOutHum= settings.Graphs.colour.dailyhum.Min; + cumulus.GraphOptions.Colour.MinOutHum = settings.Graphs.colour.dailyhum.Min; cumulus.GraphOptions.Colour.Pm2p5 = settings.Graphs.colour.aq.Pm2p5; cumulus.GraphOptions.Colour.Pm10 = settings.Graphs.colour.aq.Pm10; @@ -482,12 +472,11 @@ internal string UpdateConfig(IHttpContext context) cumulus.GraphOptions.Colour.CO2Sensor.Pm10Avg = settings.Graphs.colour.co2.pm10avg; cumulus.GraphOptions.Colour.CO2Sensor.Temp = settings.Graphs.colour.co2.temp; cumulus.GraphOptions.Colour.CO2Sensor.Hum = settings.Graphs.colour.co2.hum; - } catch (Exception ex) { var msg = "Error processing Graph settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -510,7 +499,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Display Options settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -518,7 +507,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Display settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Display Data: " + json); errorMsg += msg; context.Response.StatusCode = 500; @@ -533,6 +522,7 @@ internal string UpdateConfig(IHttpContext context) cumulus.GraphDataFiles[i].CreateRequired = true; cumulus.GraphDataFiles[i].FtpRequired = true; cumulus.GraphDataFiles[i].CopyRequired = true; + cumulus.GraphDataFiles[i].Incremental = false; } for (var i = 0; i < cumulus.GraphDataEodFiles.Length; i++) { @@ -693,7 +683,7 @@ private class JsonGraphColDailyTemp public string AvgTemp { get; set; } public string MaxTemp { get; set; } public string MinTemp { get; set; } - public string MaxDewPoint{ get; set; } + public string MaxDewPoint { get; set; } public string MinDewPoint { get; set; } public string MaxHeatIndex { get; set; } public string MinWindChill { get; set; } diff --git a/CumulusMX/EasyWeather.cs b/CumulusMX/EasyWeather.cs index 03252d03..431da35a 100644 --- a/CumulusMX/EasyWeather.cs +++ b/CumulusMX/EasyWeather.cs @@ -80,7 +80,7 @@ public EasyWeather(Cumulus cumulus) : base(cumulus) public override void Start() { tmrDataRead.Elapsed += EWGetData; - tmrDataRead.Interval = cumulus.EwOptions.Interval*60*1000; + tmrDataRead.Interval = cumulus.EwOptions.Interval * 60 * 1000; tmrDataRead.Enabled = true; DoDayResetIfNeeded(); @@ -174,7 +174,7 @@ private void EWGetData(object sender, ElapsedEventArgs elapsedEventArgs) if ((lightReading >= 0) && (lightReading <= 300000)) { - DoSolarRad((int)(lightReading * cumulus.SolarOptions.LuxToWM2), now); + DoSolarRad((int) (lightReading * cumulus.SolarOptions.LuxToWM2), now); LightValue = lightReading; } @@ -209,9 +209,10 @@ private void EWGetData(object sender, ElapsedEventArgs elapsedEventArgs) } catch (Exception ex) { - cumulus.LogMessage("Error while processing easyweather file: " + ex.Message); + cumulus.LogErrorMessage("Error while processing easyweather file: " + ex.Message); } - } else + } + else { cumulus.LogDebugMessage("Easyweather file not found"); } diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs index 153aa269..b1ea15dc 100644 --- a/CumulusMX/EcowittApi.cs +++ b/CumulusMX/EcowittApi.cs @@ -1,18 +1,13 @@ using System; using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Net.Http; -using HttpClient = System.Net.Http.HttpClient; -using System.Reactive; using System.Runtime.Serialization; using System.Text; using System.Threading; using System.Threading.Tasks; + + using ServiceStack; using ServiceStack.Text; -using static ServiceStack.Diagnostics.Events; namespace CumulusMX { @@ -22,9 +17,12 @@ internal class EcowittApi private readonly WeatherStation station; 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 int EcowittApiFudgeFactor = 5; // Number of minutes that Ecowitt API data is delayed + private DateTime LastCurrentDataTime = DateTime.MinValue; + public EcowittApi(Cumulus cuml, WeatherStation stn) { cumulus = cuml; @@ -33,7 +31,8 @@ public EcowittApi(Cumulus cuml, WeatherStation stn) //httpClient.DefaultRequestHeaders.ConnectionClose = true; // Let's decode the Unix ts to DateTime - JsConfig.Init(new Config { + JsConfig.Init(new Config + { DateHandler = DateHandler.UnixTime }); @@ -57,13 +56,15 @@ public EcowittApi(Cumulus cuml, WeatherStation stn) internal bool GetHistoricData(DateTime startTime, DateTime endTime, CancellationToken token) { + // DOC: https://doc.ecowitt.net/web/#/apiv3en?page_id=19 + var data = new EcowittHistoricData(); cumulus.LogMessage("API.GetHistoricData: Get Ecowitt Historic Data"); if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) { - cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + cumulus.LogWarningMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); cumulus.LastUpdateTime = DateTime.Now; return false; } @@ -102,7 +103,7 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation break; } sb.Append($"&wind_speed_unitid={windUnit}"); - sb.Append($"&rainfall_unitid={cumulus.Units.Rain + 12}"); + sb.Append($"&rainfall_unitid={cumulus.Units.Rain + 12}"); // 13=inches, 14=mm // available callbacks: // outdoor, indoor, solar_and_uvi, rainfall, wind, pressure, lightning @@ -192,15 +193,15 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation using (var response = Cumulus.MyHttpClient.GetAsync(url).Result) { responseBody = response.Content.ReadAsStringAsync().Result; - responseCode = (int)response.StatusCode; + responseCode = (int) response.StatusCode; cumulus.LogDebugMessage($"API.GetHistoricData: Ecowitt API Historic Response code: {responseCode}"); cumulus.LogDataMessage($"API.GetHistoricData: Ecowitt API Historic Response: {responseBody}"); } if (responseCode != 200) { - var historyError = responseBody.FromJson(); - cumulus.LogMessage($"API.GetHistoricData: Ecowitt API Historic Error: {historyError.code}, {historyError.msg}"); + var historyError = responseBody.FromJson(); + cumulus.LogMessage($"API.GetHistoricData: Ecowitt API Historic Error: {historyError.code}, {historyError.msg}, Cumulus.LogLevel.Warning"); cumulus.LogConsoleMessage($" - Error {historyError.code}: {historyError.msg}", ConsoleColor.Red); cumulus.LastUpdateTime = endTime; return false; @@ -217,7 +218,7 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation else if (responseBody.StartsWith("{\"code\":")) // sanity check { // get the sensor data - var histObj = responseBody.FromJson(); + var histObj = responseBody.FromJson(); if (histObj != null) { @@ -240,7 +241,7 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation } catch (Exception ex) { - cumulus.LogMessage("API.GetHistoricData: Error decoding the response - " + ex.Message); + cumulus.LogErrorMessage("API.GetHistoricData: Error decoding the response - " + ex.Message); cumulus.LastUpdateTime = endTime; return false; } @@ -273,7 +274,7 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation } else // No idea what we got, dump it to the log { - cumulus.LogMessage("API.GetHistoricData: Invalid historic message received"); + cumulus.LogErrorMessage("API.GetHistoricData: Invalid historic message received"); cumulus.LogDataMessage("API.GetHistoricData: Received: " + responseBody); cumulus.LastUpdateTime = endTime; return false; @@ -291,11 +292,10 @@ internal bool GetHistoricData(DateTime startTime, DateTime endTime, Cancellation } catch (Exception ex) { - cumulus.LogMessage("API.GetHistoricData: Exception: " + ex.Message); + cumulus.LogErrorMessage("API.GetHistoricData: Exception: " + ex.Message); cumulus.LastUpdateTime = endTime; return false; } - } private void ProcessHistoryData(EcowittHistoricData data, CancellationToken token) @@ -357,7 +357,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing indoor data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing indoor data. Exception: " + ex.Message); } } // Outdoor Data @@ -437,7 +437,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing outdoor data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing outdoor data. Exception: " + ex.Message); } } // Wind Data @@ -516,7 +516,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing wind data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing wind data. Exception: " + ex.Message); } } // Pressure Data @@ -549,7 +549,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing pressure data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing pressure data. Exception: " + ex.Message); } } // Rainfall Data @@ -656,7 +656,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing rainfall data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing rainfall data. Exception: " + ex.Message); } } // Solar Data @@ -676,12 +676,12 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke if (buffer.ContainsKey(itemDate)) { - buffer[itemDate].Solar = (int)item.Value; + buffer[itemDate].Solar = (int) item.Value; } else { var newItem = new EcowittApi.HistoricData(); - newItem.Solar = (int)item.Value; + newItem.Solar = (int) item.Value; buffer.Add(itemDate, newItem); } } @@ -699,12 +699,12 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke if (buffer.ContainsKey(itemDate)) { - buffer[itemDate].UVI = (int)item.Value; + buffer[itemDate].UVI = (int) item.Value; } else { var newItem = new EcowittApi.HistoricData(); - newItem.UVI = (int)item.Value; + newItem.UVI = (int) item.Value; buffer.Add(itemDate, newItem); } } @@ -712,16 +712,16 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing solar data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing solar data. Exception: " + ex.Message); } } // Extra 8 channel sensors for (var i = 1; i <= 8; i++) { - EcowittApi.EcowittHistoricTempHum srcTH = null; - EcowittApi.EcowittHistoricDataSoil srcSoil = null; - EcowittApi.EcowittHistoricDataTemp srcTemp = null; - EcowittApi.EcowittHistoricDataLeaf srcLeaf = null; + EcowittApi.HistoricTempHum srcTH = null; + EcowittApi.HistoricDataSoil srcSoil = null; + EcowittApi.HistoricDataTemp srcTemp = null; + EcowittApi.HistoricDataLeaf srcLeaf = null; switch (i) { case 1: @@ -827,7 +827,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage($"API.ProcessHistoryData: Error in pre-processing extra T/H data - chan[{i}]. Exception: {ex.Message}"); + cumulus.LogErrorMessage($"API.ProcessHistoryData: Error in pre-processing extra T/H data - chan[{i}]. Exception: {ex.Message}"); } } @@ -859,7 +859,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage($"API.ProcessHistoryData: Error in pre-processing extra soil moisture data - chan[{i}]. Exception: {ex.Message}"); + cumulus.LogErrorMessage($"API.ProcessHistoryData: Error in pre-processing extra soil moisture data - chan[{i}]. Exception: {ex.Message}"); } // User Temp Data try @@ -889,7 +889,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage($"API.ProcessHistoryData: Error in pre-processing extra user temp data - chan[{i}]. Exception: {ex.Message}"); + cumulus.LogErrorMessage($"API.ProcessHistoryData: Error in pre-processing extra user temp data - chan[{i}]. Exception: {ex.Message}"); } // Leaf Wetness Data try @@ -919,7 +919,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage($"API.ProcessHistoryData: Error in pre-processing extra leaf wetness data - chan[{i}]. Exception:{ex.Message}"); + cumulus.LogErrorMessage($"API.ProcessHistoryData: Error in pre-processing extra leaf wetness data - chan[{i}]. Exception:{ex.Message}"); } } // Indoor CO2 @@ -975,7 +975,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing indoor CO2 data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing indoor CO2 data. Exception: " + ex.Message); } } // CO2 Combi @@ -1031,7 +1031,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing CO2 combo data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing CO2 combo data. Exception: " + ex.Message); } } // pm2.5 Combi @@ -1063,7 +1063,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing pm 2.5 combo data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing pm 2.5 combo data. Exception: " + ex.Message); } } // pm10 Combi @@ -1095,13 +1095,13 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage("API.ProcessHistoryData: Error in pre-processing pm 10 combo data. Exception: " + ex.Message); + cumulus.LogErrorMessage("API.ProcessHistoryData: Error in pre-processing pm 10 combo data. Exception: " + ex.Message); } } // 4 channel PM 2.5 sensors - for (var i = 1; i <= 4 ; i++) + for (var i = 1; i <= 4; i++) { - EcowittHistoricDataPm25Aqi sensor = null; + HistoricDataPm25Aqi sensor = null; switch (i) { case 1: @@ -1144,7 +1144,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } catch (Exception ex) { - cumulus.LogMessage($"API.ProcessHistoryData: Error in pre-processing 4 chan pm 2.5 data - chan[{i}] . Exception: {ex.Message}"); + cumulus.LogErrorMessage($"API.ProcessHistoryData: Error in pre-processing 4 chan pm 2.5 data - chan[{i}] . Exception: {ex.Message}"); } } @@ -1251,7 +1251,7 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke } } - private void ApplyHistoricData(KeyValuePair rec) + private void ApplyHistoricData(KeyValuePair rec) { // === Wind == // WindGust = max for period @@ -1261,20 +1261,20 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.WindGust.HasValue && rec.Value.WindSpd.HasValue && rec.Value.WindDir.HasValue) { - var gustVal = (double)rec.Value.WindGust; - var spdVal = (double)rec.Value.WindSpd; - var dirVal = (int)rec.Value.WindDir.Value; + var gustVal = (double) rec.Value.WindGust; + var spdVal = (double) rec.Value.WindSpd; + var dirVal = (int) rec.Value.WindDir.Value; station.DoWind(gustVal, dirVal, spdVal, rec.Key); } else { - cumulus.LogMessage("ApplyHistoricData: Insufficient data process wind"); + cumulus.LogWarningMessage("ApplyHistoricData: Insufficient data process wind"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Wind data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Wind data - " + ex.Message); } // === Humidity === @@ -1287,7 +1287,7 @@ private void ApplyHistoricData(KeyValuePair r } else { - cumulus.LogMessage("ApplyHistoricData: Missing indoor humidity data"); + cumulus.LogWarningMessage("ApplyHistoricData: Missing indoor humidity data"); } if (rec.Value.Humidity.HasValue && cumulus.Gw1000PrimaryTHSensor == 0) @@ -1296,13 +1296,13 @@ private void ApplyHistoricData(KeyValuePair r } else { - cumulus.LogMessage("ApplyHistoricData: Missing outdoor humidity data"); + cumulus.LogWarningMessage("ApplyHistoricData: Missing outdoor humidity data"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Humidity data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Humidity data - " + ex.Message); } // === Pressure === @@ -1311,18 +1311,18 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.Pressure.HasValue) { - var pressVal = (double)rec.Value.Pressure; + var pressVal = (double) rec.Value.Pressure; station.DoPressure(pressVal, rec.Key); station.UpdatePressureTrendString(); } else { - cumulus.LogMessage("ApplyHistoricData: Missing pressure data"); + cumulus.LogWarningMessage("ApplyHistoricData: Missing pressure data"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Pressure data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Pressure data - " + ex.Message); } // === Indoor temp === @@ -1331,17 +1331,17 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.IndoorTemp.HasValue) { - var tempVal = (double)rec.Value.IndoorTemp; + var tempVal = (double) rec.Value.IndoorTemp; station.DoIndoorTemp(tempVal); } else { - cumulus.LogMessage("ApplyHistoricData: Missing indoor temperature data"); + cumulus.LogWarningMessage("ApplyHistoricData: Missing indoor temperature data"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Indoor temp data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Indoor temp data - " + ex.Message); } // === Outdoor temp === @@ -1350,17 +1350,17 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.Temp.HasValue && cumulus.Gw1000PrimaryTHSensor == 0) { - var tempVal = (double)rec.Value.Temp; + var tempVal = (double) rec.Value.Temp; station.DoOutdoorTemp(tempVal, rec.Key); } else { - cumulus.LogMessage("ApplyHistoricData: Missing outdoor temperature data"); + cumulus.LogWarningMessage("ApplyHistoricData: Missing outdoor temperature data"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Outdoor temp data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Outdoor temp data - " + ex.Message); } // === Rain === @@ -1371,7 +1371,7 @@ private void ApplyHistoricData(KeyValuePair r { // we have a rain rate, so we will NOT calculate it station.calculaterainrate = false; - rRate = (double)rec.Value.RainRate; + rRate = (double) rec.Value.RainRate; } else { @@ -1381,18 +1381,18 @@ private void ApplyHistoricData(KeyValuePair r if (rec.Value.RainYear.HasValue) { - var rainVal = (double)rec.Value.RainYear; + var rainVal = (double) rec.Value.RainYear; var rateVal = rRate; station.DoRain(rainVal, rateVal, rec.Key); } else { - cumulus.LogMessage("ApplyHistoricData: Missing rain data"); + cumulus.LogWarningMessage("ApplyHistoricData: Missing rain data"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Rain data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Rain data - " + ex.Message); } // === Solar === @@ -1406,7 +1406,7 @@ private void ApplyHistoricData(KeyValuePair r } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); } // === UVI === @@ -1415,12 +1415,12 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.UVI.HasValue) { - station.DoUV((double)rec.Value.UVI, rec.Key); + station.DoUV((double) rec.Value.UVI, rec.Key); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Solar data - " + ex.Message); } // === Extra Sensors === @@ -1431,7 +1431,7 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.ExtraTemp[i].HasValue) { - var tempVal = (double)rec.Value.ExtraTemp[i]; + var tempVal = (double) rec.Value.ExtraTemp[i]; if (i == cumulus.Gw1000PrimaryTHSensor) { station.DoOutdoorTemp(tempVal, rec.Key); @@ -1441,7 +1441,7 @@ private void ApplyHistoricData(KeyValuePair r } catch (Exception ex) { - cumulus.LogMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); + cumulus.LogErrorMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); } // === Extra Humidity === try @@ -1457,7 +1457,7 @@ private void ApplyHistoricData(KeyValuePair r } catch (Exception ex) { - cumulus.LogMessage($"ApplyHistoricData: Error in extra humidity data - {ex.Message}"); + cumulus.LogErrorMessage($"ApplyHistoricData: Error in extra humidity data - {ex.Message}"); } @@ -1468,17 +1468,17 @@ private void ApplyHistoricData(KeyValuePair r { if (cumulus.EcowittMapWN34[i] == 0) { - station.DoUserTemp((double)rec.Value.UserTemp[i], i); + station.DoUserTemp((double) rec.Value.UserTemp[i], i); } else { - station.DoSoilTemp((double)rec.Value.UserTemp[i], cumulus.EcowittMapWN34[i]); + station.DoSoilTemp((double) rec.Value.UserTemp[i], cumulus.EcowittMapWN34[i]); } } } catch (Exception ex) { - cumulus.LogMessage($"ApplyHistoricData: Error in extra user temperature data - {ex.Message}"); + cumulus.LogErrorMessage($"ApplyHistoricData: Error in extra user temperature data - {ex.Message}"); } // === Soil Moisture === @@ -1486,12 +1486,12 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.SoilMoist[i].HasValue) { - station.DoSoilMoisture((double)rec.Value.SoilMoist[i], i); + station.DoSoilMoisture((double) rec.Value.SoilMoist[i], i); } } catch (Exception ex) { - cumulus.LogMessage($"ApplyHistoricData: Error in soil moisture data - {ex.Message}"); + cumulus.LogErrorMessage($"ApplyHistoricData: Error in soil moisture data - {ex.Message}"); } } @@ -1505,7 +1505,7 @@ private void ApplyHistoricData(KeyValuePair r } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in CO2 data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in CO2 data - " + ex.Message); } // === Indoor CO2 24hr avg === @@ -1518,7 +1518,7 @@ private void ApplyHistoricData(KeyValuePair r } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in CO2 24hr avg data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in CO2 24hr avg data - " + ex.Message); } // === PM 2.5 Combo @@ -1526,12 +1526,12 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.AqiComboPm25.HasValue) { - station.CO2_pm2p5 = (double)rec.Value.AqiComboPm25.Value; + station.CO2_pm2p5 = (double) rec.Value.AqiComboPm25.Value; } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in AQI Combo pm2.5 data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in AQI Combo pm2.5 data - " + ex.Message); } // === PM 10 Combo @@ -1539,12 +1539,12 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.AqiComboPm10.HasValue) { - station.CO2_pm10 = (double)rec.Value.AqiComboPm10.Value; + station.CO2_pm10 = (double) rec.Value.AqiComboPm10.Value; } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in AQI Combo pm10 data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in AQI Combo pm10 data - " + ex.Message); } // === 4 channel pm 2.5 === @@ -1554,12 +1554,12 @@ private void ApplyHistoricData(KeyValuePair r { if (rec.Value.pm25[i].HasValue) { - station.DoAirQuality((double)rec.Value.pm25[i].Value, i); + station.DoAirQuality((double) rec.Value.pm25[i].Value, i); } } catch (Exception ex) { - cumulus.LogMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); + cumulus.LogErrorMessage($"ApplyHistoricData: Error in extra temperature data - {ex.Message}"); } } @@ -1575,13 +1575,13 @@ private void ApplyHistoricData(KeyValuePair r } else if (rec.Value.DewPoint.HasValue) { - var val = (double)rec.Value.DewPoint; + var val = (double) rec.Value.DewPoint; station.DoOutdoorDewpoint(val, rec.Key); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Dew point data - " + ex.Message); } // === Wind Chill === @@ -1601,7 +1601,7 @@ private void ApplyHistoricData(KeyValuePair r } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Wind chill data - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Wind chill data - " + ex.Message); } // === Humidex === @@ -1618,12 +1618,193 @@ private void ApplyHistoricData(KeyValuePair r } else { - cumulus.LogMessage("ApplyHistoricData: Insufficient data to calculate Apparent/Feels Like temps"); + cumulus.LogWarningMessage("ApplyHistoricData: Insufficient data to calculate Apparent/Feels Like temps"); } } catch (Exception ex) { - cumulus.LogMessage("ApplyHistoricData: Error in Humidex/Apparant/Feels Like - " + ex.Message); + cumulus.LogErrorMessage("ApplyHistoricData: Error in Humidex/Apparant/Feels Like - " + ex.Message); + } + } + + + // returns the data structure and the number of seconds to wait before the next update + internal CurrentDataData GetCurrentData(CancellationToken token, ref int delay) + { + // Doc: https://doc.ecowitt.net/web/#/apiv3en?page_id=17 + + cumulus.LogMessage("API.GetCurrentData: Get Ecowitt Current Data"); + + var sb = new StringBuilder(currentUrl); + + sb.Append($"application_key={cumulus.EcowittApplicationKey}"); + sb.Append($"&api_key={cumulus.EcowittUserApiKey}"); + sb.Append($"&mac={cumulus.EcowittMacAddress}"); + + // Request the data in the correct units + sb.Append($"&temp_unitid={cumulus.Units.Temp + 1}"); // 1=C, 2=F + sb.Append($"&pressure_unitid={(cumulus.Units.Press == 2 ? "4" : "3")}"); // 3=hPa, 4=inHg, 5=mmHg + string windUnit; + switch (cumulus.Units.Wind) + { + case 0: // m/s + windUnit = "6"; + break; + case 1: // mph + windUnit = "9"; + break; + case 2: // km/h + windUnit = "7"; + break; + case 3: // knots + windUnit = "8"; + break; + default: + windUnit = "?"; + break; + } + sb.Append($"&wind_speed_unitid={windUnit}"); + sb.Append($"&rainfall_unitid={cumulus.Units.Rain + 12}"); + + // available callbacks: + // all + // outdoor, indoor, solar_and_uvi, rainfall, wind, pressure, lightning + // indoor_co2, co2_aqi_combo, pm25_aqi_combo, pm10_aqi_combo, temp_and_humidity_aqi_combo + // pm25_ch[1-4] + // temp_and_humidity_ch[1-8] + // soil_ch[1-8] + // temp_ch[1-8] + // leaf_ch[1-8] + // batt + + sb.Append("&call_back=all"); + + 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.GetCurrentData: Ecowitt API Current Response code: {responseCode}"); + cumulus.LogDataMessage($"API.GetCurrentData: Ecowitt API Current Response: {responseBody}"); + } + + if (responseCode != 200) + { + var currentError = responseBody.FromJson(); + cumulus.LogWarningMessage($"API.GetCurrentData: Ecowitt API Current Error: {currentError.code}, {currentError.msg}"); + cumulus.LogConsoleMessage($" - Error {currentError.code}: {currentError.msg}", ConsoleColor.Red); + delay = 10; + return null; + } + + + if (responseBody == "{}") + { + cumulus.LogWarningMessage("API.GetCurrentData: Ecowitt API Current: No data was returned."); + cumulus.LogConsoleMessage(" - No current data available"); + delay = 10; + return null; + } + 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. + delay = 10; + return null; + } + } + else if (currObj.code == -1 || currObj.code == 45001) + { + // -1 = system busy, 45001 = rate limited + + cumulus.LogMessage("API.GetCurrentData: System Busy or Rate Limited, waiting 5 secs before retry..."); + delay = 5; + return null; + } + else + { + delay = 10; + return null; + } + } + else + { + delay = 10; + return null; + } + + } + else // No idea what we got, dump it to the log + { + cumulus.LogErrorMessage("API.GetCurrentData: Invalid current message received"); + cumulus.LogDataMessage("API.GetCurrentData: Received: " + responseBody); + delay = 10; + return null; + } + + 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); + cumulus.LogDebugMessage($"EcowittCloud: Last data update {dataTime:s}"); + + if (dataTime != LastCurrentDataTime) + { + //ProcessCurrentData(currObj.data, token); + LastCurrentDataTime = dataTime; + + // how many seconds to the next update? + // the data is updated once a minute, so wait for 5 seonds after the next update + + var lastUpdate = (DateTime.Now - LastCurrentDataTime).TotalSeconds; + if (lastUpdate > 65) + { + // hum the data is already out of date, query again after a short delay + delay = 10; + return null; + } + else + { + delay = (int) (60 - lastUpdate + 3); + return currObj.data; + } + } + else + { + delay = 10; + return null; + } + } + + delay = 20; + return null; + } + catch (Exception ex) + { + cumulus.LogErrorMessage("API.GetCurrentData: Exception: " + ex.Message); + delay = 10; + return null; } } @@ -1660,7 +1841,7 @@ private string ErrorCode(int code) } */ - private class EcowitHistErrorResp + private class ErrorResp { public int code { get; set; } public string msg { get; set; } @@ -1668,7 +1849,7 @@ private class EcowitHistErrorResp public object data { get; set; } } - internal class EcowittHistoricResp + internal class HistoricResp { public int code { get; set; } public string msg { get; set; } @@ -1680,141 +1861,141 @@ internal class EcowittHistoricResp // refactor data as a dictionary object and parse each item indivually internal class EcowittHistoricData { - public EcowittHistoricTempHum indoor { get; set; } - public EcowittHistoricDataPressure pressure { get; set; } - public EcowittHistoricOutdoor outdoor { get; set; } - public EcowittHistoricDataWind wind { get; set; } - public EcowittHistoricDataSolar solar_and_uvi { get; set; } - public EcowittHistoricDataRainfall rainfall { get; set; } - public EcowittHistoricDataRainfall rainfall_piezo { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch1 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch2 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch3 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch4 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch5 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch6 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch7 { get; set; } - public EcowittHistoricTempHum temp_and_humidity_ch8 { get; set; } - public EcowittHistoricDataSoil soil_ch1 { get; set; } - public EcowittHistoricDataSoil soil_ch2 { get; set; } - public EcowittHistoricDataSoil soil_ch3 { get; set; } - public EcowittHistoricDataSoil soil_ch4 { get; set; } - public EcowittHistoricDataSoil soil_ch5 { get; set; } - public EcowittHistoricDataSoil soil_ch6 { get; set; } - public EcowittHistoricDataSoil soil_ch7 { get; set; } - public EcowittHistoricDataSoil soil_ch8 { get; set; } - public EcowittHistoricDataTemp temp_ch1 { get; set; } - public EcowittHistoricDataTemp temp_ch2 { get; set; } - public EcowittHistoricDataTemp temp_ch3 { get; set; } - public EcowittHistoricDataTemp temp_ch4 { get; set; } - public EcowittHistoricDataTemp temp_ch5 { get; set; } - public EcowittHistoricDataTemp temp_ch6 { get; set; } - public EcowittHistoricDataTemp temp_ch7 { get; set; } - public EcowittHistoricDataTemp temp_ch8 { get; set; } - public EcowittHistoricDataLeaf leaf_ch1 { get; set; } - public EcowittHistoricDataLeaf leaf_ch2 { get; set; } - public EcowittHistoricDataLeaf leaf_ch3 { get; set; } - public EcowittHistoricDataLeaf leaf_ch4 { get; set; } - public EcowittHistoricDataLeaf leaf_ch5 { get; set; } - public EcowittHistoricDataLeaf leaf_ch6 { get; set; } - public EcowittHistoricDataLeaf leaf_ch7 { get; set; } - public EcowittHistoricDataLeaf leaf_ch8 { get; set; } - public EcowittHistoricDataLightning lightning { get; set; } - public EcowittHistoricDataCo2 indoor_co2 { get; set; } - public EcowittHistoricDataCo2 co2_aqi_combo { get; set; } - public EcowittHistoricDataPm25Aqi pm25_aqi_combo { get; set; } - public EcowittHistoricDataPm10Aqi pm10_aqi_combo { get; set; } - public EcowittHistoricDataPm25Aqi pm25_ch1 { get; set; } - public EcowittHistoricDataPm25Aqi pm25_ch2 { get; set; } - public EcowittHistoricDataPm25Aqi pm25_ch3 { get; set; } - public EcowittHistoricDataPm25Aqi pm25_ch4 { get; set; } + public HistoricTempHum indoor { get; set; } + public HistoricDataPressure pressure { get; set; } + public HistoricOutdoor outdoor { get; set; } + public HistoricDataWind wind { get; set; } + public HistoricDataSolar solar_and_uvi { get; set; } + public HistoricDataRainfall rainfall { get; set; } + public HistoricDataRainfall rainfall_piezo { get; set; } + public HistoricTempHum temp_and_humidity_ch1 { get; set; } + public HistoricTempHum temp_and_humidity_ch2 { get; set; } + public HistoricTempHum temp_and_humidity_ch3 { get; set; } + public HistoricTempHum temp_and_humidity_ch4 { get; set; } + public HistoricTempHum temp_and_humidity_ch5 { get; set; } + public HistoricTempHum temp_and_humidity_ch6 { get; set; } + public HistoricTempHum temp_and_humidity_ch7 { get; set; } + public HistoricTempHum temp_and_humidity_ch8 { get; set; } + public HistoricDataSoil soil_ch1 { get; set; } + public HistoricDataSoil soil_ch2 { get; set; } + public HistoricDataSoil soil_ch3 { get; set; } + public HistoricDataSoil soil_ch4 { get; set; } + public HistoricDataSoil soil_ch5 { get; set; } + public HistoricDataSoil soil_ch6 { get; set; } + public HistoricDataSoil soil_ch7 { get; set; } + public HistoricDataSoil soil_ch8 { get; set; } + public HistoricDataTemp temp_ch1 { get; set; } + public HistoricDataTemp temp_ch2 { get; set; } + public HistoricDataTemp temp_ch3 { get; set; } + public HistoricDataTemp temp_ch4 { get; set; } + public HistoricDataTemp temp_ch5 { get; set; } + public HistoricDataTemp temp_ch6 { get; set; } + public HistoricDataTemp temp_ch7 { get; set; } + public HistoricDataTemp temp_ch8 { get; set; } + public HistoricDataLeaf leaf_ch1 { get; set; } + public HistoricDataLeaf leaf_ch2 { get; set; } + public HistoricDataLeaf leaf_ch3 { get; set; } + public HistoricDataLeaf leaf_ch4 { get; set; } + public HistoricDataLeaf leaf_ch5 { get; set; } + public HistoricDataLeaf leaf_ch6 { get; set; } + public HistoricDataLeaf leaf_ch7 { get; set; } + public HistoricDataLeaf leaf_ch8 { get; set; } + public HistoricDataLightning lightning { get; set; } + public HistoricDataCo2 indoor_co2 { get; set; } + public HistoricDataCo2 co2_aqi_combo { get; set; } + public HistoricDataPm25Aqi pm25_aqi_combo { get; set; } + public HistoricDataPm10Aqi pm10_aqi_combo { get; set; } + public HistoricDataPm25Aqi pm25_ch1 { get; set; } + public HistoricDataPm25Aqi pm25_ch2 { get; set; } + public HistoricDataPm25Aqi pm25_ch3 { get; set; } + public HistoricDataPm25Aqi pm25_ch4 { get; set; } } - internal class EcowittHistoricDataTypeInt + internal class HistoricDataTypeInt { public string unit { get; set; } public Dictionary list { get; set; } } - internal class EcowittHistoricDataTypeDbl + internal class HistoricDataTypeDbl { public string unit { get; set; } public Dictionary list { get; set; } } - internal class EcowittHistoricTempHum + internal class HistoricTempHum { - public EcowittHistoricDataTypeDbl temperature { get; set; } - public EcowittHistoricDataTypeInt humidity { get; set; } + public HistoricDataTypeDbl temperature { get; set; } + public HistoricDataTypeInt humidity { get; set; } } - internal class EcowittHistoricOutdoor : EcowittHistoricTempHum + internal class HistoricOutdoor : HistoricTempHum { - public EcowittHistoricDataTypeDbl dew_point { get; set; } + public HistoricDataTypeDbl dew_point { get; set; } } - internal class EcowittHistoricDataPressure + internal class HistoricDataPressure { - public EcowittHistoricDataTypeDbl relative { get; set; } + public HistoricDataTypeDbl relative { get; set; } } - internal class EcowittHistoricDataWind + internal class HistoricDataWind { - public EcowittHistoricDataTypeInt wind_direction { get; set; } - public EcowittHistoricDataTypeDbl wind_speed { get; set; } - public EcowittHistoricDataTypeDbl wind_gust { get; set; } + public HistoricDataTypeInt wind_direction { get; set; } + public HistoricDataTypeDbl wind_speed { get; set; } + public HistoricDataTypeDbl wind_gust { get; set; } } - internal class EcowittHistoricDataSolar + internal class HistoricDataSolar { - public EcowittHistoricDataTypeDbl solar { get; set; } - public EcowittHistoricDataTypeDbl uvi { get; set; } + public HistoricDataTypeDbl solar { get; set; } + public HistoricDataTypeDbl uvi { get; set; } } - internal class EcowittHistoricDataRainfall + internal class HistoricDataRainfall { - public EcowittHistoricDataTypeDbl rain_rate { get; set; } - public EcowittHistoricDataTypeDbl yearly { get; set; } + public HistoricDataTypeDbl rain_rate { get; set; } + public HistoricDataTypeDbl yearly { get; set; } } - internal class EcowittHistoricDataSoil + internal class HistoricDataSoil { - public EcowittHistoricDataTypeInt soilmoisture { get; set; } + public HistoricDataTypeInt soilmoisture { get; set; } } - internal class EcowittHistoricDataTemp + internal class HistoricDataTemp { - public EcowittHistoricDataTypeDbl temperature { get; set; } + public HistoricDataTypeDbl temperature { get; set; } } - internal class EcowittHistoricDataLeaf + internal class HistoricDataLeaf { - public EcowittHistoricDataTypeInt leaf_wetness { get; set; } + public HistoricDataTypeInt leaf_wetness { get; set; } } - internal class EcowittHistoricDataLightning + internal class HistoricDataLightning { - public EcowittHistoricDataTypeDbl distance { get; set; } - public EcowittHistoricDataTypeInt count { get; set; } + public HistoricDataTypeDbl distance { get; set; } + public HistoricDataTypeInt count { get; set; } } [DataContract] - internal class EcowittHistoricDataCo2 + internal class HistoricDataCo2 { - public EcowittHistoricDataTypeInt co2 { get; set; } - [DataMember(Name= "24_hours_average")] - public EcowittHistoricDataTypeInt average24h { get; set; } + public HistoricDataTypeInt co2 { get; set; } + [DataMember(Name = "24_hours_average")] + public HistoricDataTypeInt average24h { get; set; } } - internal class EcowittHistoricDataPm25Aqi + internal class HistoricDataPm25Aqi { - public EcowittHistoricDataTypeDbl pm25 { get; set; } + public HistoricDataTypeDbl pm25 { get; set; } } - internal class EcowittHistoricDataPm10Aqi + internal class HistoricDataPm10Aqi { - public EcowittHistoricDataTypeDbl pm10 { get; set; } + public HistoricDataTypeDbl pm10 { get; set; } } internal class HistoricData @@ -1861,5 +2042,286 @@ public HistoricData() } } + + + + private class CurrentData + { + public int code { get; set; } + public string msg { get; set; } + public long time { get; set; } + + public CurrentDataData data { get; set; } + } + + internal class CurrentDataData + { + public CurrentOutdoor outdoor { get; set; } + public CurrentTempHum indoor { get; set; } + public CurrentSolar solar_and_uvi { get; set; } + public CurrentRain rainfall { get; set; } + public CurrentRain rainfall_piezo { get; set; } + public CurrentWind wind { get; set; } + public CurrentPress pressure { get; set; } + public CurrentLightning lightning { get; set; } + public CurrentCo2 indoor_co2 { get; set; } + public CurrentCo2 co2_aqi_combo { get; set; } + public CurrentPm25 pm25_aqi_combo { get; set; } + public CurrentPm10 pm10_aqi_combo { get; set; } + public CurrentTempHum t_rh_aqi_combo { get; set; } + public CurrentLeak water_leak { get; set; } + public CurrentPm25 pm25_ch1 { get; set; } + public CurrentPm25 pm25_ch2 { get; set; } + public CurrentPm25 pm25_ch3 { get; set; } + public CurrentPm25 pm25_ch4 { get; set; } + public CurrentTempHum temp_and_humidity_ch1 { get; set; } + public CurrentTempHum temp_and_humidity_ch2 { get; set; } + public CurrentTempHum temp_and_humidity_ch3 { get; set; } + public CurrentTempHum temp_and_humidity_ch4 { get; set; } + public CurrentTempHum temp_and_humidity_ch5 { get; set; } + public CurrentTempHum temp_and_humidity_ch6 { get; set; } + public CurrentTempHum temp_and_humidity_ch7 { get; set; } + public CurrentTempHum temp_and_humidity_ch8 { get; set; } + public CurrentSoil soil_ch1 { get; set; } + public CurrentSoil soil_ch2 { get; set; } + public CurrentSoil soil_ch3 { get; set; } + public CurrentSoil soil_ch4 { get; set; } + public CurrentSoil soil_ch5 { get; set; } + public CurrentSoil soil_ch6 { get; set; } + public CurrentSoil soil_ch7 { get; set; } + public CurrentSoil soil_ch8 { get; set; } + public CurrentTemp temp_ch1 { get; set; } + public CurrentTemp temp_ch2 { get; set; } + public CurrentTemp temp_ch3 { get; set; } + public CurrentTemp temp_ch4 { get; set; } + public CurrentTemp temp_ch5 { get; set; } + public CurrentTemp temp_ch6 { get; set; } + public CurrentTemp temp_ch7 { get; set; } + public CurrentTemp temp_ch8 { get; set; } + public CurrentLeaf leaf_ch1 { get; set; } + public CurrentLeaf leaf_ch2 { get; set; } + public CurrentLeaf leaf_ch3 { get; set; } + public CurrentLeaf leaf_ch4 { get; set; } + public CurrentLeaf leaf_ch5 { get; set; } + public CurrentLeaf leaf_ch6 { get; set; } + public CurrentLeaf leaf_ch7 { get; set; } + public CurrentLeaf leaf_ch8 { get; set; } + public CurrentBattery battery { get; set; } + public CurrentCamera camera { get; set; } + } + + internal class CurrentOutdoor + { + public CurrentSensorValDbl temperature { get; set; } + public CurrentSensorValDbl feels_like { get; set; } + public CurrentSensorValDbl app_temp { get; set; } + public CurrentSensorValDbl dew_point { get; set; } + public CurrentSensorValInt humidity { get; set; } + } + + internal class CurrentTemp + { + public CurrentSensorValDbl temperature { get; set; } + } + + internal class CurrentTempHum + { + public CurrentSensorValDbl temperature { get; set; } + public CurrentSensorValInt humidity { get; set; } + + } + + internal class CurrentSolar + { + public CurrentSensorValDbl solar { get; set; } + public CurrentSensorValInt uvi { get; set; } + } + + internal class CurrentRain + { + public CurrentSensorValDbl rain_rate { get; set; } + public CurrentSensorValDbl daily { get; set; } + + [IgnoreDataMember] + public CurrentSensorValDbl Event { get; set; } + + [DataMember(Name = "event")] + public CurrentSensorValDbl EventVal + { + get => Event; + set { Event = value; } + } + + + public CurrentSensorValDbl hourly { get; set; } + public CurrentSensorValDbl yearly { get; set; } + } + + internal class CurrentWind + { + public CurrentSensorValDbl wind_speed { get; set; } + public CurrentSensorValDbl wind_gust { get; set; } + public CurrentSensorValInt wind_direction { get; set; } + } + + internal class CurrentPress + { + public CurrentSensorValDbl relative { get; set; } + public CurrentSensorValDbl absolute { get; set; } + } + + internal class CurrentLightning + { + public CurrentSensorValInt distance { get; set; } + public CurrentSensorValInt count { get; set; } + } + + internal class CurrentCo2 + { + public CurrentSensorValInt co2 { get; set; } + + [IgnoreDataMember] + public int Avg24h { get; set; } + + [DataMember(Name = "24_hours_average")] + public int Average + { + get => Avg24h; + set { Avg24h = value; } + } + } + + internal class CurrentPm25 + { + public CurrentSensorValInt real_time_aqi { get; set; } + public CurrentSensorValInt pm25 { get; set; } + + [IgnoreDataMember] + public CurrentSensorValInt Avg24h { get; set; } + + [DataMember(Name = "24_hours_aqi")] + public CurrentSensorValInt AvgVal + { + get => Avg24h; + set { Avg24h = value; } + } + } + + internal class CurrentPm10 + { + public CurrentSensorValInt real_time_aqi { get; set; } + public CurrentSensorValInt pm10 { get; set; } + + [IgnoreDataMember] + public CurrentSensorValInt Avg24h { get; set; } + + [DataMember(Name = "24_hours_aqi")] + public CurrentSensorValInt AvgVal + { + get => Avg24h; + set { Avg24h = value; } + } + } + + internal class CurrentLeak + { + public CurrentSensorValInt leak_ch1 { get; set; } + public CurrentSensorValInt leak_ch2 { get; set; } + public CurrentSensorValInt leak_ch3 { get; set; } + public CurrentSensorValInt leak_ch4 { get; set; } + } + + internal class CurrentSoil + { + public CurrentSensorValInt soilmoisture { get; set; } + } + + internal class CurrentLeaf + { + public CurrentSensorValInt leaf_wetness { get; set; } + } + + internal class CurrentBattery + { + public CurrentSensorValInt t_rh_p_sensor { get; set; } + public CurrentSensorValDbl ws1900_console { get; set; } + public CurrentSensorValDbl ws1800_console { get; set; } + public CurrentSensorValInt ws6006_console { get; set; } + public CurrentSensorValDbl console { get; set; } + public CurrentSensorValInt outdoor_t_rh_sensor { get; set; } + public CurrentSensorValDbl wind_sensor { get; set; } + public CurrentSensorValDbl haptic_array_battery { get; set; } + public CurrentSensorValDbl haptic_array_capacitor { get; set; } + public CurrentSensorValDbl sonic_array { get; set; } + public CurrentSensorValDbl rainfall_sensor { get; set; } + public CurrentSensorValInt sensor_array { get; set; } + public CurrentSensorValInt lightning_sensor { get; set; } + public CurrentSensorValInt aqi_combo_sensor { get; set; } + public CurrentSensorValInt water_leak_sensor_ch1 { get; set; } + public CurrentSensorValInt water_leak_sensor_ch2 { get; set; } + public CurrentSensorValInt water_leak_sensor_ch3 { get; set; } + public CurrentSensorValInt water_leak_sensor_ch4 { get; set; } + public CurrentSensorValInt pm25_sensor_ch1 { get; set; } + public CurrentSensorValInt pm25_sensor_ch2 { get; set; } + public CurrentSensorValInt pm25_sensor_ch3 { get; set; } + public CurrentSensorValInt pm25_sensor_ch4 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch1 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch2 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch3 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch4 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch5 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch6 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch7 { get; set; } + public CurrentSensorValInt temp_humidity_sensor_ch8 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch1 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch2 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch3 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch4 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch5 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch6 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch7 { get; set; } + public CurrentSensorValDbl soilmoisture_sensor_ch8 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch1 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch2 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch3 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch4 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch5 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch6 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch7 { get; set; } + public CurrentSensorValDbl temperature_sensor_ch8 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch1 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch2 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch3 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch4 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch5 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch6 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch7 { get; set; } + public CurrentSensorValDbl leaf_wetness_sensor_ch8 { get; set; } + } + + internal class CurrentCamera + { + public CurrentCameraVal photo { get; set; } + } + + internal class CurrentSensorValDbl + { + public long time { get; set; } + public string unit { get; set; } + public double value { get; set; } + } + + internal class CurrentSensorValInt + { + public long time { get; set; } + public string unit { get; set; } + public int value { get; set; } + } + + internal class CurrentCameraVal + { + public long time { get; set; } + public string url { get; set; } + } } } diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs new file mode 100644 index 00000000..2804dbae --- /dev/null +++ b/CumulusMX/EcowittCloudStation.cs @@ -0,0 +1,949 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + + +namespace CumulusMX +{ + internal class EcowittCloudStation : WeatherStation + { + private readonly WeatherStation station; + private EcowittApi ecowittApi; + private int maxArchiveRuns = 1; + private Task liveTask; + private readonly bool main; + + + public EcowittCloudStation(Cumulus cumulus, WeatherStation station = null) : base(cumulus) + { + this.station = station; + + main = station == null; + + if (main) + { + cumulus.LogMessage("Creating Ecowitt Cloud"); + } + else + { + cumulus.LogMessage("Creating Extra Sensors - Ecowitt Cloud"); + } + + if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) + { + cumulus.LogErrorMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + return; + } + + + // Do not set these if we are only using extra sensors + if (main) + { + // cloud provides 10 min average wind speeds + cumulus.StationOptions.CalcuateAverageWindSpeed = true; + + // GW1000 does not provide an interval gust value, it gives us a 2 minute high + // The speed is the average for that update + // Therefore we need to force using the speed for the average calculation + cumulus.StationOptions.UseSpeedForAvgCalc = true; + // also use it for the Latest value + cumulus.StationOptions.UseSpeedForLatest = false; + + // does send DP + cumulus.StationOptions.CalculatedDP = false; + // does not send Wind Chill + cumulus.StationOptions.CalculatedWC = true; + // does not provide a forecast, force MX to provide it + cumulus.UseCumulusForecast = true; + // does not provide pressure trend strings + cumulus.StationOptions.UseCumulusPresstrendstr = true; + + if (cumulus.Gw1000PrimaryTHSensor == 0) + { + // We are using the primary T/H sensor + cumulus.LogMessage("Using the default outdoor temp/hum sensor data"); + } + else + { + // We are not using the primary T/H sensor + cumulus.LogMessage("Overriding the default outdoor temp/hum data with Extra temp/hum sensor #" + cumulus.Gw1000PrimaryTHSensor); + } + + if (cumulus.Gw1000PrimaryRainSensor == 0) + { + // We are using the traditional rain tipper + cumulus.LogMessage("Using the default traditional rain sensor data"); + } + else + { + cumulus.LogMessage("Using the piezo rain sensor data"); + } + + DataTimeoutMins = 2; + } + + if (main || (!main && cumulus.EcowittExtraUseAQI)) + { + cumulus.Units.AirQualityUnitText = "µg/m³"; + } + if (main || (!main && cumulus.EcowittExtraUseSoilMoist)) + { + cumulus.Units.SoilMoistureUnitText = "%"; + } + if (main || (!main && cumulus.EcowittExtraUseSoilMoist)) + { + cumulus.Units.LeafWetnessUnitText = "%"; + } + + // Only perform the Start-up if we are a proper station, not a Extra Sensor + if (main) + { + Task.Run(getAndProcessHistoryData); + } + else + { + cumulus.LogMessage("Extra Sensors - Ecowitt Cloud"); + } + } + + public override void Start() + { + // just incase we did not catch-up any history + DoDayResetIfNeeded(); + DoTrendValues(DateTime.Now); + + cumulus.LogMessage("Starting Ecowitt Cloud station"); + + cumulus.StartTimersAndSensors(); + + 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); + + if (data != null) + { + ProcessCurrentData(data, cumulus.cancellationToken); + } + 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); + } + + public override void Stop() + { + if (station == null) + { + StopMinuteTimer(); + liveTask.Wait(); + cumulus.LogMessage("Ecowitt Cloud station Stopped"); + } + } + + + public override void getAndProcessHistoryData() + { + //cumulus.LogDebugMessage("Lock: Station waiting for the lock"); + Cumulus.syncInit.Wait(); + //cumulus.LogDebugMessage("Lock: Station has the lock"); + + int archiveRun = 0; + + try + { + + ecowittApi = new EcowittApi(cumulus, this); + + do + { + GetHistoricData(); + archiveRun++; + } while (archiveRun < maxArchiveRuns); + } + catch (Exception ex) + { + cumulus.LogExceptionMessage(ex, "Exception occurred reading archive data."); + } + + //cumulus.LogDebugMessage("Lock: Station releasing the lock"); + _ = Cumulus.syncInit.Release(); + + StartLoop(); + } + + 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; + var thisStation = main ? this : station; + + // Only do the primary sensors if running as the main station + if (main) + { + // Outdoor temp/hum + if (cumulus.Gw1000PrimaryTHSensor == 0) + { + if (data.outdoor == null) + { + cumulus.LogErrorMessage("ProcessCurrentData: Error outdoor temp/humidity is missing"); + } + else + { + try + { + var time = Utils.FromUnixTime(data.outdoor.temperature.time); + DoOutdoorTemp(data.outdoor.temperature.value, time); + DoOutdoorDewpoint(data.outdoor.dew_point.value, time); + DoFeelsLike(time); + DoApparentTemp(time); + DoOutdoorHumidity(data.outdoor.humidity.value, time); + DoHumidex(time); + DoCloudBaseHeatIndex(time); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Outdoor temp data - {ex.Message}"); + } + } + } + + // Indoor temp/hum + if (data.indoor == null) + { + cumulus.LogErrorMessage("ProcessCurrentData: Error indoor temp/humidity is missing"); + } + else + { + try + { + DoIndoorTemp(data.indoor.temperature.value); + DoIndoorHumidity(data.indoor.humidity.value); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in indoor data - {ex.Message}"); + } + } + + // Pressure + if (data.pressure == null) + { + cumulus.LogErrorMessage("ProcessCurrentData: Error pressure data is missing"); + } + else + { + try + { + DoPressure(data.pressure.relative.value, Utils.FromUnixTime(data.pressure.relative.time)); + StationPressure = data.pressure.absolute.value; + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in pressure data - {ex.Message}"); + } + } + + // Wind + if (data.wind == null) + { + cumulus.LogErrorMessage("ProcessCurrentData: Error wind data is missing"); + } + else + { + 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)); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in wind data - {ex.Message}"); + } + } + + // Rain + if (cumulus.Gw1000PrimaryRainSensor == 0) // tipper + { + if (data.rainfall == null) + { + cumulus.LogErrorMessage("ProcessCurrentData: Error tipper rainfall is missing"); + } + else + { + try + { + DoRain(data.rainfall.yearly.value, data.rainfall.rain_rate.value, Utils.FromUnixTime(data.rainfall.yearly.time)); + StormRain = data.rainfall.Event.value; + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in tipper rainfall data - {ex.Message}"); + } + } + } + else // piezo + { + if (data.rainfall_piezo == null) + { + cumulus.LogErrorMessage("ProcessCurrentData: Error piezo rainfall is missing"); + } + else + { + try + { + DoRain(data.rainfall_piezo.yearly.value, data.rainfall_piezo.rain_rate.value, Utils.FromUnixTime(data.rainfall_piezo.yearly.time)); + StormRain = data.rainfall_piezo.Event.value; + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in piezo rainfall data - {ex.Message}"); + } + } + } + } + + // Solar + if ((main || cumulus.EcowittExtraUseSolar) && data.solar_and_uvi != null) + { + try + { + if (data.solar_and_uvi.solar != null) + DoSolarRad((int) data.solar_and_uvi.solar.value, Utils.FromUnixTime(data.solar_and_uvi.solar.time)); + + if (data.solar_and_uvi.uvi != null) + DoUV(data.solar_and_uvi.uvi.value, Utils.FromUnixTime(data.solar_and_uvi.solar.time)); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in solar data - {ex.Message}"); + } + } + + // Extra Temperature + if (main || cumulus.EcowittExtraUseTempHum) + { + try + { + ProcessExtraTempHum(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in extra temperature data - {ex.Message}"); + } + } + + // === Soil/Water Temp === + if (main || cumulus.EcowittExtraUseUserTemp) + { + try + { + ProcessUserTemps(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in user temperature data - {ex.Message}"); + } + } + + // === Soil Moisture === + if (main || cumulus.EcowittExtraUseSoilMoist) + { + try + { + ProcessSoilMoist(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Soil moisture data - {ex.Message}"); + } + } + + // === Leaf Wetness === + if (main || cumulus.EcowittExtraUseLeafWet) + { + try + { + ProcessLeafWetness(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Leaf wetness data - {ex.Message}"); + } + } + + // === Air Quality === + if (main || cumulus.EcowittExtraUseAQI) + { + try + { + ProcessAirQuality(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Air Quality data - {ex.Message}"); + } + } + + // === CO₂ === + if (main || cumulus.EcowittExtraUseCo2) + { + try + { + ProcessCo2(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in CO₂ data - {ex.Message}"); + } + } + + // === Lightning === + if (main || cumulus.EcowittExtraUseLightning) + { + try + { + ProcessLightning(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Lightning data - {ex.Message}"); + } + } + + // === Leak === + if (main || cumulus.EcowittExtraUseLeak) + { + try + { + ProcessLeak(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Leak data - {ex.Message}"); + } + } + + // === Batteries === + try + { + ProcessBatteries(data); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Battery data - {ex.Message}"); + } + + // === Camera === + try + { + if (data.camera != null && data.camera.photo != null) + { + EcowittCameraUrl = data.camera.photo.url; + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"ProcessCurrentData: Error in Camera data - {ex.Message}"); + } + + + thisStation.DoForecast("", false); + + cumulus.BatteryLowAlarm.Triggered = batteryLow; + + + var updateTime = Utils.FromUnixTime(data.outdoor == null ? data.indoor.temperature.time : data.indoor.temperature.time); + thisStation.UpdateStatusPanel(updateTime); + thisStation.UpdateMQTT(); + + DataStopped = false; + cumulus.DataStoppedAlarm.Triggered = false; + } + private void ProcessExtraTempHum(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.temp_and_humidity_ch1 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 1) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch1.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch1.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch1.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch1.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch1.temperature.value, 1); + station.DoExtraHum(data.temp_and_humidity_ch1.humidity.value, 1); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[1]), station.ExtraHum[1]); + station.ExtraDewPoint[1] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch2 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 2) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch2.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch2.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch2.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch2.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch2.temperature.value, 2); + station.DoExtraHum(data.temp_and_humidity_ch2.humidity.value, 2); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[2]), station.ExtraHum[2]); + station.ExtraDewPoint[2] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch3 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 3) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch3.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch3.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch3.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch3.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch3.temperature.value, 3); + station.DoExtraHum(data.temp_and_humidity_ch3.humidity.value, 3); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[3]), station.ExtraHum[3]); + station.ExtraDewPoint[3] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch4 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 4) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch4.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch4.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch4.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch4.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch4.temperature.value, 4); + station.DoExtraHum(data.temp_and_humidity_ch4.humidity.value, 4); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[4]), station.ExtraHum[4]); + station.ExtraDewPoint[4] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch5 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 5) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch5.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch5.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch5.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch5.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch5.temperature.value, 5); + station.DoExtraHum(data.temp_and_humidity_ch5.humidity.value, 5); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[5]), station.ExtraHum[5]); + station.ExtraDewPoint[5] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch6 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 6) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch6.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch6.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch6.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch6.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch6.temperature.value, 6); + station.DoExtraHum(data.temp_and_humidity_ch6.humidity.value, 6); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[6]), station.ExtraHum[6]); + station.ExtraDewPoint[6] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch7 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 7) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch7.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch7.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch7.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch7.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch7.temperature.value, 7); + station.DoExtraHum(data.temp_and_humidity_ch7.humidity.value, 7); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[7]), station.ExtraHum[7]); + station.ExtraDewPoint[7] = ConvertTempCToUser(dp); + } + + if (data.temp_and_humidity_ch8 != null) + { + if (cumulus.Gw1000PrimaryTHSensor == 8) + { + station.DoOutdoorTemp(data.temp_and_humidity_ch8.temperature.value, Utils.FromUnixTime(data.temp_and_humidity_ch8.temperature.time)); + station.DoOutdoorHumidity(data.temp_and_humidity_ch8.humidity.value, Utils.FromUnixTime(data.temp_and_humidity_ch8.humidity.time)); + } + station.DoExtraTemp(data.temp_and_humidity_ch8.temperature.value, 8); + station.DoExtraHum(data.temp_and_humidity_ch8.humidity.value, 8); + + var dp = MeteoLib.DewPoint(ConvertUserTempToC(station.ExtraTemp[8]), station.ExtraHum[8]); + station.ExtraDewPoint[8] = ConvertTempCToUser(dp); + } + } + + private void ProcessUserTemps(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.temp_ch1 != null) + { + if (cumulus.EcowittMapWN34[1] == 0) + { + station.DoUserTemp(data.temp_ch1.temperature.value, 1); + } + else + { + station.DoSoilTemp(data.temp_ch1.temperature.value, cumulus.EcowittMapWN34[1]); + } + } + + if (data.temp_ch2 != null) + { + if (cumulus.EcowittMapWN34[2] == 0) + { + station.DoUserTemp(data.temp_ch2.temperature.value, 2); + } + else + { + station.DoSoilTemp(data.temp_ch2.temperature.value, cumulus.EcowittMapWN34[2]); + } + } + + if (data.temp_ch3 != null) + { + if (cumulus.EcowittMapWN34[3] == 0) + { + station.DoUserTemp(data.temp_ch3.temperature.value, 3); + } + else + { + station.DoSoilTemp(data.temp_ch3.temperature.value, cumulus.EcowittMapWN34[3]); + } + } + + if (data.temp_ch4 != null) + { + if (cumulus.EcowittMapWN34[4] == 0) + { + station.DoUserTemp(data.temp_ch4.temperature.value, 4); + } + else + { + station.DoSoilTemp(data.temp_ch4.temperature.value, cumulus.EcowittMapWN34[4]); + } + } + + if (data.temp_ch5 != null) + { + if (cumulus.EcowittMapWN34[5] == 0) + { + station.DoUserTemp(data.temp_ch5.temperature.value, 5); + } + else + { + station.DoSoilTemp(data.temp_ch5.temperature.value, cumulus.EcowittMapWN34[5]); + } + } + + if (data.temp_ch6 != null) + { + if (cumulus.EcowittMapWN34[6] == 0) + { + station.DoUserTemp(data.temp_ch6.temperature.value, 6); + } + else + { + station.DoSoilTemp(data.temp_ch6.temperature.value, cumulus.EcowittMapWN34[6]); + } + } + + if (data.temp_ch7 != null) + { + if (cumulus.EcowittMapWN34[7] == 0) + { + station.DoUserTemp(data.temp_ch7.temperature.value, 7); + } + else + { + station.DoSoilTemp(data.temp_ch7.temperature.value, cumulus.EcowittMapWN34[7]); + } + } + + if (data.temp_ch8 != null) + { + if (cumulus.EcowittMapWN34[8] == 0) + { + station.DoUserTemp(data.temp_ch8.temperature.value, 8); + } + else + { + station.DoSoilTemp(data.temp_ch8.temperature.value, cumulus.EcowittMapWN34[8]); + } + } + } + + private void ProcessSoilMoist(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.soil_ch1 != null) + { + station.DoSoilTemp(data.soil_ch1.soilmoisture.value, 1); + } + + if (data.soil_ch2 != null) + { + station.DoSoilTemp(data.soil_ch2.soilmoisture.value, 2); + } + + if (data.soil_ch3 != null) + { + station.DoSoilTemp(data.soil_ch3.soilmoisture.value, 3); + } + + if (data.soil_ch4 != null) + { + station.DoSoilTemp(data.soil_ch4.soilmoisture.value, 4); + } + + if (data.soil_ch5 != null) + { + station.DoSoilTemp(data.soil_ch5.soilmoisture.value, 5); + } + + if (data.soil_ch6 != null) + { + station.DoSoilTemp(data.soil_ch6.soilmoisture.value, 6); + } + + if (data.soil_ch7 != null) + { + station.DoSoilTemp(data.soil_ch7.soilmoisture.value, 7); + } + + if (data.soil_ch8 != null) + { + station.DoSoilTemp(data.soil_ch8.soilmoisture.value, 8); + } + } + + private void ProcessLeafWetness(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.leaf_ch1 != null) + { + station.DoLeafWetness(data.leaf_ch1.leaf_wetness.value, 1); + } + + if (data.leaf_ch2 != null) + { + station.DoLeafWetness(data.leaf_ch2.leaf_wetness.value, 2); + } + + if (data.leaf_ch3 != null) + { + station.DoLeafWetness(data.leaf_ch3.leaf_wetness.value, 3); + } + + if (data.leaf_ch4 != null) + { + station.DoLeafWetness(data.leaf_ch4.leaf_wetness.value, 4); + } + + if (data.leaf_ch5 != null) + { + station.DoLeafWetness(data.leaf_ch5.leaf_wetness.value, 5); + } + + if (data.leaf_ch6 != null) + { + station.DoLeafWetness(data.leaf_ch6.leaf_wetness.value, 6); + } + + if (data.leaf_ch7 != null) + { + station.DoLeafWetness(data.leaf_ch7.leaf_wetness.value, 7); + } + + if (data.leaf_ch8 != null) + { + station.DoLeafWetness(data.leaf_ch8.leaf_wetness.value, 8); + } + } + + private void ProcessAirQuality(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.pm25_ch1 != null) + { + station.DoAirQuality(data.pm25_ch1.pm25.value, 1); + //station.DoAirQualityAvg(data.pm25_ch1.Avg24h.value, 1); + } + + if (data.pm25_ch2 != null) + { + station.DoAirQuality(data.pm25_ch2.pm25.value, 2); + //station.DoAirQualityAvg(data.pm25_ch2.Avg24h.value, 2); + } + if (data.pm25_ch3 != null) + { + station.DoAirQuality(data.pm25_ch3.pm25.value, 3); + //station.DoAirQualityAvg(data.pm25_ch3.Avg24h.value, 3); + } + if (data.pm25_ch4 != null) + { + station.DoAirQuality(data.pm25_ch4.pm25.value, 4); + //station.DoAirQualityAvg(data.pm25_ch1.Avg24h.value, 4); + } + } + + private void ProcessCo2(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.co2_aqi_combo != null) + { + station.CO2 = data.co2_aqi_combo.co2.value; + station.CO2_24h = data.co2_aqi_combo.Avg24h; + } + // indoor overrides the combo + if (data.indoor_co2 != null) + { + station.CO2 = data.indoor_co2.co2.value; + station.CO2_24h = data.indoor_co2.Avg24h; + } + } + + private void ProcessLightning(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.lightning != null) + { + if (data.lightning.distance != null && data.lightning.distance.value != 255) + { + station.LightningStrikesToday = data.lightning.count.value; + station.LightningDistance = ConvertKmtoUserUnits(data.lightning.distance.value); + + var tim = Utils.FromUnixTime(data.lightning.distance.time); + + if (tim > LightningTime) + { + station.LightningTime = tim; + } + } + } + } + + private void ProcessLeak(EcowittApi.CurrentDataData data, WeatherStation station) + { + if (data.water_leak != null) + { + if (data.water_leak.leak_ch1 != null) + { + station.DoLeakSensor(data.water_leak.leak_ch1.value, 1); + } + + if (data.water_leak.leak_ch2 != null) + { + station.DoLeakSensor(data.water_leak.leak_ch2.value, 2); + } + + if (data.water_leak.leak_ch3 != null) + { + station.DoLeakSensor(data.water_leak.leak_ch3.value, 3); + } + + if (data.water_leak.leak_ch4 != null) + { + station.DoLeakSensor(data.water_leak.leak_ch4.value, 4); + } + } + } + + private void ProcessBatteries(EcowittApi.CurrentDataData data) + { + var lowBatt = false; + + if (data.battery != null) + { + lowBatt = lowBatt || (data.battery.t_rh_p_sensor != null && data.battery.t_rh_p_sensor.value == 1); // flag + lowBatt = lowBatt || (data.battery.ws1900_console != null && data.battery.ws1900_console.value < 1.2); // volts - val ??? + lowBatt = lowBatt || (data.battery.ws1800_console != null && data.battery.ws1800_console.value < 1.2); // volts - val ??? + lowBatt = lowBatt || (data.battery.ws6006_console != null && data.battery.ws6006_console.value < 15); // % val ??? + lowBatt = lowBatt || (data.battery.console != null && data.battery.console.value < 2.4); // volts val ??? + lowBatt = lowBatt || (data.battery.outdoor_t_rh_sensor != null && data.battery.outdoor_t_rh_sensor.value == 1); // flag + lowBatt = lowBatt || (data.battery.wind_sensor != null && data.battery.wind_sensor.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.haptic_array_battery != null && data.battery.haptic_array_battery.value < 2.4); // volts + //lowBatt = lowBatt || (data.battery.haptic_array_capacitor != null && data.battery.haptic_array_capacitor.value == 2.4); // volts + lowBatt = lowBatt || (data.battery.sonic_array != null && data.battery.sonic_array.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.rainfall_sensor != null && data.battery.rainfall_sensor.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.sensor_array != null && data.battery.sensor_array.value == 1); // flag + lowBatt = lowBatt || (data.battery.lightning_sensor != null && data.battery.lightning_sensor.value == 1); // flag + lowBatt = lowBatt || (data.battery.aqi_combo_sensor != null && data.battery.aqi_combo_sensor.value == 1); // flag + lowBatt = lowBatt || (data.battery.water_leak_sensor_ch1 != null && data.battery.water_leak_sensor_ch1.value == 1); // flag + lowBatt = lowBatt || (data.battery.water_leak_sensor_ch2 != null && data.battery.water_leak_sensor_ch2.value == 1); // flag + lowBatt = lowBatt || (data.battery.water_leak_sensor_ch3 != null && data.battery.water_leak_sensor_ch3.value == 1); // flag + lowBatt = lowBatt || (data.battery.water_leak_sensor_ch4 != null && data.battery.water_leak_sensor_ch4.value == 1); // flag + lowBatt = lowBatt || (data.battery.pm25_sensor_ch1 != null && data.battery.pm25_sensor_ch1.value == 1); // flag + lowBatt = lowBatt || (data.battery.pm25_sensor_ch2 != null && data.battery.pm25_sensor_ch2.value == 1); // flag + lowBatt = lowBatt || (data.battery.pm25_sensor_ch3 != null && data.battery.pm25_sensor_ch3.value == 1); // flag + lowBatt = lowBatt || (data.battery.pm25_sensor_ch4 != null && data.battery.pm25_sensor_ch4.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch1 != null && data.battery.temp_humidity_sensor_ch1.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch2 != null && data.battery.temp_humidity_sensor_ch2.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch3 != null && data.battery.temp_humidity_sensor_ch3.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch4 != null && data.battery.temp_humidity_sensor_ch4.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch5 != null && data.battery.temp_humidity_sensor_ch5.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch6 != null && data.battery.temp_humidity_sensor_ch6.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch7 != null && data.battery.temp_humidity_sensor_ch7.value == 1); // flag + lowBatt = lowBatt || (data.battery.temp_humidity_sensor_ch8 != null && data.battery.temp_humidity_sensor_ch8.value == 1); // flag + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch1 != null && data.battery.soilmoisture_sensor_ch1.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch2 != null && data.battery.soilmoisture_sensor_ch2.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch3 != null && data.battery.soilmoisture_sensor_ch3.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch4 != null && data.battery.soilmoisture_sensor_ch4.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch5 != null && data.battery.soilmoisture_sensor_ch5.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch6 != null && data.battery.soilmoisture_sensor_ch6.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch7 != null && data.battery.soilmoisture_sensor_ch7.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.soilmoisture_sensor_ch8 != null && data.battery.soilmoisture_sensor_ch8.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch1 != null && data.battery.temperature_sensor_ch1.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch2 != null && data.battery.temperature_sensor_ch2.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch3 != null && data.battery.temperature_sensor_ch3.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch4 != null && data.battery.temperature_sensor_ch4.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch5 != null && data.battery.temperature_sensor_ch5.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch6 != null && data.battery.temperature_sensor_ch6.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch7 != null && data.battery.temperature_sensor_ch7.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.temperature_sensor_ch8 != null && data.battery.temperature_sensor_ch8.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch1 != null && data.battery.leaf_wetness_sensor_ch1.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch2 != null && data.battery.leaf_wetness_sensor_ch2.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch3 != null && data.battery.leaf_wetness_sensor_ch3.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch4 != null && data.battery.leaf_wetness_sensor_ch4.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch5 != null && data.battery.leaf_wetness_sensor_ch5.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch6 != null && data.battery.leaf_wetness_sensor_ch6.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch7 != null && data.battery.leaf_wetness_sensor_ch7.value < 1.2); // volts + lowBatt = lowBatt || (data.battery.leaf_wetness_sensor_ch8 != null && data.battery.leaf_wetness_sensor_ch8.value < 1.2); // volts + + cumulus.BatteryLowAlarm.Triggered = lowBatt; + } + } + } +} diff --git a/CumulusMX/EmailSender.cs b/CumulusMX/EmailSender.cs index 9159c9f1..1edf627b 100644 --- a/CumulusMX/EmailSender.cs +++ b/CumulusMX/EmailSender.cs @@ -1,14 +1,16 @@ using System; -using MailKit.Net.Smtp; -using MimeKit; +using System.CodeDom; +using System.CodeDom.Compiler; +using System.IO; using System.Text.RegularExpressions; -using MailKit; using System.Threading; -using System.IO; -using System.CodeDom.Compiler; -using System.CodeDom; using System.Threading.Tasks; +using MailKit; +using MailKit.Net.Smtp; + +using MimeKit; + namespace CumulusMX { public class EmailSender @@ -69,7 +71,7 @@ public async Task SendEmail(string[] to, string from, string subject, stri client.ServerCertificateValidationCallback = (s, c, h, e) => true; } - await client.ConnectAsync(cumulus.SmtpOptions.Server, cumulus.SmtpOptions.Port, (MailKit.Security.SecureSocketOptions)cumulus.SmtpOptions.SslOption); + await client.ConnectAsync(cumulus.SmtpOptions.Server, cumulus.SmtpOptions.Port, (MailKit.Security.SecureSocketOptions) cumulus.SmtpOptions.SslOption); // Note: since we don't have an OAuth2 token, disable // the XOAUTH2 authentication mechanism. @@ -88,7 +90,7 @@ public async Task SendEmail(string[] to, string from, string subject, stri } catch (Exception e) { - cumulus.LogMessage("SendEmail: Error - " + e); + cumulus.LogErrorMessage("SendEmail: Error - " + e); } finally { @@ -132,7 +134,7 @@ public string SendTestEmail(string[] to, string from, string subject, string mes using (SmtpClient client = cumulus.SmtpOptions.Logging ? new SmtpClient(new ProtocolLogger("MXdiags/smtp.log")) : new SmtpClient()) { - client.Connect(cumulus.SmtpOptions.Server, cumulus.SmtpOptions.Port, (MailKit.Security.SecureSocketOptions)cumulus.SmtpOptions.SslOption); + client.Connect(cumulus.SmtpOptions.Server, cumulus.SmtpOptions.Port, (MailKit.Security.SecureSocketOptions) cumulus.SmtpOptions.SslOption); //client.Connect(cumulus.SmtpOptions.Server, cumulus.SmtpOptions.Port, MailKit.Security.SecureSocketOptions.StartTlsWhenAvailable); // Note: since we don't have an OAuth2 token, disable @@ -153,7 +155,7 @@ public string SendTestEmail(string[] to, string from, string subject, string mes } catch (Exception e) { - cumulus.LogMessage("SendEmail: Error - " + e); + cumulus.LogErrorMessage("SendEmail: Error - " + e); retVal = e.Message; } finally diff --git a/CumulusMX/ExtraSensorSettings.cs b/CumulusMX/ExtraSensorSettings.cs index ab0b3515..73042538 100644 --- a/CumulusMX/ExtraSensorSettings.cs +++ b/CumulusMX/ExtraSensorSettings.cs @@ -1,9 +1,12 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net; -using ServiceStack; + using EmbedIO; +using ServiceStack; + namespace CumulusMX { public class ExtraSensorSettings @@ -85,6 +88,20 @@ public string GetAlpacaFormData() mappings = ecowittwn34map }; + ecowitt.forwarders = new JsonExtraSensorForwarders() + { + usemain = cumulus.EcowittExtraUseMainForwarders + }; + + ecowitt.forwarders.forward = new List(); + for (var i = 0; i < 10; i++) + { + if (!string.IsNullOrEmpty(cumulus.EcowittExtraForwarders[i])) + { + ecowitt.forwarders.forward.Add(new JsonEcowittForwardList() { url = cumulus.EcowittExtraForwarders[i] }); + } + } + var ambient = new JsonExtraSensorAmbient() { useSolar = cumulus.AmbientExtraUseSolar, @@ -109,6 +126,8 @@ public string GetAlpacaFormData() httpStation.extraStation = 0; else if (cumulus.AmbientExtraEnabled) httpStation.extraStation = 1; + else if (cumulus.EcowittCloudExtraEnabled) + httpStation.extraStation = 2; else httpStation.extraStation = -1; @@ -181,7 +200,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing ExtraSensor Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("ExtraSensor Data: " + json); context.Response.StatusCode = 500; return msg; @@ -201,7 +220,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing General settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -240,7 +259,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing AirLink settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -248,9 +267,11 @@ public string UpdateConfig(IHttpContext context) // Ecowitt Extra settings try { - if (settings.httpSensors.extraStation == 0) + if (settings.httpSensors.extraStation == 0 || settings.httpSensors.extraStation == 2) { - cumulus.EcowittExtraEnabled = true; + cumulus.EcowittExtraEnabled = settings.httpSensors.extraStation == 0; + cumulus.EcowittCloudExtraEnabled = settings.httpSensors.extraStation == 2; + cumulus.EcowittExtraUseSolar = settings.httpSensors.ecowitt.useSolar; cumulus.EcowittExtraUseUv = settings.httpSensors.ecowitt.useUv; cumulus.EcowittExtraUseTempHum = settings.httpSensors.ecowitt.useTempHum; @@ -350,6 +371,23 @@ public string UpdateConfig(IHttpContext context) cumulus.EcowittMapWN34[8] = settings.httpSensors.ecowitt.mappings.wn34chan8; } + cumulus.EcowittExtraUseMainForwarders = settings.httpSensors.ecowitt.forwarders.usemain; + + if (!cumulus.EcowittExtraUseMainForwarders) + { + for (var i = 0; i < 10; i++) + { + if (i < settings.httpSensors.ecowitt.forwarders.forward.Count) + { + cumulus.EcowittExtraForwarders[i] = string.IsNullOrWhiteSpace(settings.httpSensors.ecowitt.forwarders.forward[i].url) ? null : settings.httpSensors.ecowitt.forwarders.forward[i].url.Trim(); + } + else + { + cumulus.EcowittExtraForwarders[i] = null; + } + } + } + // Also enable extra logging if applicable if (cumulus.EcowittExtraUseTempHum || cumulus.EcowittExtraUseSoilTemp || cumulus.EcowittExtraUseSoilMoist || cumulus.EcowittExtraUseLeafWet || cumulus.EcowittExtraUseUserTemp || cumulus.EcowittExtraUseAQI || cumulus.EcowittExtraUseCo2) { @@ -362,7 +400,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Ecowitt settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -396,7 +434,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Ambient settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -409,7 +447,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Blake-Larsen settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -440,7 +478,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing RG-11 settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -451,7 +489,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Extra Sensor settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Extra Sensor Data: " + json); errorMsg += msg; context.Response.StatusCode = 500; @@ -524,6 +562,7 @@ public class JsonExtraSensorEcowitt : JsonExtraSensorAmbient public string localaddr { get; set; } public int interval { get; set; } public JsonStationSettingsEcowittMappings mappings { get; set; } + public JsonExtraSensorForwarders forwarders { get; set; } } diff --git a/CumulusMX/FOStation.cs b/CumulusMX/FOStation.cs index e18c3d86..e3be9f7e 100644 --- a/CumulusMX/FOStation.cs +++ b/CumulusMX/FOStation.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; -using System.IO.Ports; using System.Threading; using System.Timers; + using HidSharp; + using Timer = System.Timers.Timer; namespace CumulusMX @@ -97,16 +98,38 @@ internal FOStation(Cumulus cumulus) : base(cumulus) { var msg = $"Warning, your console logging interval ({logint} mins) does not match the Cumulus logging interval ({cumulus.logints[cumulus.DataLogInterval]} mins)"; cumulus.LogConsoleMessage(msg); - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); if (cumulus.FineOffsetOptions.SetLoggerInterval) { - WriteAddress(0x10, (byte)cumulus.logints[cumulus.DataLogInterval]); // write the logging new logging interval - WriteAddress(0x1A, 0xAA); // tell the station to read the new parameter + var retries = 2; + + cumulus.LogMessage($"Attempting to set console logging interval to {cumulus.logints[cumulus.DataLogInterval]} mins"); do { - Thread.Sleep(1000); // sleep to let it reconfigure - ReadAddress(0x10, data); - } while (data[9] != 0); + WriteAddress(0x10, (byte) cumulus.logints[cumulus.DataLogInterval]); // write the logging new logging interval + WriteAddress(0x1A, 0xAA); // tell the station to read the new parameter + + int readAttempts = 3; + + do + { + Thread.Sleep(1000); // sleep to let it reconfigure + ReadAddress(0x10, data); + readAttempts--; + } while (data[0] != cumulus.logints[cumulus.DataLogInterval] && readAttempts >= 0); + + + retries--; + } while (data[0] != cumulus.logints[cumulus.DataLogInterval] && retries >= 0); + + if (data[0] == cumulus.logints[cumulus.DataLogInterval]) + { + cumulus.LogMessage("Successfully set new logging interval"); + } + else + { + cumulus.LogWarningMessage("Failed to set new logging interval"); + } } } } @@ -185,7 +208,7 @@ private void bw_DoWork(object sender, DoWorkEventArgs e) } catch (Exception ex) { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); } //cumulus.LogDebugMessage("Lock: Station releasing the lock"); Cumulus.syncInit.Release(); @@ -288,27 +311,27 @@ public override void getAndProcessHistoryData() histData.followinginterval = followinginterval; histData.inHum = data[1] == 255 ? 10 : data[1]; histData.outHum = data[4] == 255 ? 10 : data[4]; - double outtemp = (data[5] + (data[6] & 0x7F)*256)/10.0f; + double outtemp = (data[5] + (data[6] & 0x7F) * 256) / 10.0f; var sign = (byte) (data[6] & 0x80); if (sign == 0x80) outtemp = -outtemp; if (outtemp > -200) histData.outTemp = outtemp; - histData.windGust = (data[10] + ((data[11] & 0xF0)*16))/10.0f; - histData.windSpeed = (data[9] + ((data[11] & 0x0F)*256))/10.0f; - histData.windBearing = (int) (data[12]*22.5f); + histData.windGust = (data[10] + ((data[11] & 0xF0) * 16)) / 10.0f; + histData.windSpeed = (data[9] + ((data[11] & 0x0F) * 256)) / 10.0f; + histData.windBearing = (int) (data[12] * 22.5f); - histData.rainCounter = data[13] + (data[14]*256); + histData.rainCounter = data[13] + (data[14] * 256); - double intemp = (data[2] + (data[3] & 0x7F)*256)/10.0f; + double intemp = (data[2] + (data[3] & 0x7F) * 256) / 10.0f; sign = (byte) (data[3] & 0x80); if (sign == 0x80) intemp = -intemp; histData.inTemp = intemp; // Get pressure and convert to sea level - histData.pressure = (data[7] + (data[8]*256))/10.0f + pressureOffset; + histData.pressure = (data[7] + (data[8] * 256)) / 10.0f + pressureOffset; histData.SensorContactLost = (data[15] & 0x40) == 0x40; if (hasSolar) { histData.uvVal = data[19]; - histData.solarVal = (data[16] + (data[17]*256) + (data[18]*65536))/10.0; + histData.solarVal = (data[16] + (data[17] * 256) + (data[18] * 65536)) / 10.0; } datalist.Add(histData); @@ -402,7 +425,7 @@ private void ProcessHistoryData() if (historydata.inHum > 100 || historydata.inHum < 0) { // 255 is the overflow value, when RH gets below 10% - ignore - cumulus.LogMessage("Ignoring bad data: inhum = " + historydata.inHum); + cumulus.LogWarningMessage("Ignoring bad data: inhum = " + historydata.inHum); } else { @@ -412,7 +435,7 @@ private void ProcessHistoryData() // Indoor Temperature =================================================== if (historydata.inTemp < -50 || historydata.inTemp > 50) { - cumulus.LogMessage("Ignoring bad data: intemp = " + historydata.inTemp); + cumulus.LogWarningMessage("Ignoring bad data: intemp = " + historydata.inTemp); } else { @@ -423,7 +446,7 @@ private void ProcessHistoryData() if ((historydata.pressure < cumulus.EwOptions.MinPressMB) || (historydata.pressure > cumulus.EwOptions.MaxPressMB)) { - cumulus.LogMessage("Ignoring bad data: pressure = " + historydata.pressure); + cumulus.LogWarningMessage("Ignoring bad data: pressure = " + historydata.pressure); cumulus.LogMessage(" offset = " + pressureOffset); } else @@ -433,7 +456,7 @@ private void ProcessHistoryData() if (historydata.SensorContactLost) { - cumulus.LogMessage("Sensor contact lost; ignoring outdoor data"); + cumulus.LogWarningMessage("Sensor contact lost; ignoring outdoor data"); } else { @@ -441,7 +464,7 @@ private void ProcessHistoryData() if (historydata.outHum > 100 || historydata.outHum < 0) { // 255 is the overflow value, when RH gets below 10% - ignore - cumulus.LogMessage("Ignoring bad data: outhum = " + historydata.outHum); + cumulus.LogWarningMessage("Ignoring bad data: outhum = " + historydata.outHum); } else { @@ -451,11 +474,11 @@ private void ProcessHistoryData() // Wind ================================================================= if (historydata.windGust > 60 || historydata.windGust < 0) { - cumulus.LogMessage("Ignoring bad data: gust = " + historydata.windGust); + cumulus.LogWarningMessage("Ignoring bad data: gust = " + historydata.windGust); } else if (historydata.windSpeed > 60 || historydata.windSpeed < 0) { - cumulus.LogMessage("Ignoring bad data: speed = " + historydata.windSpeed); + cumulus.LogWarningMessage("Ignoring bad data: speed = " + historydata.windSpeed); } else { @@ -465,14 +488,14 @@ private void ProcessHistoryData() // Outdoor Temperature ================================================== if (historydata.outTemp < -50 || historydata.outTemp > 70) { - cumulus.LogMessage("Ignoring bad data: outtemp = " + historydata.outTemp); + cumulus.LogWarningMessage("Ignoring bad data: outtemp = " + historydata.outTemp); } else { DoOutdoorTemp(ConvertTempCToUser(historydata.outTemp), timestamp); // add in 'archivePeriod' minutes worth of temperature to the temp samples tempsamplestoday += historydata.interval; - TempTotalToday += (OutdoorTemperature*historydata.interval); + TempTotalToday += (OutdoorTemperature * historydata.interval); } // update chill hours @@ -504,7 +527,7 @@ private void ProcessHistoryData() if (raindiff > 100) { - cumulus.LogMessage("Warning: large increase in rain gauge tip count: " + raindiff); + cumulus.LogWarningMessage("Warning: large increase in rain gauge tip count: " + raindiff); rainrate = 0; } else @@ -519,7 +542,7 @@ private void ProcessHistoryData() } } - DoRain(ConvertRainMMToUser(historydata.rainCounter*0.3), rainrate, timestamp); + DoRain(ConvertRainMMToUser(historydata.rainCounter * 0.3), rainrate, timestamp); prevraintotal = historydata.rainCounter; @@ -548,32 +571,32 @@ private void ProcessHistoryData() { if (historydata.uvVal < 0 || historydata.uvVal > 16) { - cumulus.LogMessage("Invalid UV-I value ignored: " + historydata.uvVal); + cumulus.LogWarningMessage("Invalid UV-I value ignored: " + historydata.uvVal); } else DoUV(historydata.uvVal, timestamp); if (historydata.solarVal >= 0 && historydata.solarVal <= 300000) { - DoSolarRad((int)Math.Floor(historydata.solarVal * cumulus.SolarOptions.LuxToWM2), timestamp); + DoSolarRad((int) Math.Floor(historydata.solarVal * cumulus.SolarOptions.LuxToWM2), timestamp); // add in archive period worth of sunshine, if sunny if ((SolarRad > CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100) && (SolarRad >= cumulus.SolarOptions.SolarMinimum)) - SunshineHours += (historydata.interval/60.0); + SunshineHours += (historydata.interval / 60.0); LightValue = historydata.solarVal; } else { - cumulus.LogMessage("Invalid solar value ignored: " + historydata.solarVal); + cumulus.LogWarningMessage("Invalid solar value ignored: " + historydata.solarVal); } } } // add in 'following interval' minutes worth of wind speed to windrun cumulus.LogMessage("Windrun: " + WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + historydata.followinginterval + " minutes = " + - (WindAverage*WindRunHourMult[cumulus.Units.Wind]*historydata.followinginterval/60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); + (WindAverage * WindRunHourMult[cumulus.Units.Wind] * historydata.followinginterval / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); - WindRunToday += (WindAverage*WindRunHourMult[cumulus.Units.Wind]*historydata.followinginterval/60.0); + WindRunToday += (WindAverage * WindRunHourMult[cumulus.Units.Wind] * historydata.followinginterval / 60.0); // update heating/cooling degree days UpdateDegreeDays(historydata.interval); @@ -583,9 +606,9 @@ private void ProcessHistoryData() CheckForWindrunHighLow(timestamp); - bw.ReportProgress((totalentries - datalist.Count)*100/totalentries, "processing"); + bw.ReportProgress((totalentries - datalist.Count) * 100 / totalentries, "processing"); - cumulus.DoLogFile(timestamp,false); + cumulus.DoLogFile(timestamp, false); cumulus.DoCustomIntervalLogs(timestamp); if (cumulus.StationOptions.LogExtraSensors) @@ -613,7 +636,7 @@ private void ProcessHistoryData() { processedCount++; - Console.Write("\r - processed " + (((double)processedCount) / recCount).ToString("P0")); + Console.Write("\r - processed " + (((double) processedCount) / recCount).ToString("P0")); } } @@ -664,13 +687,13 @@ private bool OpenHidDevice() } else { - cumulus.LogMessage("Stream open failed"); + cumulus.LogErrorMessage("Stream open failed"); return false; } } else { - cumulus.LogMessage("*** Fine Offset station not found ***"); + cumulus.LogErrorMessage("*** Fine Offset station not found ***"); cumulus.LogMessage("Found the following USB HID Devices..."); int cnt = 0; foreach (HidDevice device in devicelist.GetHidDevices()) @@ -681,7 +704,7 @@ private bool OpenHidDevice() if (cnt == 0) { - cumulus.LogMessage("No USB HID devices found!"); + cumulus.LogErrorMessage("No USB HID devices found!"); } return false; @@ -705,7 +728,7 @@ private bool ReadAddress(int address, byte[] buff) const int responseLength = 9; const int startByte = 1; - var request = new byte[] {0, 0xa1, highbyte, lowbyte, 0x20, 0xa1, highbyte, lowbyte, 0x20}; + var request = new byte[] { 0, 0xa1, highbyte, lowbyte, 0x20, 0xa1, highbyte, lowbyte, 0x20 }; int ptr = 0; @@ -731,7 +754,7 @@ private bool ReadAddress(int address, byte[] buff) { cumulus.LogConsoleMessage("Error sending command to station - it may need resetting", ConsoleColor.Red, true); cumulus.LogMessage(ex.Message); - cumulus.LogMessage("Error sending command to station - it may need resetting"); + cumulus.LogErrorMessage("Error sending command to station - it may need resetting"); if (!DataStopped) { DataStoppedTime = DateTime.Now; @@ -754,7 +777,7 @@ private bool ReadAddress(int address, byte[] buff) { cumulus.LogConsoleMessage("Error reading data from station - it may need resetting", ConsoleColor.Red, true); cumulus.LogMessage(ex.Message); - cumulus.LogMessage("Error reading data from station - it may need resetting"); + cumulus.LogErrorMessage("Error reading data from station - it may need resetting"); if (!DataStopped) { DataStoppedTime = DateTime.Now; @@ -765,7 +788,7 @@ private bool ReadAddress(int address, byte[] buff) return false; } - var recData = " Data" + i + ": " + BitConverter.ToString(response, startByte, responseLength - startByte); + var recData = " Data" + i + ": " + BitConverter.ToString(response, startByte, responseLength - startByte); for (int j = startByte; j < responseLength; j++) { buff[ptr++] = response[j]; @@ -777,8 +800,8 @@ private bool ReadAddress(int address, byte[] buff) private bool WriteAddress(int address, byte val) { - var addrlowbyte = (byte)(address & 0xFF); - var addrhighbyte = (byte)(address >> 8); + var addrlowbyte = (byte) (address & 0xFF); + var addrhighbyte = (byte) (address >> 8); var request = new byte[] { 0, 0xa2, addrhighbyte, addrlowbyte, 0x20, 0xa2, val, 0, 0x20 }; @@ -796,7 +819,7 @@ private bool WriteAddress(int address, byte val) { cumulus.LogConsoleMessage("Error sending command to station - it may need resetting"); cumulus.LogMessage(ex.Message); - cumulus.LogMessage("Error sending command to station - it may need resetting"); + cumulus.LogErrorMessage("Error sending command to station - it may need resetting"); if (!DataStopped) { DataStoppedTime = DateTime.Now; @@ -827,7 +850,7 @@ private void DataReadTimerTick(object state, ElapsedEventArgs elapsedEventArgs) if (!OpenHidDevice()) { - cumulus.LogMessage("Failed to reopen the USB device"); + cumulus.LogErrorMessage("Failed to reopen the USB device"); return; } } @@ -931,7 +954,7 @@ private void GetAndProcessData() // Check that were not within N seconds of the station updating memory if (FOSensorClockTime != DateTime.MinValue) { - secsToSkip = (int)(Math.Floor(now.Subtract(FOSensorClockTime).TotalSeconds)) % 48; + secsToSkip = (int) (Math.Floor(now.Subtract(FOSensorClockTime).TotalSeconds)) % 48; sensorclockOK = (secsToSkip >= (cumulus.FineOffsetOptions.ReadAvoidPeriod - 1)) && (secsToSkip <= (47 - cumulus.FineOffsetOptions.ReadAvoidPeriod)); } @@ -939,7 +962,7 @@ private void GetAndProcessData() bool stationclockOK = true; if (FOStationClockTime != DateTime.MinValue) { - secsToSkip = (int)(Math.Floor(now.Subtract(FOStationClockTime).TotalSeconds)) % 60; + secsToSkip = (int) (Math.Floor(now.Subtract(FOStationClockTime).TotalSeconds)) % 60; stationclockOK = (secsToSkip >= (cumulus.FineOffsetOptions.ReadAvoidPeriod - 1)) && (secsToSkip <= (59 - cumulus.FineOffsetOptions.ReadAvoidPeriod)); } @@ -948,7 +971,7 @@ private void GetAndProcessData() bool solarclockOK = true; if (hasSolar && FOSolarClockTime != DateTime.MinValue) { - secsToSkip = (int)(Math.Floor(now.Subtract(FOSolarClockTime).TotalSeconds)) % 60; + secsToSkip = (int) (Math.Floor(now.Subtract(FOSolarClockTime).TotalSeconds)) % 60; solarclockOK = (secsToSkip >= (cumulus.FineOffsetOptions.ReadAvoidPeriod - 1)) && (secsToSkip <= (59 - cumulus.FineOffsetOptions.ReadAvoidPeriod)); } @@ -1000,7 +1023,7 @@ private void GetAndProcessData() return; } - int addr = (data[31]*256) + data[30]; + int addr = (data[31] * 256) + data[30]; cumulus.LogDataMessage("First block read, addr = " + addr.ToString("X4")); @@ -1155,7 +1178,7 @@ private void GetAndProcessData() } } - readCounter++; + readCounter++; } @@ -1169,7 +1192,7 @@ private void GetAndProcessData() if (inhum > 100 || inhum < 0) { // bad value - cumulus.LogMessage("Ignoring bad data: inhum = " + inhum); + cumulus.LogWarningMessage("Ignoring bad data: inhum = " + inhum); } else { @@ -1186,7 +1209,7 @@ private void GetAndProcessData() } // Indoor temperature =============================================== - double intemp = ((data[2]) + (data[3] & 0x7F)*256)/10.0f; + double intemp = ((data[2]) + (data[3] & 0x7F) * 256) / 10.0f; var sign = (byte) (data[3] & 0x80); if (sign == 0x80) { @@ -1195,7 +1218,7 @@ private void GetAndProcessData() if (intemp < -50 || intemp > 50) { - cumulus.LogMessage("Ignoring bad data: intemp = " + intemp); + cumulus.LogWarningMessage("Ignoring bad data: intemp = " + intemp); } else { @@ -1203,12 +1226,12 @@ private void GetAndProcessData() } // Pressure ========================================================= - double pressure = (data[7] + ((data[8] & 0x3f)*256))/10.0f + pressureOffset; + double pressure = (data[7] + ((data[8] & 0x3f) * 256)) / 10.0f + pressureOffset; if (pressure < cumulus.EwOptions.MinPressMB || pressure > cumulus.EwOptions.MaxPressMB) { // bad value - cumulus.LogMessage("Ignoring bad data: pressure = " + pressure); + cumulus.LogWarningMessage("Ignoring bad data: pressure = " + pressure); cumulus.LogMessage(" offset = " + pressureOffset); } else @@ -1218,7 +1241,7 @@ private void GetAndProcessData() // EWpressure offset is difference between rel and abs in hPa // PressOffset is user calibration in user units. var offsetPress = pressure - pressureOffset; - pressure = offsetPress * offsetPress * cumulus.Calib.Press.Mult2 + offsetPress * cumulus.Calib.Press.Mult + ConvertUserPressureToHPa(cumulus.Calib.Press.Offset); + pressure = offsetPress * offsetPress * cumulus.Calib.Press.Mult2 + offsetPress * cumulus.Calib.Press.Mult + ConvertUserPressureToHPa(cumulus.Calib.Press.Offset); StationPressure = ConvertPressMBToUser(pressure); @@ -1229,7 +1252,7 @@ private void GetAndProcessData() if ((status & 0x40) != 0) { SensorContactLost = true; - cumulus.LogMessage("Sensor contact lost; ignoring outdoor data"); + cumulus.LogWarningMessage("Sensor contact lost; ignoring outdoor data"); } else { @@ -1240,7 +1263,7 @@ private void GetAndProcessData() if (outhum > 100 || outhum < 0) { // bad value - cumulus.LogMessage("Ignoring bad data: outhum = " + outhum); + cumulus.LogWarningMessage("Ignoring bad data: outhum = " + outhum); } else { @@ -1257,19 +1280,19 @@ private void GetAndProcessData() } // Wind ============================================================= - double gust = (data[10] + ((data[11] & 0xF0)*16))/10.0f; - double windspeed = (data[9] + ((data[11] & 0x0F)*256))/10.0f; - var winddir = (int) (data[12]*22.5f); + double gust = (data[10] + ((data[11] & 0xF0) * 16)) / 10.0f; + double windspeed = (data[9] + ((data[11] & 0x0F) * 256)) / 10.0f; + var winddir = (int) (data[12] * 22.5f); if (gust > 60 || gust < 0) { // bad value - cumulus.LogMessage("Ignoring bad data: gust = " + gust); + cumulus.LogWarningMessage("Ignoring bad data: gust = " + gust); } else if (windspeed > 60 || windspeed < 0) { // bad value - cumulus.LogMessage("Ignoring bad data: speed = " + gust); + cumulus.LogWarningMessage("Ignoring bad data: speed = " + gust); } else { @@ -1277,14 +1300,14 @@ private void GetAndProcessData() } // Outdoor Temperature ============================================== - double outtemp = ((data[5]) + (data[6] & 0x7F)*256)/10.0f; + double outtemp = ((data[5]) + (data[6] & 0x7F) * 256) / 10.0f; sign = (byte) (data[6] & 0x80); if (sign == 0x80) outtemp = -outtemp; if (outtemp < -50 || outtemp > 70) { // bad value - cumulus.LogMessage("Ignoring bad data: outtemp = " + outtemp); + cumulus.LogWarningMessage("Ignoring bad data: outtemp = " + outtemp); } else { @@ -1316,7 +1339,7 @@ private void GetAndProcessData() } // Rain ============================================================ - int raintot = data[13] + (data[14]*256); + int raintot = data[13] + (data[14] * 256); if (prevraintotal == -1) { // first reading @@ -1328,16 +1351,16 @@ private void GetAndProcessData() if (raindiff > cumulus.EwOptions.MaxRainTipDiff) { - cumulus.LogMessage("Warning: large difference in rain gauge tip count: " + raindiff); + cumulus.LogWarningMessage("Warning: large difference in rain gauge tip count: " + raindiff); ignoreraincount++; if (ignoreraincount == 6) { cumulus.LogMessage("Six consecutive rain readings; accepting value. Adjusting start of day figure to compensate"); - raindaystart += (raindiff*0.3); + raindaystart += (raindiff * 0.3); // adjust current rain total counter - Raincounter += (raindiff*0.3); + Raincounter += (raindiff * 0.3); cumulus.LogMessage("Setting raindaystart to " + raindaystart); ignoreraincount = 0; } @@ -1353,18 +1376,18 @@ private void GetAndProcessData() if (ignoreraincount == 0) { - DoRain(ConvertRainMMToUser(raintot*0.3), -1, now); + DoRain(ConvertRainMMToUser(raintot * 0.3), -1, now); prevraintotal = raintot; } // Solar/UV if (hasSolar) { - LightValue = (data[16] + (data[17]*256) + (data[18]*65536))/10.0; + LightValue = (data[16] + (data[17] * 256) + (data[18] * 65536)) / 10.0; if (LightValue < 300000) { - DoSolarRad((int)(LightValue * cumulus.SolarOptions.LuxToWM2), now); + DoSolarRad((int) (LightValue * cumulus.SolarOptions.LuxToWM2), now); } int UVreading = data[19]; @@ -1436,7 +1459,7 @@ private void StopSynchronising() if (stationSyncDone && previousStationClock == DateTime.MinValue) { - secsdiff = (int)Math.Floor((FOStationClockTime - previousStationClock).TotalSeconds) % 60; + secsdiff = (int) Math.Floor((FOStationClockTime - previousStationClock).TotalSeconds) % 60; if (secsdiff > 30) { secsdiff = 60 - secsdiff; @@ -1458,7 +1481,7 @@ private void StopSynchronising() { if (solarSyncDone && previousSolarClock != DateTime.MinValue) { - secsdiff = (int)Math.Floor((FOSolarClockTime - previousSolarClock).TotalSeconds) % 60; + secsdiff = (int) Math.Floor((FOSolarClockTime - previousSolarClock).TotalSeconds) % 60; if (secsdiff > 30) { secsdiff = 60 - secsdiff; diff --git a/CumulusMX/GW1000Api.cs b/CumulusMX/GW1000Api.cs index 9744f12a..f054ea62 100644 --- a/CumulusMX/GW1000Api.cs +++ b/CumulusMX/GW1000Api.cs @@ -1,12 +1,8 @@ using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.Linq; using System.Net.Sockets; using System.Runtime.InteropServices; -using System.Text; using System.Threading; -using System.Threading.Tasks; + namespace CumulusMX { @@ -60,7 +56,7 @@ internal bool OpenTcpPort(string ipaddr, int port) } catch (Exception ex) { - cumulus.LogMessage("Error opening TCP port: " + ex.Message); + cumulus.LogErrorMessage("Error opening TCP port: " + ex.Message); Thread.Sleep(5000 * attempt); } } @@ -87,7 +83,7 @@ internal bool OpenTcpPort(string ipaddr, int port) } catch (Exception ex) { - cumulus.LogMessage("Error reconnecting Ecowitt Gateway: " + ex.Message); + cumulus.LogErrorMessage("Error reconnecting Ecowitt Gateway: " + ex.Message); } } else @@ -171,13 +167,13 @@ internal byte[] DoCommand(Commands command, byte[] data = null) { if (bytesRead > 0) { - cumulus.LogMessage($"DoCommand({cmdName}): Invalid response"); + cumulus.LogWarningMessage($"DoCommand({cmdName}): Invalid response"); cumulus.LogDebugMessage($"command resp={buffer[2]}, checksum=" + (ChecksumOk(buffer, (int) Enum.Parse(typeof(CommandRespSize), cmdName)) ? "OK" : "BAD")); cumulus.LogDataMessage("Received " + BitConverter.ToString(buffer, 0, bytesRead - 1)); } else { - cumulus.LogMessage($"DoCommand({cmdName}): No response received"); + cumulus.LogWarningMessage($"DoCommand({cmdName}): No response received"); } return null; } @@ -188,7 +184,7 @@ internal byte[] DoCommand(Commands command, byte[] data = null) } catch (Exception ex) { - cumulus.LogMessage($"DoCommand({cmdName}): Error - " + ex.Message); + cumulus.LogErrorMessage($"DoCommand({cmdName}): Error - " + ex.Message); cumulus.LogMessage("Attempting to reopen the TCP port"); Thread.Sleep(1000); OpenTcpPort(ipAddress, tcpPort); @@ -231,7 +227,7 @@ private bool ChecksumOk(byte[] data, int lengthBytes) // sanity check the size if (size + 3 + lengthBytes > data.Length) { - cumulus.LogMessage($"Ckecksum: Error - Calculated data length [{size}] exceeds the buffer size!"); + cumulus.LogErrorMessage($"Ckecksum: Error - Calculated data length [{size}] exceeds the buffer size!"); return false; } @@ -243,7 +239,7 @@ private bool ChecksumOk(byte[] data, int lengthBytes) if (checksum != data[size + 1]) { - cumulus.LogMessage("Checksum: Error - Bad checksum"); + cumulus.LogErrorMessage("Checksum: Error - Bad checksum"); return false; } @@ -602,7 +598,7 @@ public CommandWritePayload(Commands command, byte[] data) : this() Data[3] = (byte) (3 + data.Length); data.CopyTo(Data, 4); - var Checksum = (byte)(command + Data[3]); + var Checksum = (byte) (command + Data[3]); for (int i = 0; i < data.Length; i++) { Checksum += data[i]; @@ -613,17 +609,17 @@ public CommandWritePayload(Commands command, byte[] data) : this() internal static UInt16 ConvertBigEndianUInt16(byte[] array, int start) { - return (UInt16)(array[start] << 8 | array[start + 1]); + return (UInt16) (array[start] << 8 | array[start + 1]); } internal static Int16 ConvertBigEndianInt16(byte[] array, int start) { - return (Int16)((array[start] << 8) + array[start + 1]); + return (Int16) ((array[start] << 8) + array[start + 1]); } internal static UInt32 ConvertBigEndianUInt32(byte[] array, int start) { - return (UInt32)(array[start++] << 24 | array[start++] << 16 | array[start++] << 8 | array[start]); + return (UInt32) (array[start++] << 24 | array[start++] << 16 | array[start++] << 8 | array[start]); } internal static byte[] ConvertUInt16ToLittleEndianByteArray(UInt16 ui16) diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs index d134f00a..e4f0e0b1 100644 --- a/CumulusMX/GW1000Station.cs +++ b/CumulusMX/GW1000Station.cs @@ -1,15 +1,12 @@ using System; -using System.ComponentModel; +using System.Collections.Generic; +using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; -using System.Runtime.InteropServices; -using System.Net; -using System.Timers; -using System.Collections.Generic; -using System.Linq; -using System.Globalization; using System.Threading.Tasks; +using System.Timers; + using ServiceStack.Text; namespace CumulusMX @@ -39,7 +36,7 @@ internal class GW1000Station : WeatherStation //private readonly NumberFormatInfo invNum = CultureInfo.InvariantCulture.NumberFormat; private Version fwVersion; - private string gatewayType; + //private string gatewayType; public GW1000Station(Cumulus cumulus) : base(cumulus) { @@ -231,7 +228,7 @@ public override void getAndProcessHistoryData() if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) { - cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + cumulus.LogWarningMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); cumulus.LastUpdateTime = DateTime.Now; } else @@ -251,7 +248,7 @@ public override void getAndProcessHistoryData() } catch (Exception ex) { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); } } @@ -371,7 +368,7 @@ private Discovery DiscoverGW1000() catch (Exception ex) { cumulus.LogMessage("DiscoverGW1000: Error sending discovery request"); - cumulus.LogMessage("Error: " + ex.Message); + cumulus.LogErrorMessage("DiscoverGW1000: Error: " + ex.Message); } retryCount++; @@ -380,7 +377,7 @@ private Discovery DiscoverGW1000() } catch (Exception ex) { - cumulus.LogMessage("An error occurred during Ecowitt auto-discovery"); + cumulus.LogErrorMessage("An error occurred during Ecowitt auto-discovery"); cumulus.LogMessage("Error: " + ex.Message); } @@ -401,7 +398,7 @@ private bool DoDiscovery() { // We didn't find anything on the network msg = "Failed to discover any Ecowitt devices"; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); cumulus.LogConsoleMessage(msg, ConsoleColor.DarkYellow, true); return false; } @@ -411,7 +408,7 @@ private bool DoDiscovery() // If only one device is discovered, and its MAC address matches (or our MAC is blank), then just use it if (cumulus.Gw1000IpAddress != discoveredDevices.IP[0]) { - cumulus.LogMessage("Discovered a new IP address for the Ecowitt device that does not match our current one"); + cumulus.LogWarningMessage("Discovered a new IP address for the Ecowitt device that does not match our current one"); cumulus.LogMessage($"Changing previous IP address: {ipaddr} to {discoveredDevices.IP[0]}"); ipaddr = discoveredDevices.IP[0].Trim(); cumulus.Gw1000IpAddress = ipaddr; @@ -449,7 +446,7 @@ private bool DoDiscovery() string iplist = ""; msg = "Discovered more than one potential Ecowitt device."; - cumulus.LogMessage(msg); + cumulus.LogWarningMessage(msg); cumulus.LogConsoleMessage(msg); msg = "Please select the IP address from the list and enter it manually into the configuration"; cumulus.LogMessage(msg); @@ -471,7 +468,7 @@ private bool DoDiscovery() if (string.IsNullOrWhiteSpace(ipaddr)) { var msg = "No IP address configured or discovered for your GW1000, please remedy and restart Cumulus MX"; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogConsoleMessage(msg); return false; } @@ -492,7 +489,7 @@ private void PostDiscovery() } else { - cumulus.LogMessage("Not Connected"); + cumulus.LogErrorMessage("Not Connected"); cumulus.LogConsoleMessage("Unable to connect to station", ConsoleColor.Red, true); } @@ -506,7 +503,7 @@ private void PostDiscovery() var fwString = GW1000FirmwareVersion.Split(new string[] { "_V" }, StringSplitOptions.None); if (fwString.Length > 1) { - gatewayType = fwString[0]; + //gatewayType = fwString[0]; fwVersion = new Version(fwString[1]); } else @@ -536,7 +533,7 @@ private string GetFirmwareVersion() } catch (Exception ex) { - cumulus.LogMessage($"GetFirmwareVersion: Error retrieving/processing firmware version. Message - {ex.Message}"); + cumulus.LogErrorMessage($"GetFirmwareVersion: Error retrieving/processing firmware version. Message - {ex.Message}"); } return response; } @@ -588,7 +585,7 @@ private bool GetSensorIdsNew() } catch (Exception ex) { - cumulus.LogMessage("GetSensorIdsNew: Unexpected error - " + ex.Message); + cumulus.LogErrorMessage("GetSensorIdsNew: Unexpected error - " + ex.Message); // no idea, so report battery as good return false; } @@ -707,7 +704,7 @@ private bool PrintSensorInfoNew(byte[] data, int idx) // if a WS80 is connected, it has a 4.75 second update rate, so reduce the MX update rate from the default 10 seconds if (updateRate > 4000 && updateRate != 4000) { - cumulus.LogMessage($"PrintSensorInfoNew: WS80 sensor detected, changing the update rate from {(updateRate/1000):D} seconds to 4 seconds"); + cumulus.LogMessage($"PrintSensorInfoNew: WS80 sensor detected, changing the update rate from {(updateRate / 1000):D} seconds to 4 seconds"); updateRate = 4000; } battV = data[battPos] * 0.02; @@ -728,7 +725,7 @@ private bool PrintSensorInfoNew(byte[] data, int idx) } catch (Exception ex) { - cumulus.LogMessage("PrintSensorInfoNew: Error - " + ex.Message); + cumulus.LogErrorMessage("PrintSensorInfoNew: Error - " + ex.Message); } return batteryLow; @@ -830,7 +827,7 @@ private void GetLiveData() idx += 2; break; case 0x05: //Heat index (℃) - // cumulus calculates this + // cumulus calculates this idx += 2; break; case 0x06: //Indoor Humidity(%) @@ -903,10 +900,10 @@ private void GetLiveData() idx += 4; break; case 0x15: //Light (lux) - // Save the Lux value + // Save the Lux value LightValue = GW1000Api.ConvertBigEndianUInt32(data, idx) / 10.0; // convert Lux to W/m² - approximately! - DoSolarRad((int)(LightValue * cumulus.SolarOptions.LuxToWM2), dateTime); + DoSolarRad((int) (LightValue * cumulus.SolarOptions.LuxToWM2), dateTime); idx += 4; break; case 0x16: //UV (µW/cm²) - what use is this! @@ -917,7 +914,7 @@ private void GetLiveData() idx += 1; break; case 0x18: //Date and time - // does not appear to be implemented + // does not appear to be implemented idx += 6; break; case 0x19: //Day max wind(m/s) @@ -972,7 +969,7 @@ private void GetLiveData() case 0x45: //Soil Temperature14 (℃) case 0x47: //Soil Temperature15 (℃) case 0x49: //Soil Temperature16 (℃) - // figure out the channel number + // figure out the channel number chan = data[idx - 1] - 0x2B + 2; // -> 2,4,6,8... chan /= 2; // -> 1,2,3,4... tempInt16 = GW1000Api.ConvertBigEndianInt16(data, idx); @@ -995,14 +992,14 @@ private void GetLiveData() case 0x46: //Soil Moisture14 (%) case 0x48: //Soil Moisture15 (%) case 0x4A: //Soil Moisture16 (%) - // figure out the channel number + // figure out the channel number chan = data[idx - 1] - 0x2C + 2; // -> 2,4,6,8... chan /= 2; // -> 1,2,3,4... DoSoilMoisture(data[idx], chan); idx += 1; break; case 0x4C: //All sensor lowbatt 16 char - // This has been deprecated since v1.6.5 - now use CMD_READ_SENSOR_ID_NEW + // This has been deprecated since v1.6.5 - now use CMD_READ_SENSOR_ID_NEW /* if (tenMinuteChanged && fwVersion.CompareTo(new Version("1.6.5")) >= 0) { @@ -1103,7 +1100,7 @@ private void GetLiveData() if (volts <= 1.2) { batteryLow = true; - cumulus.LogMessage($"WN34 channel #{chan} battery LOW = {volts}V"); + cumulus.LogWarningMessage($"WN34 channel #{chan} battery LOW = {volts}V"); } else { @@ -1129,8 +1126,8 @@ private void GetLiveData() idx += 16; break; case 0x71: // Ambient ONLY - AQI - //TODO: Not doing anything with this yet - //idx += 2; // SEEMS TO BE VARIABLE + //TODO: Not doing anything with this yet + //idx += 2; // SEEMS TO BE VARIABLE cumulus.LogDebugMessage("Found a device 0x71 - Ambient AQI. No decode for this yet"); // We will have lost our place now, so bail out idx = size; @@ -1288,12 +1285,12 @@ private void GetLiveData() } else { - cumulus.LogMessage("GetLiveData: Invalid response"); + cumulus.LogWarningMessage("GetLiveData: Invalid response"); } } catch (Exception ex) { - cumulus.LogMessage("GetLiveData: Error - " + ex.Message); + cumulus.LogErrorMessage("GetLiveData: Error - " + ex.Message); } } @@ -1319,13 +1316,13 @@ private void GetSystemInfo(bool driftOnly) if (data == null) { - cumulus.LogMessage("Nothing returned from System Info!"); + cumulus.LogWarningMessage("Nothing returned from System Info!"); return; } if (data.Length != 13) { - cumulus.LogMessage("Unexpected response to System Info!"); + cumulus.LogWarningMessage("Unexpected response to System Info!"); return; } try @@ -1344,7 +1341,7 @@ private void GetSystemInfo(bool driftOnly) var mainSensor = data[5] == 0 ? "WH24" : "Other than WH24"; - var unix = (int)GW1000Api.ConvertBigEndianUInt32(data, 6); + var unix = (int) GW1000Api.ConvertBigEndianUInt32(data, 6); var date = Utils.FromUnixTime(unix); var autoDST = data[11] != 0; @@ -1352,7 +1349,7 @@ private void GetSystemInfo(bool driftOnly) var offset = TimeZone.CurrentTimeZone.GetUtcOffset(now); if (autoDST && TimeZoneInfo.Local.IsDaylightSavingTime(date)) { - unix -= (int)Math.Round(offset.TotalSeconds); + unix -= (int) Math.Round(offset.TotalSeconds); date = date.AddSeconds(-offset.TotalSeconds); } @@ -1374,7 +1371,7 @@ private void GetSystemInfo(bool driftOnly) } catch (Exception ex) { - cumulus.LogMessage("Error processing System Info: " + ex.Message); + cumulus.LogErrorMessage("Error processing System Info: " + ex.Message); } } @@ -1434,13 +1431,13 @@ private void GetPiezoRainData() if (data == null) { - cumulus.LogMessage("GetPiezoRainData: Nothing returned from Read Rain!"); + cumulus.LogErrorMessage("GetPiezoRainData: Nothing returned from Read Rain!"); return; } if (data.Length < 8) { - cumulus.LogMessage("GetPiezoRainData: Unexpected response to Read Rain!"); + cumulus.LogErrorMessage("GetPiezoRainData: Unexpected response to Read Rain!"); return; } try @@ -1539,13 +1536,13 @@ private void GetPiezoRainData() } else { - cumulus.LogMessage("GetPiezoRainData: Error, no piezo rain data found in the response"); + cumulus.LogErrorMessage("GetPiezoRainData: Error, no piezo rain data found in the response"); } } } catch (Exception ex) { - cumulus.LogMessage("GetPiezoRainData: Error processing Rain Info: " + ex.Message); + cumulus.LogErrorMessage("GetPiezoRainData: Error processing Rain Info: " + ex.Message); } } @@ -1592,7 +1589,7 @@ private bool DoCO2Decode(byte[] data, int index) } catch (Exception ex) { - cumulus.LogMessage("DoCO2Decode: Error - " + ex.Message); + cumulus.LogErrorMessage("DoCO2Decode: Error - " + ex.Message); } return batteryLow; @@ -1676,7 +1673,7 @@ private void DataTimeout(object source, ElapsedEventArgs e) } else { - cumulus.LogMessage($"ERROR: No data received from the GW1000 for {tmrDataWatchdog.Interval / 1000} seconds"); + cumulus.LogErrorMessage($"ERROR: No data received from the GW1000 for {tmrDataWatchdog.Interval / 1000} seconds"); if (!DataStopped) { DataStoppedTime = DateTime.Now; diff --git a/CumulusMX/GraphOptions.cs b/CumulusMX/GraphOptions.cs index 444c575d..772fdf9a 100644 --- a/CumulusMX/GraphOptions.cs +++ b/CumulusMX/GraphOptions.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Org.BouncyCastle.Asn1.Esf; + namespace CumulusMX { diff --git a/CumulusMX/HttpFiles.cs b/CumulusMX/HttpFiles.cs index 11872756..8e98f080 100644 --- a/CumulusMX/HttpFiles.cs +++ b/CumulusMX/HttpFiles.cs @@ -1,35 +1,29 @@ using System; using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; using System.Globalization; using System.IO; -using System.Linq; using System.Net; using System.Net.Http; -using System.Runtime.CompilerServices; -//using System.Net.Security; using System.Runtime.Serialization; -//using System.Security.Cryptography.X509Certificates; -using System.Text; using System.Threading.Tasks; -using System.Web.Hosting; + using EmbedIO; -using Org.BouncyCastle.Asn1.Cms; + using ServiceStack; using ServiceStack.Text; -using Swan.Formatters; -using static CumulusMX.Cumulus; + namespace CumulusMX { internal class HttpFiles { private readonly Cumulus cumulus; + private readonly WeatherStation station; - public HttpFiles(Cumulus cumulus) + public HttpFiles(Cumulus cumulus, WeatherStation station) { this.cumulus = cumulus; + this.station = station; } public string GetAlpacaFormData() @@ -79,7 +73,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Http File Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Http File Data: " + json); context.Response.StatusCode = 500; return msg; @@ -133,8 +127,8 @@ public string UpdateConfig(IHttpContext context) } catch (Exception ex) { - var msg = "Error processing settings: " + ex.Message; - cumulus.LogMessage(msg); + var msg = "HTTP file: Error processing settings: " + ex.Message; + cumulus.LogErrorMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -143,8 +137,28 @@ public string UpdateConfig(IHttpContext context) public async Task DownloadHttpFile(string url, string filename) { + string modUrl; + + if (url == "") + { + if (string.IsNullOrEmpty(station.EcowittCameraUrl)) + { + cumulus.LogWarningMessage("DownloadHttpFile: The Ecowitt Camera URL is not available"); + return; + } + else + { + url = station.EcowittCameraUrl; + // do not append timestamp, it is already unique + modUrl = url; + } + } + else + { + modUrl = url + (url.Contains("?") ? "&" : "?") + "_=" + DateTime.Now.ToUnixTime(); + } + cumulus.LogDebugMessage($"DownloadHttpFile: Downloading from {url} to {filename}"); - var modUrl = url + (url.Contains("?") ? "&" : "?") + "_=" + DateTime.Now.ToUnixTime(); try { diff --git a/CumulusMX/HttpStationAmbient.cs b/CumulusMX/HttpStationAmbient.cs index 96f85fdb..c97f8847 100644 --- a/CumulusMX/HttpStationAmbient.cs +++ b/CumulusMX/HttpStationAmbient.cs @@ -1,7 +1,8 @@ using System; -using EmbedIO; -using System.Globalization; using System.Collections.Specialized; +using System.Globalization; + +using EmbedIO; namespace CumulusMX @@ -139,7 +140,7 @@ public string ProcessData(IHttpContext context, bool main) if (gust == null || dir == null || avg == null) { - cumulus.LogMessage($"ProcessData: Error, missing wind data"); + cumulus.LogWarningMessage($"ProcessData: Error, missing wind data"); } else { @@ -151,7 +152,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Wind data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Wind data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in wind data - " + ex.Message; } @@ -169,7 +170,7 @@ public string ProcessData(IHttpContext context, bool main) if (humIn == null) { - cumulus.LogMessage($"ProcessData: Error, missing indoor humidity"); + cumulus.LogWarningMessage($"ProcessData: Error, missing indoor humidity"); } else { @@ -179,7 +180,7 @@ public string ProcessData(IHttpContext context, bool main) if (humOut == null) { - cumulus.LogMessage($"ProcessData: Error, missing outdoor humidity"); + cumulus.LogWarningMessage($"ProcessData: Error, missing outdoor humidity"); } else { @@ -189,7 +190,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Humidity data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Humidity data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in humidity data - " + ex.Message; } @@ -206,7 +207,7 @@ public string ProcessData(IHttpContext context, bool main) if (press == null) { - cumulus.LogMessage("ProcessData: Error, missing baro pressure"); + cumulus.LogWarningMessage("ProcessData: Error, missing baro pressure"); } else { @@ -226,7 +227,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Pressure data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Pressure data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in baro pressure data - " + ex.Message; } @@ -241,7 +242,7 @@ public string ProcessData(IHttpContext context, bool main) if (temp == null) { - cumulus.LogMessage($"ProcessData: Error, missing indoor temp"); + cumulus.LogWarningMessage($"ProcessData: Error, missing indoor temp"); } else { @@ -251,7 +252,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Indoor temp data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Indoor temp data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in indoor temp data - " + ex.Message; } @@ -266,7 +267,7 @@ public string ProcessData(IHttpContext context, bool main) if (temp == null) { - cumulus.LogMessage($"ProcessData: Error, missing outdoor temp"); + cumulus.LogWarningMessage($"ProcessData: Error, missing outdoor temp"); } else { @@ -276,7 +277,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Outdoor temp data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Outdoor temp data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in outdoor temp data - " + ex.Message; } @@ -299,7 +300,7 @@ public string ProcessData(IHttpContext context, bool main) if (rain == null) { - cumulus.LogMessage($"ProcessData: Error, missing rainfall"); + cumulus.LogWarningMessage($"ProcessData: Error, missing rainfall"); } else { @@ -310,7 +311,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Rain data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Rain data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in rainfall data - " + ex.Message; } @@ -328,7 +329,7 @@ public string ProcessData(IHttpContext context, bool main) } else if (dewpnt == null) { - cumulus.LogMessage($"ProcessData: Error, missing dew point"); + cumulus.LogWarningMessage($"ProcessData: Error, missing dew point"); } else { @@ -339,7 +340,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Dew point data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Dew point data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in dew point data - " + ex.Message; } @@ -359,7 +360,7 @@ public string ProcessData(IHttpContext context, bool main) var chill = data["windchillf"]; if (chill == null) { - cumulus.LogMessage($"ProcessData: Error, missing dew point"); + cumulus.LogWarningMessage($"ProcessData: Error, missing dew point"); } else { @@ -370,7 +371,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Dew point data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Dew point data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in dew point data - " + ex.Message; } @@ -390,12 +391,12 @@ public string ProcessData(IHttpContext context, bool main) } else { - cumulus.LogMessage("ProcessData: Insufficient data to calculate Apparent/Feels Like temps"); + cumulus.LogWarningMessage("ProcessData: Insufficient data to calculate Apparent/Feels Like temps"); } } else { - cumulus.LogMessage("ProcessData: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); + cumulus.LogWarningMessage("ProcessData: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); } } @@ -410,7 +411,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in extra temperature data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in extra temperature data - " + ex.Message); } } @@ -425,7 +426,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in extra humidity data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in extra humidity data - " + ex.Message); } } @@ -440,7 +441,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in solar data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in solar data - " + ex.Message); } } @@ -455,7 +456,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in UV data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in UV data - " + ex.Message); } } @@ -470,7 +471,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Soil temp data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Soil temp data - " + ex.Message); } } @@ -485,7 +486,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Soil moisture data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Soil moisture data - " + ex.Message); } } @@ -508,7 +509,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Air Quality data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Air Quality data - " + ex.Message); } } @@ -527,7 +528,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in CO₂ data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in CO₂ data - " + ex.Message); } } @@ -545,7 +546,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Lightning data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Lightning data - " + ex.Message); } } @@ -560,7 +561,7 @@ public string ProcessData(IHttpContext context, bool main) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Leak data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Leak data - " + ex.Message); } } @@ -586,7 +587,7 @@ battrain Rain Gauge [0 or 1] } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Battery data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Battery data - " + ex.Message); } @@ -599,7 +600,7 @@ battrain Rain Gauge [0 or 1] } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error calculating extra sensor dew points - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error calculating extra sensor dew points - " + ex.Message); } } @@ -611,7 +612,7 @@ battrain Rain Gauge [0 or 1] } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error - " + ex.Message); context.Response.StatusCode = 500; return "Failed: General error - " + ex.Message; } @@ -649,7 +650,7 @@ private void ProcessSolar(NameValueCollection data, WeatherStation station, Date { if (data["solarradiation"] != null) { - station.DoSolarRad((int)Convert.ToDouble(data["solarradiation"], CultureInfo.InvariantCulture), recDate); + station.DoSolarRad((int) Convert.ToDouble(data["solarradiation"], CultureInfo.InvariantCulture), recDate); } } diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs index bd0f0650..c459c7e1 100644 --- a/CumulusMX/HttpStationEcowitt.cs +++ b/CumulusMX/HttpStationEcowitt.cs @@ -1,20 +1,14 @@ using System; -using EmbedIO; -using System.IO; -using System.Web; -using System.Globalization; using System.Collections.Specialized; -using System.Reflection; -using ServiceStack.Text; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Net.Http; using System.Text; -using System.Threading; using System.Threading.Tasks; -using System.Net.Http; -using static CumulusMX.EmailSender; -using static ServiceStack.Diagnostics.Events; +using System.Web; + +using EmbedIO; + namespace CumulusMX { @@ -167,7 +161,7 @@ public override void getAndProcessHistoryData() if (string.IsNullOrEmpty(cumulus.EcowittApplicationKey) || string.IsNullOrEmpty(cumulus.EcowittUserApiKey) || string.IsNullOrEmpty(cumulus.EcowittMacAddress)) { - cumulus.LogMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); + cumulus.LogWarningMessage("API.GetHistoricData: Missing Ecowitt API data in the configuration, aborting!"); cumulus.LastUpdateTime = DateTime.Now; } else @@ -187,7 +181,7 @@ public override void getAndProcessHistoryData() } catch (Exception ex) { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); } } @@ -229,6 +223,7 @@ POST Parameters - all fields are URL escaped PASSKEY=&stationtype=GW1100A_V2.1.4&runtime=2336207&dateutc=2022-05-13+08:55:11&tempinf=73.0&humidityin=38&baromrelin=30.156&baromabsin=29.297&tempf=63.0&humidity=49&winddir=71&windspeedmph=2.68&windgustmph=11.86&maxdailygust=15.21&solarradiation=694.87&uv=5&rainratein=0.000&eventrainin=0.000&hourlyrainin=0.000&dailyrainin=0.000&weeklyrainin=0.000&monthlyrainin=0.591&yearlyrainin=14.591&temp1f=67.8&humidity1=43&temp2f=68.7&humidity2=47&temp3f=62.6&humidity3=51&temp4f=62.6&humidity4=51&temp5f=-1.5&temp6f=76.1&humidity6=47&temp7f=44.4&humidity7=49&soilmoisture1=14&soilmoisture2=50&soilmoisture3=20&soilmoisture4=25&pm25_ch1=7.0&pm25_avg_24h_ch1=7.6&pm25_ch2=6.0&pm25_avg_24h_ch2=8.3&tf_co2=72.9&humi_co2=44&pm25_co2=1.2&pm25_24h_co2=2.6&pm10_co2=1.5&pm10_24h_co2=3.0&co2=387&co2_24h=536&lightning_num=0&lightning=31&lightning_time=1652304268&leak_ch2=0&wh65batt=0&wh80batt=2.74&wh26batt=0&batt1=0&batt2=0&batt3=0&batt4=0&batt5=0&batt6=0&batt7=0&soilbatt1=1.3&soilbatt2=1.4&soilbatt3=1.4&soilbatt4=1.4&pm25batt1=4&pm25batt2=4&wh57batt=5&leakbatt2=4&co2_batt=6&freq=868M&model=GW1100A + PASSKEY=&stationtype=EasyWeatherPro_V5.1.1&runtime=4&dateutc=2023-09-03+07:29:16&tempinf=70.0&humidityin=89&baromrelin=29.811&baromabsin=29.811&tempf=52.7&humidity=92&winddir=223&windspeedmph=1.79&windgustmph=4.92&maxdailygust=15.88&solarradiation=39.31&uv=0&pm25_ch1=4.0&pm25_avg_24h_ch1=3.1&rrain_piezo=0.254&erain_piezo=0.126&hrain_piezo=0.016&drain_piezo=0.126&wrain_piezo=0.189&mrain_piezo=0.126&yrain_piezo=34.882&ws90cap_volt=3.7&ws90_ver=126&gain10_piezo=1.00&gain20_piezo=1.00&gain30_piezo=1.00&gain40_piezo=1.00&gain50_piezo=1.00&pm25batt1=3&wh90batt=2.94&freq=868M&model=HP2564AE_Pro_V1.8.1&interval=16 */ var procName = main ? "ProcessData" : "ProcessExtraData"; @@ -255,7 +250,7 @@ POST Parameters - all fields are URL escaped var retVal = ApplyData(text, main, ts); - ForwardData(text); + ForwardData(text, main); if (retVal != "") { @@ -265,7 +260,7 @@ POST Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error - {ex.Message}"); context.Response.StatusCode = 500; return "Failed: General error - " + ex.Message; } @@ -357,7 +352,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Wind data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Wind data - {ex.Message}"); return "Failed: Error in wind data - " + ex.Message; } @@ -372,7 +367,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) if (humIn == null) { - cumulus.LogMessage($"{procName}: Error, missing indoor humidity"); + cumulus.LogWarningMessage($"{procName}: Error, missing indoor humidity"); } else { @@ -395,7 +390,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Humidity data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Humidity data - {ex.Message}"); return "Failed: Error in humidity data - " + ex.Message; } @@ -431,7 +426,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Pressure data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Pressure data - {ex.Message}"); return "Failed: Error in baro pressure data - " + ex.Message; } @@ -455,7 +450,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Indoor temp data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Indoor temp data - {ex.Message}"); return "Failed: Error in indoor temp data - " + ex.Message; } @@ -479,7 +474,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Outdoor temp data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Outdoor temp data - {ex.Message}"); return "Failed: Error in outdoor temp data - " + ex.Message; } @@ -522,14 +517,14 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) else { rain = data["yrain_piezo"]; - rRate = data["​rrain_piezo"]; + rRate = data["rrain_piezo"]; } if (rRate == null) { // No rain rate, so we will calculate it calculaterainrate = true; - rRate = "0"; + rRate = "-999"; } else { @@ -550,7 +545,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Rain data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Rain data - {ex.Message}"); return "Failed: Error in rainfall data - " + ex.Message; } } @@ -565,7 +560,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in extra temperature data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in extra temperature data - {ex.Message}"); } } @@ -580,7 +575,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in extra humidity data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in extra humidity data - {ex.Message}"); } } @@ -595,7 +590,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in solar data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in solar data - {ex.Message}"); } } @@ -610,7 +605,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in UV data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in UV data - {ex.Message}"); } } @@ -626,7 +621,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Soil temp data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Soil temp data - {ex.Message}"); } } @@ -641,10 +636,23 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Soil moisture data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Soil moisture data - {ex.Message}"); } } + // === Soil Moisture Raw === + if (main || cumulus.EcowittExtraUseSoilMoist) + { + try + { + // soilad[1-16] + ProcessSoilMoistRaw(data, thisStation); + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"{procName}: Error in Soil moisture Raw data - {ex.Message}"); + } + } // === Leaf Wetness === if (main || cumulus.EcowittExtraUseLeafWet) @@ -657,7 +665,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Leaf wetness data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Leaf wetness data - {ex.Message}"); } } @@ -672,7 +680,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in User Temp data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in User Temp data - {ex.Message}"); } } @@ -688,7 +696,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Air Quality data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Air Quality data - {ex.Message}"); } } @@ -710,7 +718,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in CO₂ data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in CO₂ data - {ex.Message}"); } } @@ -727,7 +735,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Lightning data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Lightning data - {ex.Message}"); } } @@ -742,7 +750,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Leak data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Leak data - {ex.Message}"); } } @@ -771,7 +779,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Battery data - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error in Battery data - {ex.Message}"); } @@ -784,7 +792,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error calculating extra sensor dew points - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error calculating extra sensor dew points - {ex.Message}"); } } @@ -805,7 +813,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error extracting firmware version - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error extracting firmware version - {ex.Message}"); } if (main) @@ -825,7 +833,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } else if (dewpnt == null) { - cumulus.LogMessage($"{procName}: Error, missing dew point"); + cumulus.LogWarningMessage($"{procName}: Error, missing dew point"); } else { @@ -835,7 +843,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in Dew point data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in Dew point data - " + ex.Message); return "Failed: Error in dew point data - " + ex.Message; } @@ -852,7 +860,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } else { - cumulus.LogMessage($"{procName}: Insufficient data to calculate wind chill"); + cumulus.LogWarningMessage($"{procName}: Insufficient data to calculate wind chill"); } } else @@ -860,7 +868,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) var chill = data["windchillf"]; if (chill == null) { - cumulus.LogMessage($"{procName}: Error, missing wind chill"); + cumulus.LogWarningMessage($"{procName}: Error, missing wind chill"); } else { @@ -871,7 +879,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error in wind chill data - " + ex.Message); + cumulus.LogErrorMessage($"{procName}: Error in wind chill data - " + ex.Message); return "Failed: Error in wind chill data - " + ex.Message; } @@ -890,12 +898,12 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } else { - cumulus.LogMessage($"{procName}: Insufficient data to calculate Apparent/Feels Like temps"); + cumulus.LogWarningMessage($"{procName}: Insufficient data to calculate Apparent/Feels Like temps"); } } else { - cumulus.LogMessage($"{procName}: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); + cumulus.LogWarningMessage($"{procName}: Insufficient data to calculate Humidex and Apparent/Feels Like temps"); } } @@ -906,7 +914,7 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null) } catch (Exception ex) { - cumulus.LogMessage($"{procName}: Error - {ex.Message}"); + cumulus.LogErrorMessage($"{procName}: Error - {ex.Message}"); return "Failed: General error - " + ex.Message; } @@ -949,7 +957,7 @@ private void ProcessSolar(NameValueCollection data, WeatherStation station, Date { if (data["solarradiation"] != null) { - station.DoSolarRad((int)Convert.ToDouble(data["solarradiation"], invNum), recDate); + station.DoSolarRad((int) Convert.ToDouble(data["solarradiation"], invNum), recDate); } } @@ -988,6 +996,18 @@ private void ProcessSoilMoist(NameValueCollection data, WeatherStation station) } } + private void ProcessSoilMoistRaw(NameValueCollection data, WeatherStation station) + { + for (var i = 1; i <= 16; i++) + { + if (data["soilad" + i] != null) + { + // Do nothing for now + //station.DoSoilMoistureRaw(Convert.ToDouble(data["soilad" + i], invNum), i); + } + } + } + private void ProcessLeafWetness(NameValueCollection data, WeatherStation station) { if (data["leafwetness"] != null) @@ -1148,18 +1168,18 @@ private void ProcessBatteries(NameValueCollection data) lowBatt = lowBatt || (data["wh90batt"] != null && Convert.ToDouble(data["wh90batt"], invNum) <= 2.4); for (var i = 1; i < 5; i++) { - lowBatt = lowBatt || (data["batt" + i] != null && data["batt" + i] == "1"); + lowBatt = lowBatt || (data["batt" + i] != null && data["batt" + i] == "1"); lowBatt = lowBatt || (data["soilbatt" + i] != null && Convert.ToDouble(data["soilbatt" + i], invNum) <= 1.2); lowBatt = lowBatt || (data["pm25batt" + i] != null && data["pm25batt" + i] == "1"); lowBatt = lowBatt || (data["leakbatt" + i] != null && data["leakbatt" + i] == "1"); - lowBatt = lowBatt || (data["tf_batt" + i] != null && Convert.ToDouble(data["tf_batt" + i], invNum) <= 1.2); + lowBatt = lowBatt || (data["tf_batt" + i] != null && Convert.ToDouble(data["tf_batt" + i], invNum) <= 1.2); lowBatt = lowBatt || (data["leaf_batt" + i] != null && Convert.ToDouble(data["leaf_batt" + i], invNum) <= 1.2); } for (var i = 5; i < 9; i++) { - lowBatt = lowBatt || (data["batt" + i] != null && data["batt" + i] == "1"); + lowBatt = lowBatt || (data["batt" + i] != null && data["batt" + i] == "1"); lowBatt = lowBatt || (data["soilbatt" + i] != null && Convert.ToDouble(data["soilbatt" + i], invNum) <= 1.2); - lowBatt = lowBatt || (data["tf_batt" + i] != null && Convert.ToDouble(data["tf_batt" + i], invNum) <= 1.2); + lowBatt = lowBatt || (data["tf_batt" + i] != null && Convert.ToDouble(data["tf_batt" + i], invNum) <= 1.2); lowBatt = lowBatt || (data["leaf_batt" + i] != null && Convert.ToDouble(data["leaf_batt" + i], invNum) <= 1.2); } @@ -1262,25 +1282,25 @@ private void SetCustomServer(GW1000Api api, bool main) length += 2 + 2 + 1 + 1; // + port + interval + type + active var send = new byte[length]; // set ID - send[0] = (byte)id.Length; + send[0] = (byte) id.Length; Encoding.ASCII.GetBytes(id).CopyTo(send, 1); // set password idx = 1 + id.Length; - send[idx] = (byte)pass.Length; + send[idx] = (byte) pass.Length; Encoding.ASCII.GetBytes(id).CopyTo(send, idx + 1); // set server string length idx += 1 + pass.Length; - send[idx] = (byte)customServer.Length; + send[idx] = (byte) customServer.Length; // set server string Encoding.ASCII.GetBytes(customServer).CopyTo(send, idx + 1); - idx += 1 + server.Length; + idx += 1 + customServer.Length; // set the port id - GW1000Api.ConvertUInt16ToLittleEndianByteArray((ushort)customPort).CopyTo(send, idx); + GW1000Api.ConvertUInt16ToLittleEndianByteArray((ushort) customPort).CopyTo(send, idx); // set the interval idx += 2; - GW1000Api.ConvertUInt16ToLittleEndianByteArray((ushort)customIntv).CopyTo(send, idx); + GW1000Api.ConvertUInt16ToLittleEndianByteArray((ushort) customIntv).CopyTo(send, idx); // set type idx += 2; send[idx] = 0; @@ -1293,7 +1313,7 @@ private void SetCustomServer(GW1000Api api, bool main) if (retData == null || retData[4] != 0) { - cumulus.LogMessage("Error - failed to set the Ecowitt Gateway config"); + cumulus.LogErrorMessage("Error - failed to set the Ecowitt Gateway config"); } else { @@ -1307,17 +1327,17 @@ private void SetCustomServer(GW1000Api api, bool main) { ecPath = customPath; var path = new byte[ecPath.Length + wuPath.Length + 2]; - path[0] = (byte)ecPath.Length; + path[0] = (byte) ecPath.Length; Encoding.ASCII.GetBytes(ecPath).CopyTo(path, 1); idx = 1 + ecPath.Length; - path[idx] = (byte)wuPath.Length; + path[idx] = (byte) wuPath.Length; Encoding.ASCII.GetBytes(wuPath).CopyTo(path, idx + 1); var retData = api.DoCommand(GW1000Api.Commands.CMD_WRITE_USER_PATH, path); if (retData == null || retData[4] != 0) { - cumulus.LogMessage("Error - failed to set the Ecowitt Gateway Custom Server Path"); + cumulus.LogErrorMessage("Error - failed to set the Ecowitt Gateway Custom Server Path"); } else { @@ -1327,24 +1347,26 @@ private void SetCustomServer(GW1000Api api, bool main) } catch (Exception ex) { - cumulus.LogMessage("Error setting Ecowitt Gateway Custom Server config: " + ex.Message); + cumulus.LogErrorMessage("Error setting Ecowitt Gateway Custom Server config: " + ex.Message); } } else { - cumulus.LogMessage("Error reading Ecowitt Gateway Custom Server config, cannot configure it"); + cumulus.LogErrorMessage("Error reading Ecowitt Gateway Custom Server config, cannot configure it"); } } - private void ForwardData(string data) + private void ForwardData(string data, bool main) { var encoding = new UTF8Encoding(false); - for (int i = 0; i < cumulus.EcowittForwarders.Length; i++) + var forwarders = main || cumulus.EcowittExtraUseMainForwarders ? cumulus.EcowittForwarders : cumulus.EcowittExtraForwarders; + + for (int i = 0; i < forwarders.Length; i++) { - if (!string.IsNullOrEmpty(cumulus.EcowittForwarders[i])) + if (!string.IsNullOrEmpty(forwarders[i])) { - var url = cumulus.EcowittForwarders[i]; + var url = forwarders[i]; var idx = i; cumulus.LogDebugMessage("ForwardData: Forwarding Ecowitt data to: " + url); diff --git a/CumulusMX/HttpStationWund.cs b/CumulusMX/HttpStationWund.cs index 7d2daac1..2bacc06b 100644 --- a/CumulusMX/HttpStationWund.cs +++ b/CumulusMX/HttpStationWund.cs @@ -1,7 +1,8 @@ using System; -using EmbedIO; using System.Globalization; +using EmbedIO; + namespace CumulusMX { class HttpStationWund : WeatherStation @@ -98,7 +99,7 @@ GET Parameters - all fields are URL escaped if (gust == null || dir == null || avg == null || gust == "-9999" || dir == "-9999" || avg == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing wind data"); + cumulus.LogWarningMessage($"ProcessData: Error, missing wind data"); } else { @@ -110,7 +111,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Wind data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Wind data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in wind data - " + ex.Message; } @@ -127,7 +128,7 @@ GET Parameters - all fields are URL escaped if (humIn == null || humIn == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing indoor humidity"); + cumulus.LogWarningMessage($"ProcessData: Error, missing indoor humidity"); } else { @@ -137,7 +138,7 @@ GET Parameters - all fields are URL escaped if (humOut == null || humOut == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing outdoor humidity"); + cumulus.LogWarningMessage($"ProcessData: Error, missing outdoor humidity"); } else { @@ -147,7 +148,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Humidity data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Humidity data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in humidity data - " + ex.Message; } @@ -161,7 +162,7 @@ GET Parameters - all fields are URL escaped var press = data["baromin"]; if (press == null || press == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing baro pressure"); + cumulus.LogWarningMessage($"ProcessData: Error, missing baro pressure"); } else { @@ -172,7 +173,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Pressure data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Pressure data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in baro pressure data - " + ex.Message; } @@ -186,7 +187,7 @@ GET Parameters - all fields are URL escaped var temp = data["indoortempf"]; if (temp == null || temp == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing indoor temp"); + cumulus.LogWarningMessage($"ProcessData: Error, missing indoor temp"); } else { @@ -196,7 +197,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Indoor temp data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Indoor temp data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in indoor temp data - " + ex.Message; } @@ -210,7 +211,7 @@ GET Parameters - all fields are URL escaped var temp = data["tempf"]; if (temp == null || temp == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing outdoor temp"); + cumulus.LogWarningMessage($"ProcessData: Error, missing outdoor temp"); } else { @@ -220,7 +221,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Outdoor temp data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Outdoor temp data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in outdoor temp data - " + ex.Message; } @@ -236,7 +237,7 @@ GET Parameters - all fields are URL escaped if (rain == null || rain == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing rainfall"); + cumulus.LogWarningMessage($"ProcessData: Error, missing rainfall"); } else { @@ -255,7 +256,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Rain data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Rain data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in rainfall data - " + ex.Message; } @@ -274,7 +275,7 @@ GET Parameters - all fields are URL escaped } else if (dewpnt == null || dewpnt == "-9999") { - cumulus.LogMessage($"ProcessData: Error, missing dew point"); + cumulus.LogWarningMessage($"ProcessData: Error, missing dew point"); } else { @@ -284,7 +285,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Dew point data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Dew point data - " + ex.Message); context.Response.StatusCode = 500; return "Failed: Error in dew point data - " + ex.Message; } @@ -296,7 +297,7 @@ GET Parameters - all fields are URL escaped { DoWindChill(0, recDate); - // === Apparent/Feels Like === + // === Apparent/Feels Like === if (data["humidity"] != null && data["humidity"] != "-9999") { DoApparentTemp(recDate); @@ -304,12 +305,12 @@ GET Parameters - all fields are URL escaped } else { - cumulus.LogMessage("ProcessData: Insufficient data to calculate Apparent/Feels like Temps"); + cumulus.LogWarningMessage("ProcessData: Insufficient data to calculate Apparent/Feels like Temps"); } } else { - cumulus.LogMessage("ProcessData: Insufficient data to calculate Wind Chill and Apparent/Feels like Temps"); + cumulus.LogWarningMessage("ProcessData: Insufficient data to calculate Wind Chill and Apparent/Feels like Temps"); } @@ -322,7 +323,7 @@ GET Parameters - all fields are URL escaped } else { - cumulus.LogMessage("ProcessData: Insufficient data to calculate Humidex"); + cumulus.LogWarningMessage("ProcessData: Insufficient data to calculate Humidex"); } DoForecast(string.Empty, false); @@ -344,7 +345,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in extra temperature data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in extra temperature data - " + ex.Message); } @@ -356,12 +357,12 @@ GET Parameters - all fields are URL escaped var str = data["solarradiation"]; if (str != null && str != "-9999") { - DoSolarRad((int)Convert.ToDouble(str, CultureInfo.InvariantCulture), recDate); + DoSolarRad((int) Convert.ToDouble(str, CultureInfo.InvariantCulture), recDate); } } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in solar data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in solar data - " + ex.Message); } @@ -378,7 +379,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in UV data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in UV data - " + ex.Message); } @@ -405,7 +406,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Soil temp data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Soil temp data - " + ex.Message); } @@ -432,7 +433,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Soil moisture data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Soil moisture data - " + ex.Message); } @@ -455,7 +456,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Leaf wetness data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Leaf wetness data - " + ex.Message); } @@ -478,7 +479,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error in Air Quality data - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error in Air Quality data - " + ex.Message); } UpdateStatusPanel(recDate); @@ -486,7 +487,7 @@ GET Parameters - all fields are URL escaped } catch (Exception ex) { - cumulus.LogMessage("ProcessData: Error - " + ex.Message); + cumulus.LogErrorMessage("ProcessData: Error - " + ex.Message); context.Response.StatusCode = 500; return "Failed: General error - " + ex.Message; } diff --git a/CumulusMX/ImetStation.cs b/CumulusMX/ImetStation.cs index 56b6990a..5876f509 100644 --- a/CumulusMX/ImetStation.cs +++ b/CumulusMX/ImetStation.cs @@ -38,7 +38,7 @@ public ImetStation(Cumulus cumulus) : base(cumulus) cumulus.RainDPlaceDefaults[1] = 3; // in cumulus.RainFormat = cumulus.SunFormat = "F2"; - comport = new SerialPort(cumulus.ComportName, cumulus.ImetOptions.BaudRate, Parity.None, 8, StopBits.One) {Handshake = Handshake.None, RtsEnable = true, DtrEnable = true}; + comport = new SerialPort(cumulus.ComportName, cumulus.ImetOptions.BaudRate, Parity.None, 8, StopBits.One) { Handshake = Handshake.None, RtsEnable = true, DtrEnable = true }; try { @@ -48,7 +48,7 @@ public ImetStation(Cumulus cumulus) : base(cumulus) } catch (Exception ex) { - cumulus.LogMessage(ex.Message); + cumulus.LogErrorMessage("Error opening COM port: " + ex.Message); //MessageBox.Show(ex.Message); } @@ -214,7 +214,7 @@ private void UpdateReadPointer() } else { - cumulus.LogMessage("WRST: Invalid checksum"); + cumulus.LogErrorMessage("WRST: Invalid checksum"); } } catch @@ -223,7 +223,7 @@ private void UpdateReadPointer() } else { - cumulus.LogMessage("RDST: Invalid checksum"); + cumulus.LogErrorMessage("RDST: Invalid checksum"); } } @@ -270,7 +270,7 @@ private string GetResponse(string expected) { do { - attempts ++; + attempts++; cumulus.LogDataMessage("Reading response from station, attempt " + attempts); response = comport.ReadTo(sLineBreak); byte[] ba = Encoding.Default.GetBytes(response); @@ -363,7 +363,7 @@ private int GetNumberOfLogs() } else { - cumulus.LogMessage("Unable to read log count"); + cumulus.LogErrorMessage("Unable to read log count"); } return num; @@ -387,7 +387,7 @@ private static bool ValidChecksum(string str) for (int i = 0; i <= endpos; i++) { - sum = (sum + str[i])%256; + sum = (sum + str[i]) % 256; } // 8-bit 1's complement @@ -409,7 +409,7 @@ private static string ExtractText(string input, string after) // readto() should have stripped this off int pos1 = input.IndexOf(after); //int pos2 = input.Length - 2; - return pos1>=0 ? input.Substring(pos1) : ""; + return pos1 >= 0 ? input.Substring(pos1) : ""; } public override void startReadingHistoryData() @@ -494,7 +494,7 @@ public override void getAndProcessHistoryData() DateTime timestamp = DateTime.MinValue; - NumberFormatInfo provider = new NumberFormatInfo {NumberDecimalSeparator = "."}; + NumberFormatInfo provider = new NumberFormatInfo { NumberDecimalSeparator = "." }; DateTime startfrom = cumulus.LastUpdateTime; int startindex = 0; @@ -552,7 +552,7 @@ public override void getAndProcessHistoryData() } catch { - cumulus.LogMessage("Error in earliest timestamp, unable to process logger data"); + cumulus.LogErrorMessage("Error in earliest timestamp, unable to process logger data"); dataOK = false; } @@ -585,11 +585,11 @@ public override void getAndProcessHistoryData() } catch (Exception E) { - cumulus.LogMessage("Error in timestamp, skipping entry. Error = " + E.Message); + cumulus.LogErrorMessage("Error in timestamp, skipping entry. Error = " + E.Message); timestamp = DateTime.MinValue; } - startindex++; + startindex++; } } } @@ -645,7 +645,7 @@ public override void getAndProcessHistoryData() timestamp = new DateTime(year, month, day, hour, minute, sec); cumulus.LogMessage("Processing logger data entry " + i + " for " + timestamp); - int interval = (int) (Convert.ToDouble(sl[INTERVALPOS], provider)/60); + int interval = (int) (Convert.ToDouble(sl[INTERVALPOS], provider) / 60); // Check for roll-over if (hour != rollHour) @@ -672,12 +672,12 @@ public override void getAndProcessHistoryData() DoWind(windgust, windbearing, windspeed, timestamp); // add in "archivePeriod" minutes worth of wind speed to windrun - WindRunToday += ((WindAverage*WindRunHourMult[cumulus.Units.Wind]*interval)/60.0); + WindRunToday += ((WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0); DateTime windruncheckTS; if ((hour == rollHour) && (minute == 0)) - // this is the last logger entry before roll-over - // fudge the timestamp to make sure it falls in the previous day + // this is the last logger entry before roll-over + // fudge the timestamp to make sure it falls in the previous day { windruncheckTS = timestamp.AddMinutes(-1); } @@ -698,7 +698,7 @@ public override void getAndProcessHistoryData() // add in "archivePeriod" minutes worth of temperature to the temp samples tempsamplestoday += interval; - TempTotalToday += (OutdoorTemperature*interval); + TempTotalToday += (OutdoorTemperature * interval); // update chill hours if (OutdoorTemperature < cumulus.ChillHourThreshold) @@ -815,7 +815,7 @@ public override void getAndProcessHistoryData() } catch (Exception E) { - cumulus.LogMessage("Error in data: " + E.Message); + cumulus.LogErrorMessage("Error in data: " + E.Message); } } } @@ -899,12 +899,12 @@ private void ImetGetData() if (sl.Count != 10 && sl[0] != "rdlv") { - cumulus.LogMessage($"RDLV: Unexpected response: {response}"); + cumulus.LogWarningMessage($"RDLV: Unexpected response: {response}"); return; } // Parse data using decimal points rather than user's decimal separator - NumberFormatInfo provider = new NumberFormatInfo {NumberDecimalSeparator = "."}; + NumberFormatInfo provider = new NumberFormatInfo { NumberDecimalSeparator = "." }; double windspeed = -999; double temp1 = -999; @@ -914,14 +914,14 @@ private void ImetGetData() int varInt; if (!string.IsNullOrEmpty(sl[DIRPOS]) && int.TryParse(sl[DIRPOS], out varInt) && - !string.IsNullOrEmpty(sl[WINDPOS]) && double.TryParse(sl[WINDPOS], NumberStyles.Float, provider, out varDbl)) + !string.IsNullOrEmpty(sl[WINDPOS]) && double.TryParse(sl[WINDPOS], NumberStyles.Float, provider, out varDbl)) { windspeed = varDbl; DoWind(ConvertWindMSToUser(windspeed), varInt, -1, now); } else { - cumulus.LogMessage($"RDLV: Unexpected wind dir/speed format, found: {sl[DIRPOS]}/{sl[WINDPOS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected wind dir/speed format, found: {sl[DIRPOS]}/{sl[WINDPOS]}"); } @@ -937,7 +937,7 @@ private void ImetGetData() } else { - cumulus.LogMessage($"RDLV: Unexpected temperature 1 format, found: {sl[TEMP1POS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected temperature 1 format, found: {sl[TEMP1POS]}"); } if (!string.IsNullOrEmpty(sl[TEMP2POS])) // TEMP2 is optional @@ -957,7 +957,7 @@ private void ImetGetData() } else { - cumulus.LogMessage($"RDLV: Unexpected temperature 2 format, found: {sl[TEMP2POS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected temperature 2 format, found: {sl[TEMP2POS]}"); } } @@ -968,7 +968,7 @@ private void ImetGetData() } else { - cumulus.LogMessage($"RDLV: Unexpected humidity format, found: {sl[RELHUMPOS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected humidity format, found: {sl[RELHUMPOS]}"); } if (!string.IsNullOrEmpty(sl[PRESSPOS]) && double.TryParse(sl[PRESSPOS], NumberStyles.Float, provider, out varDbl)) @@ -978,7 +978,7 @@ private void ImetGetData() } else { - cumulus.LogMessage($"RDLV: Unexpected pressure format, found: {sl[PRESSPOS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected pressure format, found: {sl[PRESSPOS]}"); } @@ -988,7 +988,7 @@ private void ImetGetData() } else { - cumulus.LogMessage($"RDLV: Unexpected rain format, found: {sl[RAINPOS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected rain format, found: {sl[RAINPOS]}"); } if (!string.IsNullOrEmpty(sl[SUNPOS]) && double.TryParse(sl[SUNPOS], NumberStyles.Float, provider, out varDbl)) @@ -997,7 +997,7 @@ private void ImetGetData() } else { - cumulus.LogMessage($"RDLV: Unexpected rain format, found: {sl[RAINPOS]}"); + cumulus.LogWarningMessage($"RDLV: Unexpected rain format, found: {sl[RAINPOS]}"); } if (temp1 > -999 && humidity > -999) @@ -1020,7 +1020,7 @@ private void ImetGetData() } else { - cumulus.LogMessage("RDLV: Invalid checksum:"); + cumulus.LogErrorMessage("RDLV: Invalid checksum:"); cumulus.LogMessage(response); } @@ -1028,17 +1028,17 @@ private void ImetGetData() return; // Keep the log pointer current, to avoid large numbers of logs - // being downloaded at next start-up - // Only do this every 30 read intervals - if (readCounter > 0) - { - readCounter--; - } - else - { - UpdateReadPointer(); - readCounter = 30; - } + // being downloaded at next start-up + // Only do this every 30 read intervals + if (readCounter > 0) + { + readCounter--; + } + else + { + UpdateReadPointer(); + readCounter = 30; + } } } } diff --git a/CumulusMX/IniFile.cs b/CumulusMX/IniFile.cs index 1fca7375..b80915d2 100644 --- a/CumulusMX/IniFile.cs +++ b/CumulusMX/IniFile.cs @@ -8,7 +8,6 @@ using System.Globalization; using System.IO; using System.Text; -using FluentFTP.Helpers; namespace CumulusMX { @@ -16,7 +15,7 @@ namespace CumulusMX internal class IniFile { -#region "Declarations" + #region "Declarations" // *** Lock for thread-safe access to file and local cache *** private readonly object m_Lock = new object(); @@ -35,14 +34,14 @@ internal string FileName private bool m_Lazy = false; // *** Local cache *** - private readonly Dictionary> m_Sections = new Dictionary>(); + private readonly Dictionary> m_Sections = new Dictionary>(); // *** Local cache modified flag *** private bool m_CacheModified = false; -#endregion + #endregion -#region "Methods" + #region "Methods" // *** Constructor *** public IniFile(string FileName) @@ -56,7 +55,7 @@ public IniFile(string FileName, bool Lazy) } // *** Initialization *** - private void Initialize (string FileName, bool Lazy) + private void Initialize(string FileName, bool Lazy) { m_FileName = FileName; m_Lazy = Lazy; @@ -273,7 +272,7 @@ internal void SetValue(string SectionName, string Key, string Value) { // *** If it doesn't, add it *** Section = new Dictionary(); - m_Sections.Add(SectionName,Section); + m_Sections.Add(SectionName, Section); } // *** Modify the value *** @@ -340,11 +339,11 @@ private string EncodeByteArray(byte[] Value) StringBuilder sb = new StringBuilder(); foreach (byte b in Value) { - string hex = Convert.ToString(b,16); + string hex = Convert.ToString(b, 16); int l = hex.Length; if (l > 2) { - sb.Append(hex.Substring(l-2,2)); + sb.Append(hex.Substring(l - 2, 2)); } else { @@ -365,7 +364,7 @@ private byte[] DecodeByteArray(string Value) l /= 2; byte[] Result = new byte[l]; - for (int i=0; i 72) windSpeedKph = 72; // Windspeed limited to 20 m/s = 72 km/h double apptemp = (1.04 * tempC) + (2 * avp) - (windSpeedKph * 0.1805553) - 2.7; double feels; @@ -192,7 +192,8 @@ public static double CalculateWetBulbCIterative(double tempC, int humidity, doub double Tw; double Tw1 = tempC; - do { + do + { Tw = Tw1; var Ewg = SaturationVapourPressure1980(Tw); var eg = Ewg - pressureHpa * (tempC - Tw) * 0.00066 * (1 + (0.00115 * Tw)); @@ -225,7 +226,7 @@ public static double DewPoint(double tempC, double humidity) return tempC; // Davis algorithm - double lnVapor = Math.Log(ActualVapourPressure2008(tempC, (int)humidity)); + double lnVapor = Math.Log(ActualVapourPressure2008(tempC, (int) humidity)); return ((243.12 * lnVapor) - 440.1) / (19.43 - lnVapor); } diff --git a/CumulusMX/MoonriseMoonset.cs b/CumulusMX/MoonriseMoonset.cs index ef93af64..5263ed1f 100644 --- a/CumulusMX/MoonriseMoonset.cs +++ b/CumulusMX/MoonriseMoonset.cs @@ -8,18 +8,18 @@ namespace CumulusMX internal class MoonriseMoonset { // JavaScript by Peter Hayes http://www.aphayes.pwp.blueyonder.co.uk/ -// Copyright 2001-2010 -// Unless otherwise stated this code is based on the methods in -// Astronomical Algorithms, first edition, by Jean Meeus -// Published by Willmann-Bell, Inc. -// This code is made freely available but please keep this notice. -// The calculations are approximate but should be good enough for general use, -// I accept no responsibility for errors in astronomy or coding. + // Copyright 2001-2010 + // Unless otherwise stated this code is based on the methods in + // Astronomical Algorithms, first edition, by Jean Meeus + // Published by Willmann-Bell, Inc. + // This code is made freely available but please keep this notice. + // The calculations are approximate but should be good enough for general use, + // I accept no responsibility for errors in astronomy or coding. -// WARNING moonrise code changed on 6 May 2003 to correct a systematic error -// these are now local times NOT UTC as the original code did. + // WARNING moonrise code changed on 6 May 2003 to correct a systematic error + // these are now local times NOT UTC as the original code did. -// Meeus first edition table 45.A Longitude and distance of the moon + // Meeus first edition table 45.A Longitude and distance of the moon private static readonly int[] T45AD = { @@ -59,7 +59,7 @@ internal class MoonriseMoonset -1897, -2117, 2354, 0, 0, -1423, -1117, -1571, -1739, 0, -4421, 0, 0, 0, 0, 1165, 0, 0, 8752 }; -// Meeus table 45B latitude of the moon + // Meeus table 45B latitude of the moon private static readonly int[] T45BD = { @@ -1088,27 +1088,27 @@ internal class MoonriseMoonset private static double rev(double angle) { - return angle - Math.Floor(angle/360.0)*360.0; + return angle - Math.Floor(angle / 360.0) * 360.0; } private static double sind(double angle) { - return Math.Sin((angle*Math.PI)/180.0); + return Math.Sin((angle * Math.PI) / 180.0); } private static double cosd(double angle) { - return Math.Cos((angle*Math.PI)/180.0); + return Math.Cos((angle * Math.PI) / 180.0); } private static double tand(double angle) { - return Math.Tan((angle*Math.PI)/180.0); + return Math.Tan((angle * Math.PI) / 180.0); } private static double asind(double c) { - return (180.0/Math.PI)*Math.Asin(c); + return (180.0 / Math.PI) * Math.Asin(c); } //private double acosd(double c) @@ -1119,48 +1119,49 @@ private static double asind(double c) private static double atan2d(double y, double x) { double num = x < 0 ? 1 : 0; - return (180.0/Math.PI)*Math.Atan(y/x) - 180.0*num; + return (180.0 / Math.PI) * Math.Atan(y / x) - 180.0 * num; } - private static double DayNum(int year, int month,int day,double hours) { + private static double DayNum(int year, int month, int day, double hours) + { // Day number is a modified Julian date, day 0 is 2000 January 0.0 // which corresponds to a Julian date of 2451543.5 - double d= (367*year-Math.Floor(7*(year+Math.Floor((month+9)/12.0))/4) - +Math.Floor((275*month)/9.0)+day-730530+hours/24); + double d = (367 * year - Math.Floor(7 * (year + Math.Floor((month + 9) / 12.0)) / 4) + + Math.Floor((275 * month) / 9.0) + day - 730530 + hours / 24); return d; } - private static double Julian(int year, int month,int day,double hours) + private static double Julian(int year, int month, int day, double hours) { return DayNum(year, month, day, hours) + 2451543.5; } - private static double LocalSidereal(int year,int month,int day,double hours,double lon) + private static double LocalSidereal(int year, int month, int day, double hours, double lon) { // Compute local sidereal time in degrees // year, month, day and hours are the Greenwich date and time // lon is the observers longitude - var d=DayNum(year,month,day,hours); - var lst=(98.9818+0.985647352*d+hours*15+lon); - return rev(lst)/15; + var d = DayNum(year, month, day, hours); + var lst = (98.9818 + 0.985647352 * d + hours * 15 + lon); + return rev(lst) / 15; } - private static double[] RaDecToAltAz(double ra,double dec,int year,int month,int day,double hours,double lat,double lon) + private static double[] RaDecToAltAz(double ra, double dec, int year, int month, int day, double hours, double lat, double lon) { // convert ra and dec to altitude and azimuth // year, month, day and hours are the Greenwich date and time // lat and lon are the observers latitude and longitude - var lst=LocalSidereal(year,month,day,hours,lon); - var x=cosd(15.0*(lst-ra))*cosd(dec); - var y=sind(15.0*(lst-ra))*cosd(dec); - var z=sind(dec); + var lst = LocalSidereal(year, month, day, hours, lon); + var x = cosd(15.0 * (lst - ra)) * cosd(dec); + var y = sind(15.0 * (lst - ra)) * cosd(dec); + var z = sind(dec); // rotate so z is the local zenith - var xhor=x*sind(lat)-z*cosd(lat); - var yhor=y; - var zhor=x*cosd(lat)+z*sind(lat); - var azimuth=rev(atan2d(yhor,xhor)+180.0); // so 0 degrees is north - var altitude=atan2d(zhor,Math.Sqrt(xhor*xhor+yhor*yhor)); - return new double[]{altitude,azimuth}; + var xhor = x * sind(lat) - z * cosd(lat); + var yhor = y; + var zhor = x * cosd(lat) + z * sind(lat); + var azimuth = rev(atan2d(yhor, xhor) + 180.0); // so 0 degrees is north + var altitude = atan2d(zhor, Math.Sqrt(xhor * xhor + yhor * yhor)); + return new double[] { altitude, azimuth }; } // MoonPos calculates the Moon position, based on Meeus chapter 45 @@ -1168,27 +1169,27 @@ public static double[] MoonPos(int year, int month, int day, double hours) { // julian date var jd = Julian(year, month, day, hours); - var T = (jd - 2451545.0)/36525; - var T2 = T*T; - var T3 = T2*T; - var T4 = T3*T; + var T = (jd - 2451545.0) / 36525; + var T2 = T * T; + var T3 = T2 * T; + var T4 = T3 * T; // Moons mean longitude L' - var LP = 218.3164477 + 481267.88123421*T - 0.0015786*T2 + T3/538841.0 - T4/65194000.0; + var LP = 218.3164477 + 481267.88123421 * T - 0.0015786 * T2 + T3 / 538841.0 - T4 / 65194000.0; // Moons mean elongation - var D = 297.8501921 + 445267.1114034*T - 0.0018819*T2 + T3/545868.0 - T4/113065000.0; + var D = 297.8501921 + 445267.1114034 * T - 0.0018819 * T2 + T3 / 545868.0 - T4 / 113065000.0; // Suns mean anomaly - var M = 357.5291092 + 35999.0502909*T - 0.0001536*T2 + T3/24490000.0; + var M = 357.5291092 + 35999.0502909 * T - 0.0001536 * T2 + T3 / 24490000.0; // Moons mean anomaly M' - var MP = 134.9633964 + 477198.8675055*T + 0.0087414*T2 + T3/69699.0 - T4/14712000.0; + var MP = 134.9633964 + 477198.8675055 * T + 0.0087414 * T2 + T3 / 69699.0 - T4 / 14712000.0; // Moons argument of latitude - var F = 93.2720950 + 483202.0175233*T - 0.0036539*T2 - T3/3526000.0 + T4/863310000.0; + var F = 93.2720950 + 483202.0175233 * T - 0.0036539 * T2 - T3 / 3526000.0 + T4 / 863310000.0; // Additional arguments - var A1 = 119.75 + 131.849*T; - var A2 = 53.09 + 479264.290*T; - var A3 = 313.45 + 481266.484*T; - var E = 1 - 0.002516*T - 0.0000074*T2; - var E2 = E*E; + var A1 = 119.75 + 131.849 * T; + var A2 = 53.09 + 479264.290 * T; + var A3 = 313.45 + 481266.484 * T; + var E = 1 - 0.002516 * T - 0.0000074 * T2; + var E2 = E * E; // Sums of periodic terms from table 45.A and 45.B var Sl = 0.0; var Sr = 0.0; @@ -1197,8 +1198,8 @@ public static double[] MoonPos(int year, int month, int day, double hours) var Eterm = 1.0; if (Math.Abs(T45AM[i]) == 1) Eterm = E; if (Math.Abs(T45AM[i]) == 2) Eterm = E2; - Sl += T45AL[i]*Eterm*sind(rev(T45AD[i]*D + T45AM[i]*M + T45AMP[i]*MP + T45AF[i]*F)); - Sr += T45AR[i]*Eterm*cosd(rev(T45AD[i]*D + T45AM[i]*M + T45AMP[i]*MP + T45AF[i]*F)); + Sl += T45AL[i] * Eterm * sind(rev(T45AD[i] * D + T45AM[i] * M + T45AMP[i] * MP + T45AF[i] * F)); + Sr += T45AR[i] * Eterm * cosd(rev(T45AD[i] * D + T45AM[i] * M + T45AMP[i] * MP + T45AF[i] * F)); } var Sb = 0.0; for (var i = 0; i < 60; i++) @@ -1206,23 +1207,23 @@ public static double[] MoonPos(int year, int month, int day, double hours) var Eterm = 1.0; if (Math.Abs(T45BM[i]) == 1) Eterm = E; if (Math.Abs(T45BM[i]) == 2) Eterm = E2; - Sb += T45BL[i]*Eterm*sind(rev(T45BD[i]*D + T45BM[i]*M + T45BMP[i]*MP + T45BF[i]*F)); + Sb += T45BL[i] * Eterm * sind(rev(T45BD[i] * D + T45BM[i] * M + T45BMP[i] * MP + T45BF[i] * F)); } // Additional additive terms - Sl = Sl + 3958*sind(rev(A1)) + 1962*sind(rev(LP - F)) + 318*sind(rev(A2)); - Sb = Sb - 2235*sind(rev(LP)) + 382*sind(rev(A3)) + 175*sind(rev(A1 - F)) + 175*sind(rev(A1 + F)) + 127*sind(rev(LP - MP)) - 115*sind(rev(LP + MP)); + Sl = Sl + 3958 * sind(rev(A1)) + 1962 * sind(rev(LP - F)) + 318 * sind(rev(A2)); + Sb = Sb - 2235 * sind(rev(LP)) + 382 * sind(rev(A3)) + 175 * sind(rev(A1 - F)) + 175 * sind(rev(A1 + F)) + 127 * sind(rev(LP - MP)) - 115 * sind(rev(LP + MP)); // geocentric longitude, latitude and distance - var mglong = rev(LP + Sl/1000000.0); - var mglat = rev(Sb/1000000.0); + var mglong = rev(LP + Sl / 1000000.0); + var mglat = rev(Sb / 1000000.0); if (mglat > 180.0) mglat -= 360; - var mr = Math.Round(385000.56 + Sr/1000.0); + var mr = Math.Round(385000.56 + Sr / 1000.0); // Obliquity of Ecliptic - var obl = 23.4393 - 3.563E-9*(jd - 2451543.5); + var obl = 23.4393 - 3.563E-9 * (jd - 2451543.5); // RA and dec - var ra = rev(atan2d(sind(mglong)*cosd(obl) - tand(mglat)*sind(obl), cosd(mglong)))/15.0; - var dec = rev(asind(sind(mglat)*cosd(obl) + cosd(mglat)*sind(obl)*sind(mglong))); + var ra = rev(atan2d(sind(mglong) * cosd(obl) - tand(mglat) * sind(obl), cosd(mglong))) / 15.0; + var dec = rev(asind(sind(mglat) * cosd(obl) + cosd(mglat) * sind(obl) * sind(mglong))); if (dec > 180.0) dec -= 360; - return new double[] {ra, dec, mr}; + return new double[] { ra, dec, mr }; } public static double[] MoonRise(int year, int month, int day, double TZ, double latitude, double longitude) @@ -1251,11 +1252,11 @@ public static double[] MoonRise(int year, int month, int day, double TZ, double // set the return code to allow for always up or never rises if (elh[0] > 0.0) { - riseset = new double[]{-2, -2}; + riseset = new double[] { -2, -2 }; } else { - riseset = new double[]{-1, -1}; + riseset = new double[] { -1, -1 }; } hours = 24; rad = MoonPos(year, month, day, hours - TZ); @@ -1269,9 +1270,9 @@ public static double[] MoonRise(int year, int month, int day, double TZ, double double hfirst = 0; double hlast = 24; // Try a binary chop on the hours to speed the search - while (Math.Ceiling((hlast - hfirst)/2.0) > 1) + while (Math.Ceiling((hlast - hfirst) / 2.0) > 1) { - int hmid = (int) (hfirst + Math.Round((hlast - hfirst)/2.0)); + int hmid = (int) (hfirst + Math.Round((hlast - hfirst) / 2.0)); if (!elhdone[hmid]) { hours = hmid; @@ -1328,26 +1329,26 @@ public static double[] MoonRise(int year, int month, int day, double TZ, double // alert("day ="+day+" hour ="+hours+" altaz="+altaz[0]+" "+altaz[1]); if ((rise == 0) && (altaz[0] <= 0.0)) { - hfirst = hours; + hfirst = hours; elfirst = altaz[0]; } if ((rise == 0) && (altaz[0] > 0.0)) { - hlast = hours; + hlast = hours; ellast = altaz[0]; } if ((rise == 1) && (altaz[0] <= 0.0)) { - hlast = hours; + hlast = hours; ellast = altaz[0]; } if ((rise == 1) && (altaz[0] > 0.0)) { - hfirst = hours; + hfirst = hours; elfirst = altaz[0]; } var eld = Math.Abs(elfirst) + Math.Abs(ellast); - riseset[rise] = hfirst + (hlast - hfirst)*Math.Abs(elfirst)/eld; + riseset[rise] = hfirst + (hlast - hfirst) * Math.Abs(elfirst) / eld; } } // End of rise/set loop return (riseset); @@ -1357,22 +1358,22 @@ public static double MoonPhase(int year, int month, int day, int hours) { // the illuminated percentage from Meeus chapter 46 var j = Julian(year, month, day, hours); - var T = (j - 2451545.0)/36525; - var T2 = T*T; - var T3 = T2*T; - var T4 = T3*T; + var T = (j - 2451545.0) / 36525; + var T2 = T * T; + var T3 = T2 * T; + var T4 = T3 * T; // Moons mean elongation Meeus first edition // var D=297.8502042+445267.1115168*T-0.0016300*T2+T3/545868.0-T4/113065000.0; // Moons mean elongation Meeus second edition - var D = 297.8501921 + 445267.1114034*T - 0.0018819*T2 + T3/545868.0 - T4/113065000.0; + var D = 297.8501921 + 445267.1114034 * T - 0.0018819 * T2 + T3 / 545868.0 - T4 / 113065000.0; // Moons mean anomaly M' Meeus first edition // var MP=134.9634114+477198.8676313*T+0.0089970*T2+T3/69699.0-T4/14712000.0; // Moons mean anomaly M' Meeus second edition - var MP = 134.9633964 + 477198.8675055*T + 0.0087414*T2 + T3/69699.0 - T4/14712000.0; + var MP = 134.9633964 + 477198.8675055 * T + 0.0087414 * T2 + T3 / 69699.0 - T4 / 14712000.0; // Suns mean anomaly - var M = 357.5291092 + 35999.0502909*T - 0.0001536*T2 + T3/24490000.0; + var M = 357.5291092 + 35999.0502909 * T - 0.0001536 * T2 + T3 / 24490000.0; // phase angle - var pa = 180.0 - D - 6.289*sind(MP) + 2.1*sind(M) - 1.274*sind(2*D - MP) - 0.658*sind(2*D) - 0.214*sind(2*MP) - 0.11*sind(D); + var pa = 180.0 - D - 6.289 * sind(MP) + 2.1 * sind(M) - 1.274 * sind(2 * D - MP) - 0.658 * sind(2 * D) - 0.214 * sind(2 * MP) - 0.11 * sind(D); return (rev(pa)); } @@ -1550,12 +1551,12 @@ public static string CreateMoonImage(double phaseAngle, double latitude, int siz { // moons limb var y = srcSize2 - yPos; - var xPos = (int)(Math.Sqrt(srcSize2 * srcSize2 - y * y) * corr + srcSize2); + var xPos = (int) (Math.Sqrt(srcSize2 * srcSize2 - y * y) * corr + srcSize2); // Determine the edges of the illuminated part of the moon - double x = 1 - ((double)yPos / (double)srcSize2); + double x = 1 - ((double) yPos / (double) srcSize2); x = Math.Sqrt(1 - (x * x)); - var xPos2 = (int)(srcSize2 + (phase - 0.5) * x * srcSize * (-corr)); + var xPos2 = (int) (srcSize2 + (phase - 0.5) * x * srcSize * (-corr)); Point p1 = new Point(xPos, yPos); Point p2 = new Point(xPos2, yPos); diff --git a/CumulusMX/MqttPublisher.cs b/CumulusMX/MqttPublisher.cs index f58b0def..7832e93d 100644 --- a/CumulusMX/MqttPublisher.cs +++ b/CumulusMX/MqttPublisher.cs @@ -3,8 +3,10 @@ using System.IO; using System.Threading; using System.Threading.Tasks; + using MQTTnet; using MQTTnet.Client; + using ServiceStack; namespace CumulusMX @@ -22,7 +24,7 @@ public static void Setup(Cumulus cumulus) var mqttFactory = new MqttFactory(); - mqttClient = (MqttClient)mqttFactory.CreateMqttClient(); + mqttClient = (MqttClient) mqttFactory.CreateMqttClient(); var clientId = Guid.NewGuid().ToString(); @@ -60,7 +62,7 @@ public static void Setup(Cumulus cumulus) mqttClient.DisconnectedAsync += (async e => { - cumulus.LogMessage("Error: MQTT disconnected from the server"); + cumulus.LogWarningMessage("Error: MQTT disconnected from the server"); await Task.Delay(TimeSpan.FromSeconds(5)); cumulus.LogDebugMessage("MQTT attempting to reconnect with server"); @@ -71,7 +73,7 @@ public static void Setup(Cumulus cumulus) } catch { - cumulus.LogMessage("Error: MQTT reconnection to server failed"); + cumulus.LogErrorMessage("Error: MQTT reconnection to server failed"); } }); @@ -96,7 +98,7 @@ private static async Task SendMessageAsync(string topic, string message, bool re } else { - cumulus.LogMessage("MQTT: Error - Not connected to MQTT server - message not sent"); + cumulus.LogErrorMessage("MQTT: Error - Not connected to MQTT server - message not sent"); } } @@ -108,7 +110,7 @@ private static async void Connect(MQTTnet.Client.MqttClientOptions options) } catch (Exception e) { - cumulus.LogMessage("MQTT Error: failed to connect to the host"); + cumulus.LogErrorMessage("MQTT Error: failed to connect to the host"); cumulus.LogMessage(e.Message); } } @@ -175,7 +177,7 @@ public static void UpdateMQTTfeed(string feedType) } catch (Exception ex) { - cumulus.LogMessage($"UpdateMQTTfeed: Error process the template file [{template}], error = {ex.Message}"); + cumulus.LogErrorMessage($"UpdateMQTTfeed: Error processing the template file [{template}], error = {ex.Message}"); } } } diff --git a/CumulusMX/MySQLTable.cs b/CumulusMX/MySQLTable.cs index 3a95537c..5f68156d 100644 --- a/CumulusMX/MySQLTable.cs +++ b/CumulusMX/MySQLTable.cs @@ -1,8 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; namespace CumulusMX { @@ -82,8 +79,8 @@ public string StartOfInsert public string PrimaryKey { - get - { + get + { return _PrimaryKey; } set @@ -121,7 +118,7 @@ internal class Column internal string Name; internal string Attributes; - public Column (string ColName, string ColAttributes) + public Column(string ColName, string ColAttributes) { Name = ColName; Attributes = ColAttributes; diff --git a/CumulusMX/MysqlSettings.cs b/CumulusMX/MysqlSettings.cs index fed1a21c..632be2eb 100644 --- a/CumulusMX/MysqlSettings.cs +++ b/CumulusMX/MysqlSettings.cs @@ -1,16 +1,16 @@ using System; +using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net; +using System.Runtime.Serialization; +using System.Text; + +using EmbedIO; + using MySqlConnector; + using ServiceStack; -using EmbedIO; -using System.Text; -using MySqlConnector.Logging; -using System.Collections.Generic; -using Org.BouncyCastle.Asn1.Cms; -using System.Globalization; -using System.Runtime.Serialization; -using System.Xml.Linq; namespace CumulusMX { @@ -130,7 +130,7 @@ public string GetAlpacaFormData() }; cmdCnt = 0; - for (var i = 0;i < 10; i++) + for (var i = 0; i < 10; i++) { if (!string.IsNullOrEmpty(cumulus.MySqlSettings.CustomTimed.Commands[i])) cmdCnt++; @@ -219,7 +219,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing MySQL Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("MySQL Data: " + json); context.Response.StatusCode = 500; return msg; @@ -378,7 +378,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); context.Response.StatusCode = 500; return msg; } @@ -402,7 +402,7 @@ private string CreateMySQLTable(string createSQL) } catch (Exception ex) { - cumulus.LogMessage("MySQL Create Table: Error encountered during MySQL operation."); + cumulus.LogErrorMessage("MySQL Create Table: Error encountered during MySQL operation."); cumulus.LogMessage(ex.Message); res = "Error: " + ex.Message; } @@ -477,7 +477,7 @@ private string UpdateMySQLTable(MySqlTable table) catch (Exception ex) { cumulus.LogMessage("MySQL Update Table: Error encountered during MySQL operation."); - cumulus.LogMessage(ex.Message); + cumulus.LogErrorMessage(ex.Message); res = "Error: " + ex.Message; } diff --git a/CumulusMX/NOAA.cs b/CumulusMX/NOAA.cs index d8404e80..95ad4f62 100644 --- a/CumulusMX/NOAA.cs +++ b/CumulusMX/NOAA.cs @@ -3,10 +3,8 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Security.Cryptography; using System.Text; -using System.Text.RegularExpressions; -using static System.Collections.Specialized.BitVector32; + namespace CumulusMX { @@ -165,7 +163,7 @@ private double GetAverageWindSpeed(int month, int year, out int domdir) } catch (Exception e) { - cumulus.LogMessage($"Error at line {linenum}, column {idx}, value '{(st.Count >= idx ? st[idx] : "")}' of {logFile} : {e}"); + cumulus.LogErrorMessage($"Error at line {linenum}, column {idx}, value '{(st.Count >= idx ? st[idx] : "")}' of {logFile} : {e}"); cumulus.LogMessage("Please edit the file to correct the error"); } } @@ -188,7 +186,7 @@ private double GetAverageWindSpeed(int month, int year, out int domdir) } catch (Exception ex) { - cumulus.LogMessage("Error in NOAA dominant wind direction calculation: " + ex.Message); + cumulus.LogErrorMessage("Error in NOAA dominant wind direction calculation: " + ex.Message); domdir = 0; } return avgwindspeed; @@ -270,7 +268,7 @@ public string CreateMonthlyReport(DateTime thedate) if (dayList[daynumber].valid) { // already had this date - error! - cumulus.LogMessage($"Duplicate entry in dayfile: {day.Date}."); + cumulus.LogWarningMessage($"Duplicate entry in dayfile: {day.Date}."); continue; } @@ -437,7 +435,7 @@ public string CreateMonthlyReport(DateTime thedate) } catch (Exception ex) { - cumulus.LogMessage($"Error processing dayfile data for {currDate}: {ex.Message}"); + cumulus.LogErrorMessage($"Error processing dayfile data for {currDate}: {ex.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); } @@ -498,7 +496,7 @@ public string CreateMonthlyReport(DateTime thedate) } catch (Exception ex) { - cumulus.LogMessage($"Error at line {linenum}, column {idx}, value '{(st.Count >= idx ? st[idx] : "")}' of {logFile} : {ex}"); + cumulus.LogErrorMessage($"Error at line {linenum}, column {idx}, value '{(st.Count >= idx ? st[idx] : "")}' of {logFile} : {ex}"); cumulus.LogMessage("Please edit the file to correct the error"); // set the days after this error as invalid for (var i = daynumber; i < dayList.Length - 1; i++) @@ -539,7 +537,7 @@ public string CreateMonthlyReport(DateTime thedate) } catch { - cumulus.LogMessage("Error in NOAA dominant wind direction calculation "); + cumulus.LogErrorMessage("Error in NOAA dominant wind direction calculation"); } if (dayList[i].winddomdir == 0) @@ -568,7 +566,7 @@ public string CreateMonthlyReport(DateTime thedate) } catch { - cumulus.LogMessage("Error in NOAA dominant wind direction calculation "); + cumulus.LogErrorMessage("Error in NOAA dominant wind direction calculation"); overalldomdir = 0; } @@ -945,7 +943,7 @@ public string CreateYearlyReport(DateTime thedate) } catch (Exception ex) { - cumulus.LogMessage($"Error at line {linenum} of dayfile.txt: {ex.Message}"); + cumulus.LogErrorMessage($"Error at line {linenum} of dayfile.txt: {ex.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); } @@ -1226,7 +1224,7 @@ public string CreateYearlyReport(DateTime thedate) } catch (Exception e) { - cumulus.LogMessage($"CreateYearlyReport: Error creating wind section: {e.Message}"); + cumulus.LogErrorMessage($"CreateYearlyReport: Error creating wind section: {e.Message}"); cumulus.LogDebugMessage("CreateYearlyReport: Last line generated was..."); cumulus.LogDebugMessage($"CreateYearlyReport: \"{repLine}\""); throw; @@ -1237,7 +1235,7 @@ public string CreateYearlyReport(DateTime thedate) } catch (Exception e) { - cumulus.LogMessage($"CreateYearlyReport: Error creating the report: {e.Message}"); + cumulus.LogErrorMessage($"CreateYearlyReport: Error creating the report: {e.Message}"); cumulus.LogDebugMessage("CreateYearlyReport: Output generated so far was..."); cumulus.LogDebugMessage(string.Join("\n", output)); throw; @@ -1267,7 +1265,7 @@ public string CreateYearlyReport(DateTime thedate) } catch (Exception e) { - cumulus.LogMessage($"CreateYearlyReport: Error creating wind summary: {e.Message}"); + cumulus.LogErrorMessage($"CreateYearlyReport: Error creating wind summary: {e.Message}"); cumulus.LogDebugMessage("CreateYearlyReport: Last line generated was..."); cumulus.LogDebugMessage($"CreateYearlyReport: \"{repLine}\""); throw; @@ -1282,7 +1280,7 @@ public string CreateYearlyReport(DateTime thedate) } catch (Exception ex) { - cumulus.LogMessage("Error in NOAA dominant wind direction calculation: " + ex.Message); + cumulus.LogErrorMessage("Error in NOAA dominant wind direction calculation: " + ex.Message); domdir = 0; } diff --git a/CumulusMX/NOAAReports.cs b/CumulusMX/NOAAReports.cs index a8724c7a..00910fdc 100644 --- a/CumulusMX/NOAAReports.cs +++ b/CumulusMX/NOAAReports.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Threading.Tasks; namespace CumulusMX { @@ -36,7 +37,7 @@ public string GenerateNoaaYearReport(int year) } catch (Exception e) { - cumulus.LogMessage($"Error creating NOAA yearly report: {e.Message}"); + cumulus.LogErrorMessage($"Error creating NOAA yearly report: {e.Message}"); throw; } return report; @@ -63,12 +64,108 @@ public string GenerateNoaaMonthReport(int year, int month) } catch (Exception e) { - cumulus.LogMessage($"Error creating NOAA yearly report '{reportName}': {e.Message}"); + cumulus.LogErrorMessage($"Error creating NOAA yearly report '{reportName}': {e.Message}"); throw; } return report; } + public string GenerateMissing() + { + var missingMonths = new List(); + var missingYears = new List(); + var checkDate = cumulus.RecordsBeganDateTime.Date; + string reportName; + var now = DateTime.Now; + + + var lastRptDate = GetLastReportDate(); + var lastYear = 0; + + // iterate all years and months since records began date + var doMore = true; + while (doMore) + { + // first check the yearly report + if (lastYear != checkDate.Year) + { + reportName = checkDate.ToString(cumulus.NOAAconf.YearFile); + + if (!File.Exists(cumulus.ReportPath + reportName)) + { + missingYears.Add(checkDate); + } + lastYear = checkDate.Year; + } + + // then check the monthly report + reportName = checkDate.ToString(cumulus.NOAAconf.MonthFile); + + if (!File.Exists(cumulus.ReportPath + reportName)) + { + missingMonths.Add(checkDate); + } + + // increment the month + // note this may reset the day + checkDate = checkDate.AddMonths(1); + + if (checkDate.Year == lastRptDate.Year && checkDate.Month == lastRptDate.Month) + { + doMore = false; + } + } + + + if (missingMonths.Count > 0 || missingYears.Count > 0) + { + // spawn a task to recreate the reports, but don't wait for it to complete + + Task.Run(() => + { + // first do the months + foreach (var month in missingMonths) + { + GenerateNoaaMonthReport(month.Year, month.Month); + } + + // then the years + foreach (var year in missingYears) + { + GenerateNoaaYearReport(year.Year); + } + }); + + // report back how many reports are being created + var sb = new StringBuilder("Recreating the following reports...\n"); + if (missingMonths.Count > 0) + { + sb.AppendLine("Monthly:"); + foreach (var rpt in missingMonths) + { + sb.AppendLine("\t" + rpt.ToString("MMM yyyy")); + } + } + + if (missingYears.Count > 0) + { + sb.AppendLine("\nYearly:"); + foreach (var rpt in missingYears) + { + sb.AppendLine("\t" + rpt.ToString("yyyy")); + } + } + + sb.Append("\nThis may take a little while, you can check the progress in the MX diags log"); + + return sb.ToString(); + } + 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"; + } + } + public string GetNoaaYearReport(int year) { DateTime noaats = new DateTime(year, 1, 1); @@ -83,7 +180,7 @@ public string GetNoaaYearReport(int year) } catch (Exception e) { - cumulus.LogMessage($"Error getting NOAA yearly report '{reportName}': {e.Message}"); + cumulus.LogErrorMessage($"Error getting NOAA yearly report '{reportName}': {e.Message}"); report = "Something went wrong!"; } return report; @@ -103,8 +200,8 @@ public string GetNoaaMonthReport(int year, int month) } catch (Exception e) { - cumulus.LogMessage($"Error getting NOAA monthly report '{reportName}': {e.Message}"); - report = "Something went wrong!" ; + cumulus.LogErrorMessage($"Error getting NOAA monthly report '{reportName}': {e.Message}"); + report = "Something went wrong!"; } return report; } @@ -143,7 +240,7 @@ public string GetLastNoaaYearReportFilename(DateTime dat, bool fullPath) return logfiledate.ToString(cumulus.NOAAconf.YearFile); } - public string GetLastNoaaMonthReportFilename (DateTime dat, bool fullPath) + public string GetLastNoaaMonthReportFilename(DateTime dat, bool fullPath) { // First determine the date for the log file. // If we're using 9am roll-over, the date should be 9 hours (10 in summer) @@ -175,5 +272,34 @@ public string GetLastNoaaMonthReportFilename (DateTime dat, bool fullPath) else return logfiledate.AddHours(-1).ToString(cumulus.NOAAconf.MonthFile); } + + private DateTime GetLastReportDate() + { + // returns the datetime of the latest possible report + var now = DateTime.Now; + DateTime reportDate; + + if (cumulus.RolloverHour == 0) + { + reportDate = now.AddDays(-1); + } + else + { + TimeZone tz = TimeZone.CurrentTimeZone; + + if (cumulus.Use10amInSummer && tz.IsDaylightSavingTime(now)) + { + // Locale is currently on Daylight (summer) time + reportDate = now.AddHours(-10); + } + else + { + // Locale is currently on Standard time or unknown + reportDate = now.AddHours(-9); + } + } + + return reportDate.Date; + } } } diff --git a/CumulusMX/NOAASettings.cs b/CumulusMX/NOAASettings.cs index 2a81ed06..1536e9c6 100644 --- a/CumulusMX/NOAASettings.cs +++ b/CumulusMX/NOAASettings.cs @@ -1,10 +1,11 @@ using System; -using System.Globalization; using System.IO; using System.Net; -using ServiceStack; + using EmbedIO; +using ServiceStack; + namespace CumulusMX { public class NOAASettings @@ -21,7 +22,7 @@ public string GetAlpacaFormData() //var InvC = new CultureInfo(""); var normalmeantemps = new JsonNOAASettingsNormalMeanTemps() { - jan = Math.Round(cumulus.NOAAconf.TempNorms[1],cumulus.TempDPlaces), + jan = Math.Round(cumulus.NOAAconf.TempNorms[1], cumulus.TempDPlaces), feb = Math.Round(cumulus.NOAAconf.TempNorms[2], cumulus.TempDPlaces), mar = Math.Round(cumulus.NOAAconf.TempNorms[3], cumulus.TempDPlaces), apr = Math.Round(cumulus.NOAAconf.TempNorms[4], cumulus.TempDPlaces), @@ -135,7 +136,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing NOAA Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("NOAA Data: " + json); context.Response.StatusCode = 500; return msg; @@ -155,7 +156,7 @@ public string UpdateConfig(IHttpContext context) cumulus.NOAAconf.State = string.IsNullOrWhiteSpace(settings.sitedetails.state) ? null : settings.sitedetails.state.Trim(); cumulus.NOAAconf.MonthFile = string.IsNullOrWhiteSpace(settings.outputfiles.monthfileformat) ? null : settings.outputfiles.monthfileformat.Trim(); - cumulus.NOAAconf.YearFile = string.IsNullOrWhiteSpace(settings.outputfiles.yearfileformat) ? null : settings.outputfiles.yearfileformat.Trim(); + cumulus.NOAAconf.YearFile = string.IsNullOrWhiteSpace(settings.outputfiles.yearfileformat) ? null : settings.outputfiles.yearfileformat.Trim(); cumulus.NOAAconf.Use12hour = settings.options.timeformat == 1; cumulus.NOAAconf.UseUtf8 = settings.options.utf8; @@ -216,7 +217,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing NOAA settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("NOAA data: " + json); context.Response.StatusCode = 500; return msg; @@ -228,28 +229,28 @@ public string UpdateConfig(IHttpContext context) public class JsonNOAASettingsData { public bool accessible { get; set; } - public bool autosave {get; set; } + public bool autosave { get; set; } public JsonNOAASettingsSite sitedetails { get; set; } public JsonNOAASettingsOutput outputfiles { get; set; } public JsonNOAASettingsOptions options { get; set; } public JsonNOAASettingsFtpCopy ftp { get; set; } public JsonNOAASettingsFtpCopy copy { get; set; } public JsonNOAASettingsThresholds thresholds { get; set; } - public JsonNOAASettingsNormalMeanTemps normalmeantemps {get; set; } - public JsonNOAASettingsNormalRain normalrain {get; set; } + public JsonNOAASettingsNormalMeanTemps normalmeantemps { get; set; } + public JsonNOAASettingsNormalRain normalrain { get; set; } } public class JsonNOAASettingsSite { - public string sitename {get; set; } - public string city {get; set; } - public string state {get; set; } + public string sitename { get; set; } + public string city { get; set; } + public string state { get; set; } } public class JsonNOAASettingsOutput { - public string monthfileformat {get; set; } - public string yearfileformat {get; set; } + public string monthfileformat { get; set; } + public string yearfileformat { get; set; } } public class JsonNOAASettingsOptions @@ -282,33 +283,33 @@ public class JsonNOAASettingsThresholds public class JsonNOAASettingsNormalMeanTemps { - public double jan {get; set; } - public double feb {get; set; } - public double mar {get; set; } - public double apr {get; set; } - public double may {get; set; } - public double jun {get; set; } - public double jul {get; set; } - public double aug {get; set; } - public double sep {get; set; } - public double oct {get; set; } - public double nov {get; set; } - public double dec {get; set; } + public double jan { get; set; } + public double feb { get; set; } + public double mar { get; set; } + public double apr { get; set; } + public double may { get; set; } + public double jun { get; set; } + public double jul { get; set; } + public double aug { get; set; } + public double sep { get; set; } + public double oct { get; set; } + public double nov { get; set; } + public double dec { get; set; } } public class JsonNOAASettingsNormalRain { - public double jan {get; set; } - public double feb {get; set; } - public double mar {get; set; } - public double apr {get; set; } - public double may {get; set; } - public double jun {get; set; } - public double jul {get; set; } - public double aug {get; set; } - public double sep {get; set; } - public double oct {get; set; } - public double nov {get; set; } - public double dec {get; set; } + public double jan { get; set; } + public double feb { get; set; } + public double mar { get; set; } + public double apr { get; set; } + public double may { get; set; } + public double jun { get; set; } + public double jul { get; set; } + public double aug { get; set; } + public double sep { get; set; } + public double oct { get; set; } + public double nov { get; set; } + public double dec { get; set; } } } diff --git a/CumulusMX/Program.cs b/CumulusMX/Program.cs index 24375b70..3e45c57b 100644 --- a/CumulusMX/Program.cs +++ b/CumulusMX/Program.cs @@ -1,11 +1,11 @@ using System; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Reflection; using System.Runtime.InteropServices; -using System.Threading; using System.ServiceProcess; -using System.IO; +using System.Threading; namespace CumulusMX { @@ -86,7 +86,7 @@ private static void Main(string[] args) while (!exitSystem) { // Wait for a signal to be delivered - unixSignalWaitAny?.Invoke(null, new object[] {signals}); + unixSignalWaitAny?.Invoke(null, new object[] { signals }); var msg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff ") + "Exiting system due to external SIGTERM signal"; Console.WriteLine(msg); @@ -149,13 +149,13 @@ private static void Main(string[] args) switch (args[i]) { case "-lang" when args.Length >= i: - { - var lang = args[++i]; + { + var lang = args[++i]; - CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(lang); - CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(lang); - break; - } + CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(lang); + CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(lang); + break; + } case "-port" when args.Length >= i: httpport = Convert.ToInt32(args[++i]); break; @@ -168,39 +168,39 @@ private static void Main(string[] args) Console.WriteLine("The use of the -wsport command line parameter is deprecated"); break; case "-install" when windows: - { - if (SelfInstaller.InstallMe()) { - Console.WriteLine("Cumulus MX is now installed to run as service"); - Environment.Exit(0); + if (SelfInstaller.InstallMe()) + { + Console.WriteLine("Cumulus MX is now installed to run as service"); + Environment.Exit(0); + } + else + { + Console.WriteLine("Cumulus MX failed to install as service"); + Environment.Exit(1); + } + + break; } - else - { - Console.WriteLine("Cumulus MX failed to install as service"); - Environment.Exit(1); - } - - break; - } case "-install": Console.WriteLine("You can only install Cumulus MX as a service in Windows"); Environment.Exit(1); break; case "-uninstall" when windows: - { - if (SelfInstaller.UninstallMe()) { - Console.WriteLine("Cumulus MX is no longer installed to run as service"); - Environment.Exit(0); + if (SelfInstaller.UninstallMe()) + { + Console.WriteLine("Cumulus MX is no longer installed to run as service"); + Environment.Exit(0); + } + else + { + Console.WriteLine("Cumulus MX failed uninstall itself as service"); + Environment.Exit(1); + } + + break; } - else - { - Console.WriteLine("Cumulus MX failed uninstall itself as service"); - Environment.Exit(1); - } - - break; - } case "-uninstall": Console.WriteLine("You can only uninstall Cumulus MX as a service in Windows"); Environment.Exit(1); @@ -233,7 +233,7 @@ private static void Main(string[] args) } else { - if (Environment.UserInteractive ||(!windows && !service)) + if (Environment.UserInteractive || (!windows && !service)) { // Windows interactive or Linux and no service flag svcTextListener.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff ") + "Running interactively"); @@ -353,7 +353,7 @@ private static bool Handler(CtrlType sig) { var reason = new[] { "Ctrl-C", "Ctrl-Break", "Close Main Window", "unknown", "unknown", "User Logoff", "System Shutdown" }; - Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff ") + "Exiting system due to external: " + reason[(int)sig]); + Trace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff ") + "Exiting system due to external: " + reason[(int) sig]); if (Program.cumulus != null) { diff --git a/CumulusMX/ProgramSettings.cs b/CumulusMX/ProgramSettings.cs index 8a5baf20..3559614d 100644 --- a/CumulusMX/ProgramSettings.cs +++ b/CumulusMX/ProgramSettings.cs @@ -3,9 +3,10 @@ using System.IO; using System.Net; using System.Threading; -using ServiceStack.Text; + using EmbedIO; -using System.Diagnostics; + +using ServiceStack.Text; namespace CumulusMX { @@ -57,7 +58,8 @@ public string GetAlpacaFormData() datalogging = cumulus.ProgramOptions.DataLogging, ftplogging = cumulus.FtpOptions.Logging, emaillogging = cumulus.SmtpOptions.Logging, - spikelogging = cumulus.ErrorLogSpikeRemoval + spikelogging = cumulus.ErrorLogSpikeRemoval, + errorlistlevel = (int) cumulus.ErrorListLoggingLevel }; var options = new JsonProgramSettingsGeneralOptions() @@ -72,6 +74,13 @@ public string GetAlpacaFormData() timeFormat = cumulus.ProgramOptions.TimeFormat }; + var security = new JsonProgramSettingsSecurity() + { + securesettings = cumulus.ProgramOptions.SecureSettings, + username = cumulus.ProgramOptions.SettingsUsername, + password = cumulus.ProgramOptions.SettingsPassword, + }; + var settings = new JsonProgramSettings() { accessible = cumulus.ProgramOptions.EnableAccessibility, @@ -79,7 +88,8 @@ public string GetAlpacaFormData() shutdown = shutdown, logging = logging, options = options, - culture = culture + culture = culture, + security = security }; //return JsonConvert.SerializeObject(data); @@ -109,7 +119,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Program Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Program Data: " + json); context.Response.StatusCode = 500; return msg; @@ -138,12 +148,17 @@ public string UpdateConfig(IHttpContext context) cumulus.ProgramOptions.DataLogging = settings.logging.datalogging; cumulus.SmtpOptions.Logging = settings.logging.emaillogging; cumulus.ErrorLogSpikeRemoval = settings.logging.spikelogging; + cumulus.ErrorListLoggingLevel = (Cumulus.LogLevel) settings.logging.errorlistlevel; cumulus.ProgramOptions.WarnMultiple = settings.options.stopsecondinstance; cumulus.ProgramOptions.ListWebTags = settings.options.listwebtags; cumulus.ProgramOptions.TimeFormat = settings.culture.timeFormat; cumulus.ProgramOptions.Culture.RemoveSpaceFromDateSeparator = settings.culture.removespacefromdateseparator; + cumulus.ProgramOptions.SecureSettings = settings.security.securesettings; + cumulus.ProgramOptions.SettingsUsername = (settings.security.username ?? string.Empty).Trim(); + cumulus.ProgramOptions.SettingsPassword = (settings.security.password ?? string.Empty).Trim(); + if (cumulus.ProgramOptions.TimeFormat == "t") cumulus.ProgramOptions.TimeFormatLong = "T"; else if (cumulus.ProgramOptions.TimeFormat == "h:mm tt") @@ -189,7 +204,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Program Options: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -209,6 +224,7 @@ public class JsonProgramSettings public JsonProgramSettingsLoggingOptions logging { get; set; } public JsonProgramSettingsGeneralOptions options { get; set; } public JsonProgramSettingsCultureOptions culture { get; set; } + public JsonProgramSettingsSecurity security { get; set; } } public class JsonProgramSettingsStartupOptions @@ -217,7 +233,7 @@ public class JsonProgramSettingsStartupOptions public int startuppingescape { get; set; } public int startupdelay { get; set; } public int startupdelaymaxuptime { get; set; } - public JsonProgramSettingsTask startuptask {get; set;} + public JsonProgramSettingsTask startuptask { get; set; } } public class JsonProgramSettingsTask @@ -234,6 +250,7 @@ public class JsonProgramSettingsLoggingOptions public bool ftplogging { get; set; } public bool emaillogging { get; set; } public bool spikelogging { get; set; } + public int errorlistlevel { get; set; } } public class JsonProgramSettingsGeneralOptions { @@ -251,4 +268,10 @@ public class JsonProgramSettingsShutdownOptions public int datastoppedmins { get; set; } public JsonProgramSettingsTask shutdowntask { get; set; } } + public class JsonProgramSettingsSecurity + { + public bool securesettings { get; set;} + public string username { get; set; } + public string password { get; set; } + } } diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs deleted file mode 100644 index 179b7566..00000000 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Cumulus MX")] -[assembly: AssemblyDescription("Version 3.26.0 - Build 3248")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Cumulus MX")] -[assembly: AssemblyCopyright("Copyright © 2015-2023 Cumulus MX")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("57190d2e-7e45-4efb-8c09-06a176cef3f3")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.26.0.3248")] -[assembly: AssemblyFileVersion("3.26.0.3248")] diff --git a/CumulusMX/Properties/launchSettings.json b/CumulusMX/Properties/launchSettings.json new file mode 100644 index 00000000..91e544c8 --- /dev/null +++ b/CumulusMX/Properties/launchSettings.json @@ -0,0 +1,7 @@ +{ + "profiles": { + "CumulusMX": { + "commandName": "Project" + } + } +} \ No newline at end of file diff --git a/CumulusMX/SQLite.cs b/CumulusMX/SQLite.cs index e61ed133..335ca3ad 100644 --- a/CumulusMX/SQLite.cs +++ b/CumulusMX/SQLite.cs @@ -1700,7 +1700,8 @@ public int InsertAll(System.Collections.IEnumerable objects, bool runInTransacti var c = 0; if (runInTransaction) { - RunInTransaction(() => { + RunInTransaction(() => + { foreach (var r in objects) { c += Insert(r); @@ -1737,7 +1738,8 @@ public int InsertAll(System.Collections.IEnumerable objects, string extra, bool var c = 0; if (runInTransaction) { - RunInTransaction(() => { + RunInTransaction(() => + { foreach (var r in objects) { c += Insert(r, extra); @@ -1774,7 +1776,8 @@ public int InsertAll(System.Collections.IEnumerable objects, Type objType, bool var c = 0; if (runInTransaction) { - RunInTransaction(() => { + RunInTransaction(() => + { foreach (var r in objects) { c += Insert(r, objType); @@ -2142,7 +2145,8 @@ public int UpdateAll(System.Collections.IEnumerable objects, bool runInTransacti var c = 0; if (runInTransaction) { - RunInTransaction(() => { + RunInTransaction(() => + { foreach (var r in objects) { c += Update(r); @@ -3099,7 +3103,8 @@ public static string Collation(MemberInfo p) return (p.CustomAttributes .Where(x => typeof(CollationAttribute) == x.AttributeType) - .Select(x => { + .Select(x => + { var args = x.ConstructorArguments; return args.Count > 0 ? ((args[0].Value as string) ?? "") : ""; }) @@ -3749,31 +3754,36 @@ internal static Action GetFastSetter(SQLiteCon if (clrType == typeof(String)) { - fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => + { return SQLite3.ColumnString(stmt, index); }); } else if (clrType == typeof(Int32)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return SQLite3.ColumnInt(stmt, index); }); } else if (clrType == typeof(Boolean)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return SQLite3.ColumnInt(stmt, index) == 1; }); } else if (clrType == typeof(double)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return SQLite3.ColumnDouble(stmt, index); }); } else if (clrType == typeof(float)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (float) SQLite3.ColumnDouble(stmt, index); }); } @@ -3781,13 +3791,15 @@ internal static Action GetFastSetter(SQLiteCon { if (conn.StoreTimeSpanAsTicks) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return new TimeSpan(SQLite3.ColumnInt64(stmt, index)); }); } else { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { var text = SQLite3.ColumnString(stmt, index); TimeSpan resultTime; if (!TimeSpan.TryParseExact(text, "c", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.TimeSpanStyles.None, out resultTime)) @@ -3802,13 +3814,15 @@ internal static Action GetFastSetter(SQLiteCon { if (conn.StoreDateTimeAsTicks) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return new DateTime(SQLite3.ColumnInt64(stmt, index)); }); } else { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { var text = SQLite3.ColumnString(stmt, index); DateTime resultDate; if (!DateTime.TryParseExact(text, conn.DateTimeStringFormat, System.Globalization.CultureInfo.InvariantCulture, conn.DateTimeStyle, out resultDate)) @@ -3821,7 +3835,8 @@ internal static Action GetFastSetter(SQLiteCon } else if (clrType == typeof(DateTimeOffset)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return new DateTimeOffset(SQLite3.ColumnInt64(stmt, index), TimeSpan.Zero); }); } @@ -3831,76 +3846,88 @@ internal static Action GetFastSetter(SQLiteCon } else if (clrType == typeof(Int64)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return SQLite3.ColumnInt64(stmt, index); }); } else if (clrType == typeof(UInt32)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (uint) SQLite3.ColumnInt64(stmt, index); }); } else if (clrType == typeof(decimal)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (decimal) SQLite3.ColumnDouble(stmt, index); }); } else if (clrType == typeof(Byte)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (byte) SQLite3.ColumnInt(stmt, index); }); } else if (clrType == typeof(UInt16)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (ushort) SQLite3.ColumnInt(stmt, index); }); } else if (clrType == typeof(Int16)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (short) SQLite3.ColumnInt(stmt, index); }); } else if (clrType == typeof(sbyte)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { return (sbyte) SQLite3.ColumnInt(stmt, index); }); } else if (clrType == typeof(byte[])) { - fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => + { return SQLite3.ColumnByteArray(stmt, index); }); } else if (clrType == typeof(Guid)) { - fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateNullableTypedSetterDelegate(column, (stmt, index) => + { var text = SQLite3.ColumnString(stmt, index); return new Guid(text); }); } else if (clrType == typeof(Uri)) { - fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => + { var text = SQLite3.ColumnString(stmt, index); return new Uri(text); }); } else if (clrType == typeof(StringBuilder)) { - fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => + { var text = SQLite3.ColumnString(stmt, index); return new StringBuilder(text); }); } else if (clrType == typeof(UriBuilder)) { - fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => { + fastSetter = CreateTypedSetterDelegate(column, (stmt, index) => + { var text = SQLite3.ColumnString(stmt, index); return new UriBuilder(text); }); @@ -3938,7 +3965,8 @@ private static Action CreateNullableTypedSetterDe typeof(Action), null, column.PropertyInfo.GetSetMethod()); - return (o, stmt, i) => { + return (o, stmt, i) => + { var colType = SQLite3.ColumnType(stmt, i); if (colType != SQLite3.ColType.Null) setProperty.Invoke((ObjectType) o, getColumnValue.Invoke(stmt, i)); @@ -3962,7 +3990,8 @@ private static Action CreateTypedSetterDelegate), null, column.PropertyInfo.GetSetMethod()); - return (o, stmt, i) => { + return (o, stmt, i) => + { var colType = SQLite3.ColumnType(stmt, i); if (colType != SQLite3.ColType.Null) setProperty.Invoke((ObjectType) o, getColumnValue.Invoke(stmt, i)); diff --git a/CumulusMX/Simulator.cs b/CumulusMX/Simulator.cs index eb24ee2a..9d507878 100644 --- a/CumulusMX/Simulator.cs +++ b/CumulusMX/Simulator.cs @@ -1,10 +1,5 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; namespace CumulusMX { @@ -78,7 +73,7 @@ public override void Start() catch (Exception ex) { // any others, log them and carry on - cumulus.LogMessage("Simulator Start: Exception = " + ex.Message); + cumulus.LogErrorMessage("Simulator Start: Exception = " + ex.Message); } } @@ -170,7 +165,7 @@ private void doSolar(DateTime recDate) solar += volatility * (2 * random.NextDouble() - 1); if (solar < 0 || CurrentSolarMax == 0) solar = 0; - DoSolarRad((int)solar, recDate); + DoSolarRad((int) solar, recDate); } @@ -200,7 +195,7 @@ public DataSet() // Temperature - both annual and daily variations, daily offset by 0.1 of a day var tempMean = new Func((x) => 15 + 10 * Math.Cos(x.DayOfYear / 365.0 * 2 * Math.PI) - 10 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI)); // Wind - daily variation, offset by 0.1 of a day - var windMean = new Func((x) => 10 - 9.5 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI)); + var windMean = new Func((x) => 10 - 9.5 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI)); var windVolatility = new Func((x) => 2 - 1.5 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI)); // Humidity - daily variation, offset by 0.1 of a day var humMean = new Func((x) => 60 + 30 * Math.Cos((x.TimeOfDay.TotalDays - 0.1) * 2 * Math.PI)); @@ -224,16 +219,16 @@ public DataSet() public void SetNewData(DateTime readTime) { tempVal = temperature.GetValue(readTime); - humVal = (int)humidity.GetValue(readTime); - windSpeedVal = Math.Round(windSpeed.GetValue(readTime), 1); + humVal = (int) humidity.GetValue(readTime); + windSpeedVal = Math.Round(windSpeed.GetValue(readTime), 1); if (windSpeedVal > 0) { - windBearingVal = ((int)windDirection.GetValue(readTime) % 360) + 1; + windBearingVal = ((int) windDirection.GetValue(readTime) % 360) + 1; } rainRateVal = rainRate.GetValue(readTime); pressureVal = pressure.GetValue(readTime); tempInVal = insideTemp.GetValue(readTime); - humInVal = (int)insideHum.GetValue(readTime); + humInVal = (int) insideHum.GetValue(readTime); } } diff --git a/CumulusMX/StationSettings.cs b/CumulusMX/StationSettings.cs index 5e75e6e0..830d4f72 100644 --- a/CumulusMX/StationSettings.cs +++ b/CumulusMX/StationSettings.cs @@ -1,15 +1,16 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Net; -using System.Threading; using System.Reflection; -using ServiceStack.Text; +using System.Threading; + using EmbedIO; -using static Swan.Terminal; -using Swan.Formatters; + using ServiceStack; -using System.Globalization; +using ServiceStack.Text; + namespace CumulusMX { @@ -163,13 +164,13 @@ internal string GetAlpacaFormData() interval = cumulus.EcowittCustomInterval }; - ecowitt.forward = new List(); + ecowitt.forward = new List(); for (var i = 0; i < 10; i++) { if (!string.IsNullOrEmpty(cumulus.EcowittForwarders[i])) { - ecowitt.forward.Add(new JsonEcowittForward() { url = cumulus.EcowittForwarders[i] }); + ecowitt.forward.Add(new JsonEcowittForwardList() { url = cumulus.EcowittForwarders[i] }); } } @@ -239,7 +240,7 @@ internal string GetAlpacaFormData() LatToDMS(cumulus.Latitude, out deg, out min, out sec, out hem); - var latitude = new JsonStationSettingsLatLong() {degrees = deg, minutes = min, seconds = sec, hemisphere = hem}; + var latitude = new JsonStationSettingsLatLong() { degrees = deg, minutes = min, seconds = sec, hemisphere = hem }; LongToDMS(cumulus.Longitude, out deg, out min, out sec, out hem); @@ -455,7 +456,7 @@ private void LongToDMS(decimal longitude, out int d, out int m, out int s, out s coordinate = longitude; hem = "East"; } - int secs = (int)(coordinate * 60 * 60); + int secs = (int) (coordinate * 60 * 60); s = secs % 60; @@ -479,7 +480,7 @@ private void LatToDMS(decimal latitude, out int d, out int m, out int s, out str hem = "North"; } - int secs = (int)(coordinate * 60 * 60); + int secs = (int) (coordinate * 60 * 60); s = secs % 60; @@ -512,7 +513,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Station Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Station Data: " + json); context.Response.StatusCode = 500; return msg; @@ -531,7 +532,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Rainfall settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -547,7 +548,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Growing Degree Day settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -562,7 +563,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Temperature Sum settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -576,7 +577,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Chill Hours settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -604,7 +605,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Solar settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -624,7 +625,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Forecast settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -658,7 +659,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Location settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -693,7 +694,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Options settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -708,7 +709,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Log roll-over settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -748,74 +749,81 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Davis VP/VP2/Vue settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } - // WLL + // WLL/Davis Cloud try { if (settings.daviswll != null) { - cumulus.DavisOptions.ConnectionType = 2; // Always TCP/IP for WLL - cumulus.WLLAutoUpdateIpAddress = settings.daviswll.network.autoDiscover; - cumulus.DavisOptions.IPAddr = string.IsNullOrWhiteSpace(settings.daviswll.network.ipaddress) ? null : settings.daviswll.network.ipaddress.Trim(); + if (settings.general.stationtype == 11) // WLL only + { + cumulus.DavisOptions.ConnectionType = 2; // Always TCP/IP for WLL + cumulus.WLLAutoUpdateIpAddress = settings.daviswll.network.autoDiscover; + cumulus.DavisOptions.IPAddr = string.IsNullOrWhiteSpace(settings.daviswll.network.ipaddress) ? null : settings.daviswll.network.ipaddress.Trim(); + + cumulus.DavisOptions.TCPPort = settings.daviswll.advanced.tcpport; + cumulus.WllTriggerDataStoppedOnBroadcast = settings.daviswll.advanced.datastopped; + } cumulus.WllApiKey = string.IsNullOrWhiteSpace(settings.daviswll.api.apiKey) ? null : settings.daviswll.api.apiKey.Trim(); cumulus.WllApiSecret = string.IsNullOrWhiteSpace(settings.daviswll.api.apiSecret) ? null : settings.daviswll.api.apiSecret.Trim(); cumulus.WllStationId = settings.daviswll.api.apiStationId; - cumulus.WllPrimaryRain = settings.daviswll.primary.rain; - cumulus.WllPrimarySolar = settings.daviswll.primary.solar; - cumulus.WllPrimaryTempHum = settings.daviswll.primary.temphum; - cumulus.WllPrimaryUV = settings.daviswll.primary.uv; - cumulus.WllPrimaryWind = settings.daviswll.primary.wind; - - cumulus.WllExtraLeafTx1 = settings.daviswll.soilLeaf.extraLeaf.leafTx1; - cumulus.WllExtraLeafTx2 = settings.daviswll.soilLeaf.extraLeaf.leafTx2; - cumulus.WllExtraLeafIdx1 = settings.daviswll.soilLeaf.extraLeaf.leafIdx1; - cumulus.WllExtraLeafIdx2 = settings.daviswll.soilLeaf.extraLeaf.leafIdx2; - - cumulus.WllExtraSoilMoistureIdx1 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx1; - cumulus.WllExtraSoilMoistureIdx2 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx2; - cumulus.WllExtraSoilMoistureIdx3 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx3; - cumulus.WllExtraSoilMoistureIdx4 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx4; - cumulus.WllExtraSoilMoistureTx1 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx1; - cumulus.WllExtraSoilMoistureTx2 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx2; - cumulus.WllExtraSoilMoistureTx3 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx3; - cumulus.WllExtraSoilMoistureTx4 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx4; - - cumulus.WllExtraSoilTempIdx1 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx1; - cumulus.WllExtraSoilTempIdx2 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx2; - cumulus.WllExtraSoilTempIdx3 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx3; - cumulus.WllExtraSoilTempIdx4 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx4; - cumulus.WllExtraSoilTempTx1 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx1; - cumulus.WllExtraSoilTempTx2 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx2; - cumulus.WllExtraSoilTempTx3 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx3; - cumulus.WllExtraSoilTempTx4 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx4; - - cumulus.WllExtraTempTx[1] = settings.daviswll.extraTemp.extraTempTx1; - cumulus.WllExtraTempTx[2] = settings.daviswll.extraTemp.extraTempTx2; - cumulus.WllExtraTempTx[3] = settings.daviswll.extraTemp.extraTempTx3; - cumulus.WllExtraTempTx[4] = settings.daviswll.extraTemp.extraTempTx4; - cumulus.WllExtraTempTx[5] = settings.daviswll.extraTemp.extraTempTx5; - cumulus.WllExtraTempTx[6] = settings.daviswll.extraTemp.extraTempTx6; - cumulus.WllExtraTempTx[7] = settings.daviswll.extraTemp.extraTempTx7; - cumulus.WllExtraTempTx[8] = settings.daviswll.extraTemp.extraTempTx8; - - cumulus.WllExtraHumTx[1] = settings.daviswll.extraTemp.extraHumTx1; - cumulus.WllExtraHumTx[2] = settings.daviswll.extraTemp.extraHumTx2; - cumulus.WllExtraHumTx[3] = settings.daviswll.extraTemp.extraHumTx3; - cumulus.WllExtraHumTx[4] = settings.daviswll.extraTemp.extraHumTx4; - cumulus.WllExtraHumTx[5] = settings.daviswll.extraTemp.extraHumTx5; - cumulus.WllExtraHumTx[6] = settings.daviswll.extraTemp.extraHumTx6; - cumulus.WllExtraHumTx[7] = settings.daviswll.extraTemp.extraHumTx7; - cumulus.WllExtraHumTx[8] = settings.daviswll.extraTemp.extraHumTx8; + if (settings.general.stationtype == 11 || settings.general.stationtype == 19) // WLL & Cloud WLL/WLC only + { + cumulus.WllPrimaryRain = settings.daviswll.primary.rain; + cumulus.WllPrimarySolar = settings.daviswll.primary.solar; + cumulus.WllPrimaryTempHum = settings.daviswll.primary.temphum; + cumulus.WllPrimaryUV = settings.daviswll.primary.uv; + cumulus.WllPrimaryWind = settings.daviswll.primary.wind; + + cumulus.WllExtraLeafTx1 = settings.daviswll.soilLeaf.extraLeaf.leafTx1; + cumulus.WllExtraLeafTx2 = settings.daviswll.soilLeaf.extraLeaf.leafTx2; + cumulus.WllExtraLeafIdx1 = settings.daviswll.soilLeaf.extraLeaf.leafIdx1; + cumulus.WllExtraLeafIdx2 = settings.daviswll.soilLeaf.extraLeaf.leafIdx2; + + cumulus.WllExtraSoilMoistureIdx1 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx1; + cumulus.WllExtraSoilMoistureIdx2 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx2; + cumulus.WllExtraSoilMoistureIdx3 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx3; + cumulus.WllExtraSoilMoistureIdx4 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistIdx4; + cumulus.WllExtraSoilMoistureTx1 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx1; + cumulus.WllExtraSoilMoistureTx2 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx2; + cumulus.WllExtraSoilMoistureTx3 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx3; + cumulus.WllExtraSoilMoistureTx4 = settings.daviswll.soilLeaf.extraSoilMoist.soilMoistTx4; + + cumulus.WllExtraSoilTempIdx1 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx1; + cumulus.WllExtraSoilTempIdx2 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx2; + cumulus.WllExtraSoilTempIdx3 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx3; + cumulus.WllExtraSoilTempIdx4 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempIdx4; + cumulus.WllExtraSoilTempTx1 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx1; + cumulus.WllExtraSoilTempTx2 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx2; + cumulus.WllExtraSoilTempTx3 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx3; + cumulus.WllExtraSoilTempTx4 = settings.daviswll.soilLeaf.extraSoilTemp.soilTempTx4; + + cumulus.WllExtraTempTx[1] = settings.daviswll.extraTemp.extraTempTx1; + cumulus.WllExtraTempTx[2] = settings.daviswll.extraTemp.extraTempTx2; + cumulus.WllExtraTempTx[3] = settings.daviswll.extraTemp.extraTempTx3; + cumulus.WllExtraTempTx[4] = settings.daviswll.extraTemp.extraTempTx4; + cumulus.WllExtraTempTx[5] = settings.daviswll.extraTemp.extraTempTx5; + cumulus.WllExtraTempTx[6] = settings.daviswll.extraTemp.extraTempTx6; + cumulus.WllExtraTempTx[7] = settings.daviswll.extraTemp.extraTempTx7; + cumulus.WllExtraTempTx[8] = settings.daviswll.extraTemp.extraTempTx8; + + cumulus.WllExtraHumTx[1] = settings.daviswll.extraTemp.extraHumTx1; + cumulus.WllExtraHumTx[2] = settings.daviswll.extraTemp.extraHumTx2; + cumulus.WllExtraHumTx[3] = settings.daviswll.extraTemp.extraHumTx3; + cumulus.WllExtraHumTx[4] = settings.daviswll.extraTemp.extraHumTx4; + cumulus.WllExtraHumTx[5] = settings.daviswll.extraTemp.extraHumTx5; + cumulus.WllExtraHumTx[6] = settings.daviswll.extraTemp.extraHumTx6; + cumulus.WllExtraHumTx[7] = settings.daviswll.extraTemp.extraHumTx7; + cumulus.WllExtraHumTx[8] = settings.daviswll.extraTemp.extraHumTx8; + } cumulus.DavisOptions.RainGaugeType = settings.daviswll.advanced.raingaugetype; - cumulus.DavisOptions.TCPPort = settings.daviswll.advanced.tcpport; - cumulus.WllTriggerDataStoppedOnBroadcast = settings.daviswll.advanced.datastopped; // Automatically enable extra logging? @@ -839,8 +847,8 @@ internal string UpdateConfig(IHttpContext context) } catch (Exception ex) { - var msg = "Error processing WLL settings: " + ex.Message; - cumulus.LogMessage(msg); + var msg = "Error processing WLL/Davis Cloud settings: " + ex.Message; + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -853,7 +861,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Log interval setting: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -871,7 +879,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing GW1000 settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -902,7 +910,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Ecowitt settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1000,7 +1008,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Ecowitt sensor mapping: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1019,7 +1027,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = $"Error processing WeatherFlow settings: {ex.Message}"; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1040,7 +1048,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing EasyWeather settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1061,7 +1069,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Fine Offset settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1083,7 +1091,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Instromet settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1099,7 +1107,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing WMR928 settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1117,7 +1125,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Ecowitt API settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1155,7 +1163,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Units settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1194,7 +1202,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Units settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1207,7 +1215,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Records Began Date: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1218,7 +1226,7 @@ internal string UpdateConfig(IHttpContext context) { if (cumulus.StationType != settings.general.stationtype) { - cumulus.LogMessage("Station type changed, restart required"); + cumulus.LogWarningMessage("Station type changed, restart required"); cumulus.LogConsoleMessage("*** Station type changed, restart required ***", ConsoleColor.Yellow, true); } cumulus.StationType = settings.general.stationtype; @@ -1227,7 +1235,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Station Type setting: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1240,7 +1248,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Accessibility setting: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1252,7 +1260,7 @@ internal string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Station settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Station Data: " + json); errorMsg += msg; context.Response.StatusCode = 500; @@ -1304,7 +1312,7 @@ internal string UploadNow(IHttpContext context) catch (Exception ex) { returnMsg = "Error aborting a currently running upload"; - cumulus.LogMessage($"Upload Now: {returnMsg}: {ex.Message}"); + cumulus.LogErrorMessage($"Upload Now: {returnMsg}: {ex.Message}"); return returnMsg; } } @@ -1352,17 +1360,17 @@ internal string UploadNow(IHttpContext context) cumulus.ftpThread = new Thread(() => cumulus.DoHTMLFiles()) { IsBackground = true }; cumulus.ftpThread.Start(); } - catch(Exception ex) + catch (Exception ex) { returnMsg = "Error starting a new upload"; - cumulus.LogMessage($"Upload Now: {returnMsg}: {ex.Message}"); + cumulus.LogErrorMessage($"Upload Now: {returnMsg}: {ex.Message}"); } cumulus.LogDebugMessage("Upload Now: Process complete"); return returnMsg; } catch (Exception ex) { - cumulus.LogMessage($"Upload Now: General error: {ex.Message}"); + cumulus.LogErrorMessage($"Upload Now: General error: {ex.Message}"); context.Response.StatusCode = 500; return $"Error: {ex.Message}"; } @@ -1400,7 +1408,7 @@ internal string SetSelectaChartOptions(IHttpContext context) catch (Exception ex) { var msg = "Error select-a-chart Options: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -1410,7 +1418,7 @@ internal string SetSelectaChartOptions(IHttpContext context) } catch (Exception ex) { - cumulus.LogMessage(ex.Message); + cumulus.LogErrorMessage("Update Selectaschhrt options error: " + ex.Message); context.Response.StatusCode = 500; return ex.Message; } @@ -1418,6 +1426,50 @@ internal string SetSelectaChartOptions(IHttpContext context) return context.Response.StatusCode == 200 ? "success" : errorMsg; } + internal string SetSelectaPeriodOptions(IHttpContext context) + { + var errorMsg = ""; + context.Response.StatusCode = 200; + // get the response + try + { + cumulus.LogMessage("Updating select-a-period settings"); + + var data = new StreamReader(context.Request.InputStream).ReadToEnd(); + + var json = WebUtility.UrlDecode(data); + + // de-serialize it to the settings structure + var settings = JsonSerializer.DeserializeFromString(json); + + // process the settings + try + { + cumulus.SelectaPeriodOptions.series = settings.series; + cumulus.SelectaPeriodOptions.colours = settings.colours; + } + catch (Exception ex) + { + var msg = "Error select-a-period Options: " + ex.Message; + cumulus.LogErrorMessage(msg); + errorMsg += msg + "\n\n"; + context.Response.StatusCode = 500; + } + + // Save the settings + cumulus.WriteIniFile(); + } + catch (Exception ex) + { + cumulus.LogErrorMessage("Update selecaperiod options error: " + ex.Message); + context.Response.StatusCode = 500; + return ex.Message; + } + + return context.Response.StatusCode == 200 ? "success" : errorMsg; + } + + internal string GetWSport() { return "{\"wsport\":\"" + cumulus.wsPort + "\"}"; @@ -1613,10 +1665,10 @@ internal class JsonStationSettingsEcowitt public string gwaddr { get; set; } public string localaddr { get; set; } public int interval { get; set; } - public List forward { get; set; } + public List forward { get; set; } } - internal class JsonEcowittForward + public class JsonEcowittForwardList { public string url { get; set; } } @@ -1643,6 +1695,12 @@ 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/CumulusMX/SunriseSunset.cs b/CumulusMX/SunriseSunset.cs index 15a9bfb3..d665127f 100644 --- a/CumulusMX/SunriseSunset.cs +++ b/CumulusMX/SunriseSunset.cs @@ -95,9 +95,9 @@ private static double ModifiedJulianDay(int year, int month, int day) year--; } - b = (int)Math.Floor(year / 400.0) - (int)Math.Floor(year / 100.0) + (int)Math.Floor(year / 4.0); + b = (int) Math.Floor(year / 400.0) - (int) Math.Floor(year / 100.0) + (int) Math.Floor(year / 4.0); a = 365.0 * year - 679004.0; - return a + b + (int)Math.Floor(30.6001 * (month + 1)) + day; + return a + b + (int) Math.Floor(30.6001 * (month + 1)) + day; } private static double Frac(double x) @@ -106,7 +106,7 @@ private static double Frac(double x) // returns the fractional part of x as used in minimoon and minisun // double a; - a = x - (int)Math.Floor(x); + a = x - (int) Math.Floor(x); return a; } @@ -120,7 +120,7 @@ private static double Range(double x) double a; double b; b = x / 360; - a = 360 * (b - (int)Math.Floor(b)); + a = 360 * (b - (int) Math.Floor(b)); if (a < 0) { a += 360; @@ -338,10 +338,10 @@ private static string SunEvent(int year, int month, int day, double tz, double g sett = 1; } } // end of nz = 1 case - // - // case where two events are found in this interval - // (rare but whole reason we are not using simple iteration) - // + // + // case where two events are found in this interval + // (rare but whole reason we are not using simple iteration) + // if (nz == 2) { if (ye < 0.0) @@ -364,12 +364,12 @@ private static string SunEvent(int year, int month, int day, double tz, double g hour += 2.0; } // end of while loop - // - // now search has completed, we compile the string to pass back - // to the user. The string depends on several combinations - // of the above flag (always above or always below) and the rise - // and sett flags - // + // + // now search has completed, we compile the string to pass back + // to the user. The string depends on several combinations + // of the above flag (always above or always below) and the rise + // and sett flags + // if (rise == 1 || sett == 1) { if (rise == 1) diff --git a/CumulusMX/TempestStation.cs b/CumulusMX/TempestStation.cs index fb9a5e98..a8e97acc 100644 --- a/CumulusMX/TempestStation.cs +++ b/CumulusMX/TempestStation.cs @@ -7,9 +7,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; + using CumulusMX.Tempest; + using ServiceStack.Text; +#pragma warning disable IDE0025 + namespace CumulusMX { internal class TempestStation : WeatherStation @@ -56,7 +60,7 @@ public override void getAndProcessHistoryData() } catch (Exception ex) { - cumulus.LogMessage("Exception occurred reading archive data: " + ex.Message); + cumulus.LogErrorMessage("Exception occurred reading archive data: " + ex.Message); if (ex.InnerException != null) { ex = Utils.GetOriginalException(ex); @@ -125,7 +129,7 @@ private void ProcessHistoryData(List datalist) // Pressure ============================================================= var alt = AltitudeM(cumulus.Altitude); - var seaLevel = MeteoLib.GetSeaLevelPressure(alt, (double) historydata.StationPressure, (double)historydata.Temperature); + var seaLevel = MeteoLib.GetSeaLevelPressure(alt, (double) historydata.StationPressure, (double) historydata.Temperature); DoPressure(ConvertPressMBToUser(seaLevel), timestamp); // Outdoor Humidity ===================================================== @@ -192,7 +196,7 @@ private void ProcessHistoryData(List datalist) // add in 'following interval' minutes worth of wind speed to windrun cumulus.LogMessage("Windrun: " + WindAverage.ToString(cumulus.WindFormat) + cumulus.Units.WindText + " for " + historydata.ReportInterval + " minutes = " + - (WindAverage*WindRunHourMult[cumulus.Units.Wind]*historydata.ReportInterval/60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); + (WindAverage * WindRunHourMult[cumulus.Units.Wind] * historydata.ReportInterval / 60.0).ToString(cumulus.WindRunFormat) + cumulus.Units.WindRunText); WindRunToday += WindAverage * WindRunHourMult[cumulus.Units.Wind] * historydata.ReportInterval / 60.0; @@ -234,7 +238,7 @@ private void ProcessHistoryData(List datalist) } ticks = Environment.TickCount - ticks; - var rate = ((double)totalentries / ticks) * 1000; + var rate = ((double) totalentries / ticks) * 1000; cumulus.LogMessage($"End processing history data. Rate: {rate:f2}/second"); cumulus.LogConsoleMessage($"Completed processing history data. {DateTime.Now.ToLongTimeString()}, Rate: {rate:f2}/second"); @@ -284,7 +288,7 @@ private void WeatherPacketReceived(WeatherPacket wp) ts = wp.Observation.Timestamp; var userTemp = ConvertTempCToUser(Convert.ToDouble(wp.Observation.Temperature)); - DoOutdoorTemp(userTemp,ts); + DoOutdoorTemp(userTemp, ts); DoWind(ConvertWindMSToUser((double) wp.Observation.WindGust), wp.Observation.WindDirection, ConvertWindMSToUser((double) wp.Observation.WindAverage), @@ -310,7 +314,7 @@ private void WeatherPacketReceived(WeatherPacket wp) cumulus.LogDebugMessage( $"TempestDoRain: Total Precip for Day: {Raincounter}"); - DoOutdoorHumidity((int)wp.Observation.Humidity,ts); + DoOutdoorHumidity((int) wp.Observation.Humidity, ts); OutdoorDewpoint = ConvertTempCToUser(MeteoLib.DewPoint(ConvertUserTempToC(OutdoorTemperature), @@ -320,7 +324,7 @@ private void WeatherPacketReceived(WeatherPacket wp) DoApparentTemp(ts); DoFeelsLike(ts); - DoWindChill(userTemp,ts); + DoWindChill(userTemp, ts); DoHumidex(ts); DoCloudBaseHeatIndex(ts); @@ -440,7 +444,7 @@ public static class StationListener public static void Start(Cumulus c) { - cumulus=c; + cumulus = c; Task.Run(StartUdpListen); } @@ -498,7 +502,7 @@ private static void _udpListener_PacketReceived(object sender, PacketReceivedArg public const string REST_URL = "https://swd.weatherflow.com/swd/rest/observations/"; - public static List GetRestPacket(string url, string token,int deviceId, DateTime start, DateTime end, Cumulus c) + public static List GetRestPacket(string url, string token, int deviceId, DateTime start, DateTime end, Cumulus c) { List ret = new List(); cumulus = c; @@ -509,7 +513,7 @@ public static List GetRestPacket(string url, string token,int devic var tpEnd = end; double ts = tpEnd.Subtract(tpStart).TotalDays; - while (ts > 0) + while (ts > 0) { long st; long end_time; @@ -518,7 +522,7 @@ public static List GetRestPacket(string url, string token,int devic if (ts > 4)// load max 4 days at a time { tpStart = tpStart.AddDays(4); - end_time = WeatherPacket.ToUnixTimeSeconds(tpStart)-1;// subtract a second so we don't overlap + end_time = WeatherPacket.ToUnixTimeSeconds(tpStart) - 1;// subtract a second so we don't overlap ts = tpEnd.Subtract(tpStart).TotalDays; } else @@ -549,7 +553,7 @@ public static List GetRestPacket(string url, string token,int devic else { var msg = $"Error downloading tempest history: {apiResponse}"; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogConsoleMessage(msg, ConsoleColor.Red); if (rp.status.status_code == 404) { @@ -699,14 +703,14 @@ public static DateTime FromUnixTimeSeconds(long epoch) { // Unix timestamp is seconds past epoch System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc); - dtDateTime = dtDateTime.AddSeconds( epoch ).ToLocalTime(); + dtDateTime = dtDateTime.AddSeconds(epoch).ToLocalTime(); return dtDateTime; } public static long ToUnixTimeSeconds(DateTime dt) { TimeSpan t = dt.ToUniversalTime() - new DateTime(1970, 1, 1); - return (long)t.TotalSeconds; + return (long) t.TotalSeconds; } public static decimal GetDecimal(decimal? d) @@ -764,7 +768,7 @@ public DeviceStatus(WeatherPacket packet) if (!int.TryParse(packet.firmware_revision.ToString(), out var i)) i = -1; FirmwareRevision = i; } - catch {} + catch { } RSSI = packet.rssi; HubRSSI = packet.hub_rssi; @@ -873,7 +877,7 @@ public Observation(WeatherPacket packet) if (!int.TryParse(packet.firmware_revision.ToString(), out i)) i = -1; FirmwareRevision = i; } - catch {} + catch { } if (packet.obs[0].Length >= 18) { @@ -884,7 +888,7 @@ public Observation(WeatherPacket packet) public Observation(List obs) { - LoadObservation(this,obs.ToArray()); + LoadObservation(this, obs.ToArray()); } private static void LoadObservation(Observation o, decimal?[] ob) diff --git a/CumulusMX/ThirdPartySettings.cs b/CumulusMX/ThirdPartySettings.cs index c248912b..89df56e2 100644 --- a/CumulusMX/ThirdPartySettings.cs +++ b/CumulusMX/ThirdPartySettings.cs @@ -1,9 +1,11 @@ -using ServiceStack; -using System; +using System; using System.IO; using System.Net; + using EmbedIO; -using static Swan.Terminal; + +using ServiceStack; + namespace CumulusMX { @@ -36,7 +38,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing 3rdParty Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("3rdParty Data: " + json); context.Response.StatusCode = 500; return msg; @@ -77,7 +79,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing wunderground settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -99,7 +101,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Windy settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -130,7 +132,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing AWEKAS settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -156,7 +158,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing WeatherCloud settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -178,7 +180,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing PWS weather settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -202,7 +204,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing WOW settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -224,7 +226,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing CWOP settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -244,7 +246,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing OpenWeatherMap settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -264,7 +266,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing WindGuru settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -326,7 +328,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Custom settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -340,7 +342,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Third Party settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Third Party data: " + json); errorMsg += msg; context.Response.StatusCode = 500; diff --git a/CumulusMX/TokenParser.cs b/CumulusMX/TokenParser.cs index b1777ba1..acd384f1 100644 --- a/CumulusMX/TokenParser.cs +++ b/CumulusMX/TokenParser.cs @@ -40,7 +40,7 @@ public string AltResultNoParseList _AltList = value; AltTags = value.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries).Select(p => p.Trim()).ToList(); } - + get { return _AltList; @@ -55,7 +55,7 @@ public string AltResultNoParseList public delegate void TokenHandler(string strToken, ref string strReplacement); public event TokenHandler OnToken; - + public TokenParser(TokenHandler tokenHandler) { OnToken = tokenHandler; @@ -238,7 +238,7 @@ private string Parse3() if (len == 0) { - Program.cumulus.LogMessage($"TokenParser error in file: {SourceFile}, InputString is zero length"); + Program.cumulus.LogWarningMessage($"TokenParser error in file: {SourceFile}, InputString is zero length"); return $"TokenParser error in file: {SourceFile}, InputString is zero length"; } @@ -272,7 +272,7 @@ private string Parse3() } catch (Exception e) { - Program.cumulus.LogMessage($"Web tag error in file: {SourceFile}"); + Program.cumulus.LogWarningMessage($"Web tag error in file: {SourceFile}"); Program.cumulus.LogMessage($"token={match.Value}"); Program.cumulus.LogMessage($"Position in file (character)={match.Index}"); Program.cumulus.LogMessage($"Exception: i={i} len={len}"); diff --git a/CumulusMX/Trig.cs b/CumulusMX/Trig.cs index 23cc9fdd..d0d8477c 100644 --- a/CumulusMX/Trig.cs +++ b/CumulusMX/Trig.cs @@ -2,174 +2,174 @@ namespace CumulusMX { - public class Trig - { - public Trig() - { - // - // TODO: Add constructor logic here - // - } - - public static double DegToRad(double pfDeg) - { - return pfDeg / 180 * Math.PI; - } - - public static double RadToDeg(double pfRad) - { - return pfRad * 180 / Math.PI; - } - - public static double Cos(double pfDeg) - { - return Math.Cos(DegToRad(pfDeg)); - } - - public static double Sin(double pfDeg) - { - return Math.Sin(DegToRad(pfDeg)); - } - - public static double Tan(double pfDeg) - { - return Math.Tan(DegToRad(pfDeg)); - } - - public static double Cosec(double pfDeg) - { - return (1.0 / Math.Sin(DegToRad(pfDeg))); - } - - public static double Sec(double pfDeg) - { - return (1.0 / Math.Cos(DegToRad(pfDeg))); - } - - public static double Cot(double pfDeg) - { - return (1.0 / Math.Tan(DegToRad(pfDeg))); - } - - public static double Acos(double pfNum) - { - return RadToDeg(Math.Acos(pfNum)); - } - - public static double Asin(double pfNum) - { - return RadToDeg(Math.Asin(pfNum)); - } - - public static double Atan(double pfNum) - { - return RadToDeg(Math.Atan(pfNum)); - } - - public static double Cosh(double pfDeg) - { - return Math.Cosh(DegToRad(pfDeg)); - } - - public static double Sinh(double pfDeg) - { - return Math.Sinh(DegToRad(pfDeg)); - } - - public static double Tanh(double pfDeg) - { - return Math.Tanh(DegToRad(pfDeg)); - } - - public static double Cosech(double pfDeg) - { - return (1.0 / Math.Sinh(DegToRad(pfDeg))); - } - - public static double Sech(double pfDeg) - { - return (1.0 / Math.Cosh(DegToRad(pfDeg))); - } - - public static double Coth(double pfDeg) - { - return (1.0 / Math.Tanh(DegToRad(pfDeg))); - } - - public static double PutIn360Deg(double pfDeg) - { - while (pfDeg >= 360) - { - pfDeg -= 360; - } - while (pfDeg < 0) - { - pfDeg += 360; - } - return pfDeg; - } - - public static double PutIn24Hour(double pfHour) - { - while (pfHour >= 24) - { - pfHour -= 24; - } - while (pfHour < 0) - { - pfHour += 24; - } - return pfHour; - } - - public static double TanQuadrant(double pfX, double pfY, double pfTanVal) - { - if ((pfY >= 0) && (pfX >= 0)) - { - while (pfTanVal >= 90) - { - pfTanVal -= 90; - } - while (pfTanVal < 0) - { - pfTanVal += 90; - } - } - else if ((pfY < 0) && (pfX >= 0)) - { - while (pfTanVal >= 360) - { - pfTanVal -= 90; - } - while (pfTanVal < 270) - { - pfTanVal += 90; - } - } - else if ((pfY >= 0) && (pfX < 0)) - { - while (pfTanVal >= 180) - { - pfTanVal -= 90; - } - while (pfTanVal < 90) - { - pfTanVal += 90; - } - } - else if ((pfY < 0) && (pfX < 0)) - { - while (pfTanVal >= 270) - { - pfTanVal -= 90; - } - while (pfTanVal < 180) - { - pfTanVal += 90; - } - } - return pfTanVal; - } - - } + public class Trig + { + public Trig() + { + // + // TODO: Add constructor logic here + // + } + + public static double DegToRad(double pfDeg) + { + return pfDeg / 180 * Math.PI; + } + + public static double RadToDeg(double pfRad) + { + return pfRad * 180 / Math.PI; + } + + public static double Cos(double pfDeg) + { + return Math.Cos(DegToRad(pfDeg)); + } + + public static double Sin(double pfDeg) + { + return Math.Sin(DegToRad(pfDeg)); + } + + public static double Tan(double pfDeg) + { + return Math.Tan(DegToRad(pfDeg)); + } + + public static double Cosec(double pfDeg) + { + return (1.0 / Math.Sin(DegToRad(pfDeg))); + } + + public static double Sec(double pfDeg) + { + return (1.0 / Math.Cos(DegToRad(pfDeg))); + } + + public static double Cot(double pfDeg) + { + return (1.0 / Math.Tan(DegToRad(pfDeg))); + } + + public static double Acos(double pfNum) + { + return RadToDeg(Math.Acos(pfNum)); + } + + public static double Asin(double pfNum) + { + return RadToDeg(Math.Asin(pfNum)); + } + + public static double Atan(double pfNum) + { + return RadToDeg(Math.Atan(pfNum)); + } + + public static double Cosh(double pfDeg) + { + return Math.Cosh(DegToRad(pfDeg)); + } + + public static double Sinh(double pfDeg) + { + return Math.Sinh(DegToRad(pfDeg)); + } + + public static double Tanh(double pfDeg) + { + return Math.Tanh(DegToRad(pfDeg)); + } + + public static double Cosech(double pfDeg) + { + return (1.0 / Math.Sinh(DegToRad(pfDeg))); + } + + public static double Sech(double pfDeg) + { + return (1.0 / Math.Cosh(DegToRad(pfDeg))); + } + + public static double Coth(double pfDeg) + { + return (1.0 / Math.Tanh(DegToRad(pfDeg))); + } + + public static double PutIn360Deg(double pfDeg) + { + while (pfDeg >= 360) + { + pfDeg -= 360; + } + while (pfDeg < 0) + { + pfDeg += 360; + } + return pfDeg; + } + + public static double PutIn24Hour(double pfHour) + { + while (pfHour >= 24) + { + pfHour -= 24; + } + while (pfHour < 0) + { + pfHour += 24; + } + return pfHour; + } + + public static double TanQuadrant(double pfX, double pfY, double pfTanVal) + { + if ((pfY >= 0) && (pfX >= 0)) + { + while (pfTanVal >= 90) + { + pfTanVal -= 90; + } + while (pfTanVal < 0) + { + pfTanVal += 90; + } + } + else if ((pfY < 0) && (pfX >= 0)) + { + while (pfTanVal >= 360) + { + pfTanVal -= 90; + } + while (pfTanVal < 270) + { + pfTanVal += 90; + } + } + else if ((pfY >= 0) && (pfX < 0)) + { + while (pfTanVal >= 180) + { + pfTanVal -= 90; + } + while (pfTanVal < 90) + { + pfTanVal += 90; + } + } + else if ((pfY < 0) && (pfX < 0)) + { + while (pfTanVal >= 270) + { + pfTanVal -= 90; + } + while (pfTanVal < 180) + { + pfTanVal += 90; + } + } + return pfTanVal; + } + + } } diff --git a/CumulusMX/Utils.cs b/CumulusMX/Utils.cs index 1fbf16af..d60874ab 100644 --- a/CumulusMX/Utils.cs +++ b/CumulusMX/Utils.cs @@ -1,21 +1,18 @@ using System; +using System.IO; using System.Linq; -using System.Text.RegularExpressions; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using Swan; -using Renci.SshNet.Messages; using System.Security.Cryptography; -using ServiceStack; -using System.IO; using System.Text; -using static SQLite.SQLite3; +using System.Text.RegularExpressions; using System.Threading.Tasks; -using System.Collections.Generic; -using ServiceStack.Text; -using System.Threading; -using System.Web.UI.WebControls; + +using ServiceStack; + +using Swan; + // A rag tag of useful functions @@ -62,7 +59,7 @@ public static DateTime RoundTimeUpToInterval(DateTime dateTime, TimeSpan intvl) public static string ByteArrayToHexString(byte[] ba) { - System.Text.StringBuilder hex = new System.Text.StringBuilder(ba.Length * 2); + System.Text.StringBuilder hex = new StringBuilder(ba.Length * 2); foreach (byte b in ba) hex.AppendFormat("{0:x2}", b); return hex.ToString(); @@ -71,7 +68,7 @@ public static string ByteArrayToHexString(byte[] ba) public static string GetMd5String(byte[] bytes) { - using (var md5 = System.Security.Cryptography.MD5.Create()) + using (var md5 = MD5.Create()) { var hashBytes = md5.ComputeHash(bytes); return ByteArrayToHexString(hashBytes); @@ -193,7 +190,7 @@ public static IPAddress GetIpWithDefaultGateway() .Select(g => g.Address) .First(); } - catch {} + catch { } try { // next just return the first IPv4 address found @@ -206,7 +203,7 @@ public static IPAddress GetIpWithDefaultGateway() } } } - catch {} + catch { } // finally, give up and just return a 0.0.0.0 IP! return IPAddress.Any; diff --git a/CumulusMX/WM918Station.cs b/CumulusMX/WM918Station.cs index 95a20069..f6672171 100644 --- a/CumulusMX/WM918Station.cs +++ b/CumulusMX/WM918Station.cs @@ -8,11 +8,11 @@ namespace CumulusMX { class WM918Station : WeatherStation { - private const int WM918HumidData = 0x8F; - private const int WM918TempData = 0x9F; - private const int WM918BaroData = 0xAF; - private const int WM918RainData = 0xBF; - private const int WM918WindData = 0xCF; + private const int WM918HumidData = 0x8F; + private const int WM918TempData = 0x9F; + private const int WM918BaroData = 0xAF; + private const int WM918RainData = 0xBF; + private const int WM918WindData = 0xCF; private readonly int[] WM918PacketLength = { 0, 0, 0, 0, 0, 0, 0, 0, 35, 34, 31, 14, 27, 0, 0, 0, 255 }; @@ -186,11 +186,11 @@ public override void startReadingHistoryData() } catch (Exception ex) { - cumulus.LogMessage(ex.Message); - //MessageBox.Show(ex.Message); + cumulus.LogErrorMessage("Error starting station: " + ex.Message); } } + public override void Stop() { stop = true; @@ -211,7 +211,7 @@ private bool WM918valid(List s, out int csum) if (s.Count < 14) { - cumulus.LogMessage("WM918 packet too short. Length = " + s.Count); + cumulus.LogWarningMessage("WM918 packet too short. Length = " + s.Count); result = false; } else @@ -220,7 +220,7 @@ private bool WM918valid(List s, out int csum) if (csum != s[s.Count - 1]) { - cumulus.LogMessage("Invalid checksum. Expected " + csum + ", got " + s[s.Count - 1]); + cumulus.LogErrorMessage("Invalid checksum. Expected " + csum + ", got " + s[s.Count - 1]); result = false; } else @@ -290,11 +290,11 @@ private void WM918Wind(List buff) // Wind Chill W1W2 (WS bit 1 gives sign) // Checksum C1C2 - double current = ConvertWindMSToUser((double)(BCDchartoint(buff[1]) + ((BCDchartoint(buff[2]) % 10) * 100)) / 10); - double average = ConvertWindMSToUser((double)(BCDchartoint(buff[4]) + ((BCDchartoint(buff[5]) % 10) * 100)) / 10); + double current = ConvertWindMSToUser((double) (BCDchartoint(buff[1]) + ((BCDchartoint(buff[2]) % 10) * 100)) / 10); + double average = ConvertWindMSToUser((double) (BCDchartoint(buff[4]) + ((BCDchartoint(buff[5]) % 10) * 100)) / 10); int bearing = BCDchartoint(buff[2]) / 10 + (BCDchartoint(buff[3]) * 10); - DoWind(current, bearing ,average, DateTime.Now); + DoWind(current, bearing, average, DateTime.Now); // Extract wind chill int wc = BCDchartoint(buff[16]); @@ -303,7 +303,7 @@ private void WM918Wind(List buff) if (wc > -70) { - DoWindChill(ConvertTempCToUser(wc),DateTime.Now); + DoWindChill(ConvertTempCToUser(wc), DateTime.Now); } } @@ -319,31 +319,35 @@ private void WM918Baro(List buff) // Sea-Level pressure S1S2S3S4.SD // Checksum C1C2 - DoOutdoorDewpoint(ConvertTempCToUser(BCDchartoint(buff[18])),DateTime.Now); + DoOutdoorDewpoint(ConvertTempCToUser(BCDchartoint(buff[18])), DateTime.Now); - double locPress = BCDchartoint(buff[1]) + (BCDchartoint(buff[2])*100); + double locPress = BCDchartoint(buff[1]) + (BCDchartoint(buff[2]) * 100); StationPressure = ConvertPressMBToUser(locPress); - double pressure = ConvertPressMBToUser((BCDchartoint(buff[3]) / 10) + (BCDchartoint(buff[4]) * 10) + + double pressure = ConvertPressMBToUser((BCDchartoint(buff[3]) / 10) + (BCDchartoint(buff[4]) * 10) + ((BCDchartoint(buff[5]) % 10) * 1000)); - DoPressure(pressure,DateTime.Now); + DoPressure(pressure, DateTime.Now); UpdatePressureTrendString(); - string forecast=String.Empty; + string forecast = String.Empty; // Forecast int num = buff[6] & 0xF; switch (num) { - case 1: forecast = "Sunny"; + case 1: + forecast = "Sunny"; break; - case 2: forecast = "Cloudy"; + case 2: + forecast = "Cloudy"; break; - case 4: forecast = "Partly Cloudy"; + case 4: + forecast = "Partly Cloudy"; break; - case 8: forecast = "Rain"; + case 8: + forecast = "Rain"; break; } @@ -360,7 +364,7 @@ private void WM918Temp(List buff) // Outdoor temp double temp10 = BCDchartoint(buff[16]) + (((buff[17]) & 0x7) * 100); - if ((buff[17] & 0x08) == 8) temp10 = -temp10; + if ((buff[17] & 0x08) == 8) temp10 = -temp10; if (temp10 > -500) { @@ -370,7 +374,7 @@ private void WM918Temp(List buff) // Indoor temp temp10 = BCDchartoint(buff[1]) + (((buff[2]) & 0x7) * 100); - if ((buff[2] & 0x08) == 8) temp10 = -temp10; + if ((buff[2] & 0x08) == 8) temp10 = -temp10; DoIndoorTemp(ConvertTempCToUser(temp10 / 10)); @@ -396,7 +400,7 @@ private void WM918Rain(List buff) double raincounter = ConvertRainMMToUser(BCDchartoint(buff[5]) + (BCDchartoint(buff[6]) * 100)); double rainrate = ConvertRainMMToUser(BCDchartoint(buff[1]) + ((BCDchartoint(buff[2]) % 10) * 100)); - DoRain(raincounter,rainrate,DateTime.Now); + DoRain(raincounter, rainrate, DateTime.Now); } private void WM918Humid(List buff) @@ -407,7 +411,7 @@ private void WM918Humid(List buff) DoIndoorHumidity(BCDchartoint(buff[8])); - DoOutdoorHumidity(BCDchartoint(buff[20]),DateTime.Now); + DoOutdoorHumidity(BCDchartoint(buff[20]), DateTime.Now); } } } diff --git a/CumulusMX/WMR100Station.cs b/CumulusMX/WMR100Station.cs index 7b4ee86c..ea65070d 100644 --- a/CumulusMX/WMR100Station.cs +++ b/CumulusMX/WMR100Station.cs @@ -1,6 +1,6 @@ using System; -using System.IO.Ports; using System.Threading; + using HidSharp; namespace CumulusMX @@ -88,7 +88,7 @@ public override void Start() { while (!stop) { - cumulus.LogDebugMessage("Calling Read, current packet length = "+currentPacketLength); + cumulus.LogDebugMessage("Calling Read, current packet length = " + currentPacketLength); try { @@ -143,7 +143,7 @@ public override void Start() if (currentPacketLength == 4) { currentPacketType = c; - cumulus.LogDebugMessage("Current packet type: "+currentPacketType.ToString("X2")); + cumulus.LogDebugMessage("Current packet type: " + currentPacketType.ToString("X2")); } if (currentPacketLength - 2 == WMR100PacketLength(currentPacketType)) { @@ -275,7 +275,7 @@ private void ProcessWMR100Packet() ProcessPondPacket(); break; default: - cumulus.LogMessage("Unknown packet type: " + currentPacketType.ToString("X2")); + cumulus.LogWarningMessage("Unknown packet type: " + currentPacketType.ToString("X2")); return; } @@ -308,7 +308,7 @@ private void ProcessPondPacket() else sign = 1; - double num = (sign*((packetBuffer[4] & 0xF)*256 + packetBuffer[3]))/10.0; + double num = (sign * ((packetBuffer[4] & 0xF) * 256 + packetBuffer[3])) / 10.0; WMR200ExtraTempValues[sensor] = ConvertTempCToUser(num); DoExtraTemp(WMR200ExtraTempValues[sensor], sensor); @@ -349,9 +349,9 @@ private void ProcessRainPacket() RainBattStatus = packetBuffer[0] & 0x4; - double counter = ((packetBuffer[9]*256) + packetBuffer[8])/100.0; + double counter = ((packetBuffer[9] * 256) + packetBuffer[8]) / 100.0; - double rate = ((packetBuffer[3]*256) + packetBuffer[2])/100.0; + double rate = ((packetBuffer[3] * 256) + packetBuffer[2]) / 100.0; // check for overflow (9999 mm = approx 393 in) and set to 999 mm/hr if (rate > 393) @@ -379,11 +379,11 @@ private void ProcessWindPacket() double wc; // bearing - double b = (packetBuffer[2] & 0xF)*22.5; + double b = (packetBuffer[2] & 0xF) * 22.5; // gust - double g = ((packetBuffer[5] & 0xF)*256 + packetBuffer[4])/10.0; + double g = ((packetBuffer[5] & 0xF) * 256 + packetBuffer[4]) / 10.0; // average - double a = ((packetBuffer[6]*16) + (packetBuffer[5]/16))/10.0; + double a = ((packetBuffer[6] * 16) + (packetBuffer[5] / 16)) / 10.0; DoWind(ConvertWindMSToUser(g), (int) (b), ConvertWindMSToUser(a), now); @@ -403,7 +403,7 @@ private void ProcessWindPacket() else { // wind chill is in Fahrenheit! - wc = (packetBuffer[7] + (packetBuffer[8] & 0xF)*256)/10.0; + wc = (packetBuffer[7] + (packetBuffer[8] & 0xF) * 256) / 10.0; if ((packetBuffer[8] & 0x80) == 0x80) // wind chill negative @@ -411,7 +411,7 @@ private void ProcessWindPacket() if ((cumulus.Units.Rain == 0)) // convert to C - wc = (wc - 32)/1.8; + wc = (wc - 32) / 1.8; DoWindChill(wc, now); } @@ -448,7 +448,7 @@ private void ProcessTempPacket() else sign = 1; - num = (sign*((packetBuffer[4] & 0xF)*256 + packetBuffer[3]))/10.0; + num = (sign * ((packetBuffer[4] & 0xF) * 256 + packetBuffer[3])) / 10.0; DoOutdoorTemp(ConvertTempCToUser(num), Now); // outdoor dewpoint @@ -457,7 +457,7 @@ private void ProcessTempPacket() else sign = 1; - num = (sign*((packetBuffer[7] & 0xF)*256 + packetBuffer[6]))/10.0; + num = (sign * ((packetBuffer[7] & 0xF) * 256 + packetBuffer[6])) / 10.0; DoOutdoorDewpoint(ConvertTempCToUser(num), Now); DoApparentTemp(Now); @@ -483,7 +483,7 @@ private void ProcessTempPacket() else sign = 1; - num = (sign*((packetBuffer[4] & 0xF)*256 + packetBuffer[3]))/10.0; + num = (sign * ((packetBuffer[4] & 0xF) * 256 + packetBuffer[3])) / 10.0; DoIndoorTemp(ConvertTempCToUser(num)); } @@ -501,7 +501,7 @@ private void ProcessTempPacket() else sign = 1; - num = (sign*((packetBuffer[4] & 0xF)*256 + packetBuffer[3]))/10.0; + num = (sign * ((packetBuffer[4] & 0xF) * 256 + packetBuffer[3])) / 10.0; WMR200ExtraTempValues[sensor] = ConvertTempCToUser(num); DoExtraTemp(WMR200ExtraTempValues[sensor], sensor); @@ -512,7 +512,7 @@ private void ProcessTempPacket() else sign = 1; - num = (sign*((packetBuffer[7] & 0xF)*256 + packetBuffer[6]))/10.0; + num = (sign * ((packetBuffer[7] & 0xF) * 256 + packetBuffer[6])) / 10.0; WMR200ExtraDPValues[sensor] = ConvertTempCToUser(num); DoExtraDP(WMR200ExtraDPValues[sensor], sensor); ExtraSensorsDetected = true; @@ -522,11 +522,11 @@ private void ProcessTempPacket() private void ProcessBaroPacket() { cumulus.LogDebugMessage("Barometer packet"); - double num = ((packetBuffer[5] & 0xF)*256) + packetBuffer[4]; + double num = ((packetBuffer[5] & 0xF) * 256) + packetBuffer[4]; double slp = ConvertPressMBToUser(num); - num = ((packetBuffer[3] & 0xF)*256) + packetBuffer[2]; + num = ((packetBuffer[3] & 0xF) * 256) + packetBuffer[2]; StationPressure = ConvertPressMBToUser(num); @@ -534,7 +534,7 @@ private void ProcessBaroPacket() UpdatePressureTrendString(); - int forecast = packetBuffer[3]/16; + int forecast = packetBuffer[3] / 16; string fcstr; switch (forecast) @@ -583,7 +583,7 @@ private Boolean CRCOK() else { // packet CRC is in last two bytes, low byte then high byte - var packetCRC = (packetBuffer[packetLen - 1]*256) + packetBuffer[packetLen - 2]; + var packetCRC = (packetBuffer[packetLen - 1] * 256) + packetBuffer[packetLen - 2]; var calculatedCRC = 0; @@ -594,7 +594,7 @@ private Boolean CRCOK() } cumulus.LogDebugMessage("Packet CRC = " + packetCRC); - cumulus.LogDebugMessage("Calculated CRC = "+calculatedCRC); + cumulus.LogDebugMessage("Calculated CRC = " + calculatedCRC); return (packetCRC == calculatedCRC); } @@ -621,7 +621,7 @@ private void SendReset() } catch (Exception ex) { - cumulus.LogMessage($"SendReset: Error - {ex.Message}"); + cumulus.LogErrorMessage($"SendReset: Error - {ex.Message}"); } } diff --git a/CumulusMX/WMR200Station.cs b/CumulusMX/WMR200Station.cs index be0eeed9..49efa755 100644 --- a/CumulusMX/WMR200Station.cs +++ b/CumulusMX/WMR200Station.cs @@ -1,10 +1,11 @@ -using HidSharp; -using System; +using System; using System.ComponentModel; -using System.IO.Ports; using System.Text; using System.Threading; using System.Timers; + +using HidSharp; + using Timer = System.Timers.Timer; namespace CumulusMX @@ -77,7 +78,7 @@ public WMR200Station(Cumulus cumulus) : base(cumulus) } else { - cumulus.LogMessage("WMR200 station not found!"); + cumulus.LogErrorMessage("WMR200 station not found!"); cumulus.LogConsoleMessage("WMR200 station not found!", ConsoleColor.Red); } } @@ -305,7 +306,7 @@ private void SendReset() } else { - reset = new byte[] {0x00, 0x20, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00}; + reset = new byte[] { 0x00, 0x20, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00 }; } stream.Write(reset); @@ -358,7 +359,7 @@ private Boolean CRCOK() else { // packet CRC is in last two bytes, low byte then high byte - var packetCRC = (packetBuffer[packetLen - 1]*256) + packetBuffer[packetLen - 2]; + var packetCRC = (packetBuffer[packetLen - 1] * 256) + packetBuffer[packetLen - 2]; var calculatedCRC = 0; @@ -564,7 +565,7 @@ private void ProcessTempHumPacket() if (sensor == cumulus.WMR200TempChannel) { // outdoor hum - DoOutdoorHumidity(packetBuffer[10],now); + DoOutdoorHumidity(packetBuffer[10], now); // outdoor temp if ((packetBuffer[9] & 0x80) == 0x80) { @@ -575,8 +576,8 @@ private void ProcessTempHumPacket() sign = 1; } - num = sign*((packetBuffer[9] & 0xF)*256 + packetBuffer[8])/10.0; - DoOutdoorTemp(ConvertTempCToUser(num),now); + num = sign * ((packetBuffer[9] & 0xF) * 256 + packetBuffer[8]) / 10.0; + DoOutdoorTemp(ConvertTempCToUser(num), now); // outdoor dewpoint if ((packetBuffer[12] & 0x80) == 0x80) { @@ -586,7 +587,7 @@ private void ProcessTempHumPacket() { sign = 1; } - num = sign*((packetBuffer[12] & 0xF)*256 + packetBuffer[11])/10.0; + num = sign * ((packetBuffer[12] & 0xF) * 256 + packetBuffer[11]) / 10.0; DoOutdoorDewpoint(ConvertTempCToUser(num), now); DoApparentTemp(now); @@ -607,7 +608,7 @@ private void ProcessTempHumPacket() { sign = 1; } - num = (sign*((packetBuffer[9] & 0xF)*256 + packetBuffer[8]))/10.0; + num = (sign * ((packetBuffer[9] & 0xF) * 256 + packetBuffer[8])) / 10.0; DoIndoorTemp(ConvertTempCToUser(num)); } if ((sensor > 1) && (sensor < 11)) @@ -615,7 +616,7 @@ private void ProcessTempHumPacket() WMR200ChannelPresent[sensor] = true; // outdoor hum WMR200ExtraHumValues[sensor] = packetBuffer[10]; - DoExtraHum(WMR200ExtraHumValues[sensor],sensor); + DoExtraHum(WMR200ExtraHumValues[sensor], sensor); // outdoor temp if ((packetBuffer[9] & 0x80) == 0x80) { @@ -639,7 +640,7 @@ private void ProcessTempHumPacket() } WMR200ExtraDPValues[sensor] = ConvertTempCToUser((sign * ((packetBuffer[12] & 0xF) * 256 + packetBuffer[11])) / 10.0); - DoExtraDP(WMR200ExtraDPValues[sensor],sensor); + DoExtraDP(WMR200ExtraDPValues[sensor], sensor); ExtraSensorsDetected = true; } } @@ -718,9 +719,9 @@ private void ProcessRainPacket() //Byte 20: (cL) Check-sum low byte //Byte 21: (cH) Check-sum high byte - double counter = (((packetBuffer[14]*256) + packetBuffer[13])/100.0); + double counter = (((packetBuffer[14] * 256) + packetBuffer[13]) / 100.0); - var rate = ((packetBuffer[8]*256) + packetBuffer[7])/100.0; + var rate = ((packetBuffer[8] * 256) + packetBuffer[7]) / 100.0; // check for overflow (9999 mm = approx 393 in) and set to 999 mm/hr if (rate > 393) @@ -740,7 +741,7 @@ private double ConvertWMR200Rain(double value) if (cumulus.Units.Rain == 0) { // mm - num = value*25.4; + num = value * 25.4; } else { @@ -850,11 +851,11 @@ private void ProcessWindPacket() DateTime now = DateTime.Now; // bearing - int bearing = (int) ((packetBuffer[7] & 0xF)*22.5); + int bearing = (int) ((packetBuffer[7] & 0xF) * 22.5); // gust - double gust = ((packetBuffer[10] & 0xF)*256 + packetBuffer[9])/10.0; + double gust = ((packetBuffer[10] & 0xF) * 256 + packetBuffer[9]) / 10.0; // average - double average = ((packetBuffer[11]*16) + (packetBuffer[10]/16))/10.0; + double average = ((packetBuffer[11] * 16) + (packetBuffer[10] / 16)) / 10.0; DoWind(ConvertWindMSToUser(gust), bearing, ConvertWindMSToUser(average), now); @@ -865,12 +866,12 @@ 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(OutdoorTemperature, now); } else { // wind chill is in Fahrenheit! - var wc = (packetBuffer[12] + (packetBuffer[13] & 0xF)*256)/10.0; + var wc = (packetBuffer[12] + (packetBuffer[13] & 0xF) * 256) / 10.0; if ((packetBuffer[13] & 0x80) == 0x80) wc = -wc; @@ -881,7 +882,7 @@ private void ProcessWindPacket() wc = MeteoLib.FtoC(wc); } - DoWindChill(wc,now); + DoWindChill(wc, now); } } @@ -945,14 +946,14 @@ private void ProcessBaroPacket() //Byte 11: (cL) Check-sum low byte //Byte 12: (cH) Check-sum high byte - double slp = ((packetBuffer[10] & 0xF)*256) + packetBuffer[9]; - DoPressure(ConvertPressMBToUser(slp),DateTime.Now); + double slp = ((packetBuffer[10] & 0xF) * 256) + packetBuffer[9]; + DoPressure(ConvertPressMBToUser(slp), DateTime.Now); - StationPressure = ConvertPressMBToUser(((packetBuffer[8] & 0xF)*256) + packetBuffer[7]); + StationPressure = ConvertPressMBToUser(((packetBuffer[8] & 0xF) * 256) + packetBuffer[7]); UpdatePressureTrendString(); - var forecast = packetBuffer[8]/16; + var forecast = packetBuffer[8] / 16; string fcstr; switch (forecast) @@ -983,7 +984,7 @@ private void ProcessBaroPacket() break; } - DoForecast(fcstr,false); + DoForecast(fcstr, false); } private void ProcessUVPacket() @@ -1034,7 +1035,7 @@ private void ProcessUVPacket() if (num > 16) num = 16; - DoUV(num,DateTime.Now); + DoUV(num, DateTime.Now); // UV value is stored as channel 1 of the extra sensors WMR200ExtraHumValues[1] = num; @@ -1433,21 +1434,21 @@ private void ProcessHistoryDataPacket() } previousHistoryTimeStamp = timestamp; // pressure - StationPressure = ConvertPressMBToUser(((packetBuffer[29] & 0xF)*256) + packetBuffer[28]); - double num = ((packetBuffer[31] & 0xF)*256) + packetBuffer[30]; - DoPressure(ConvertPressMBToUser(num),timestamp); + StationPressure = ConvertPressMBToUser(((packetBuffer[29] & 0xF) * 256) + packetBuffer[28]); + double num = ((packetBuffer[31] & 0xF) * 256) + packetBuffer[30]; + DoPressure(ConvertPressMBToUser(num), timestamp); // bearing - int bearing = (int) ((packetBuffer[20] & 0xF)*22.5); + int bearing = (int) ((packetBuffer[20] & 0xF) * 22.5); // gust - double gust = ((packetBuffer[23] & 0xF)*256 + packetBuffer[22])/10.0; + double gust = ((packetBuffer[23] & 0xF) * 256 + packetBuffer[22]) / 10.0; // average - double average = ((packetBuffer[24]*16) + (packetBuffer[23]/16))/10.0; + double average = ((packetBuffer[24] * 16) + (packetBuffer[23] / 16)) / 10.0; - DoWind(ConvertWindMSToUser(gust), bearing,ConvertWindMSToUser(average), timestamp); + DoWind(ConvertWindMSToUser(gust), bearing, ConvertWindMSToUser(average), timestamp); // add in 'interval' minutes worth of wind speed to windrun - WindRunToday += (WindAverage*WindRunHourMult[cumulus.Units.Wind]*interval*60)/1000.0; + WindRunToday += (WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval * 60) / 1000.0; // update dominant wind bearing CalculateDominantWindBearing(Bearing, WindAverage, interval); int sensorcount = packetBuffer[32]; @@ -1455,7 +1456,7 @@ private void ProcessHistoryDataPacket() { try { - var offset = 33 + (i*7); + var offset = 33 + (i * 7); var sensornumber = packetBuffer[offset] & 0xF; int sign; if (sensornumber == 0) @@ -1472,13 +1473,13 @@ private void ProcessHistoryDataPacket() { sign = 1; } - DoIndoorTemp(ConvertTempCToUser(sign*((packetBuffer[offset + 2] & 0xF)*256 + packetBuffer[offset + 1]))/10.0); + DoIndoorTemp(ConvertTempCToUser(sign * ((packetBuffer[offset + 2] & 0xF) * 256 + packetBuffer[offset + 1])) / 10.0); } else if (sensornumber == cumulus.WMR200TempChannel) { // channel 1 outdoor sensor // outdoor humidity - DoOutdoorHumidity(packetBuffer[offset + 3],timestamp); + DoOutdoorHumidity(packetBuffer[offset + 3], timestamp); // outdoor temp if ((packetBuffer[offset + 2] & 0x80) == 0x80) { @@ -1488,14 +1489,14 @@ private void ProcessHistoryDataPacket() { sign = 1; } - DoOutdoorTemp(ConvertTempCToUser((sign*((packetBuffer[offset + 2] & 0xF)*256 + packetBuffer[offset + 1]))/10.0),timestamp); + DoOutdoorTemp(ConvertTempCToUser((sign * ((packetBuffer[offset + 2] & 0xF) * 256 + packetBuffer[offset + 1])) / 10.0), timestamp); // update heating/cooling degree days UpdateDegreeDays(interval); // add in 'archivePeriod' minutes worth of temperature to the temp samples tempsamplestoday += interval; - TempTotalToday += (OutdoorTemperature*interval); + TempTotalToday += (OutdoorTemperature * interval); // update chill hours if (OutdoorTemperature < cumulus.ChillHourThreshold) @@ -1514,14 +1515,14 @@ private void ProcessHistoryDataPacket() sign = 1; } - DoOutdoorDewpoint(ConvertTempCToUser((sign*((packetBuffer[offset + 5] & 0xF)*256 + packetBuffer[offset + 4]))/10.0),timestamp); + DoOutdoorDewpoint(ConvertTempCToUser((sign * ((packetBuffer[offset + 5] & 0xF) * 256 + packetBuffer[offset + 4])) / 10.0), timestamp); } if (sensornumber > 1 && sensornumber < 11) { // extra sensors // humidity - DoExtraHum(packetBuffer[offset + 3],sensornumber); + DoExtraHum(packetBuffer[offset + 3], sensornumber); // temperature if ((packetBuffer[offset + 2] & 0x80) == 0x80) @@ -1533,7 +1534,7 @@ private void ProcessHistoryDataPacket() sign = 1; } - DoExtraTemp(ConvertTempCToUser((sign*((packetBuffer[offset + 2] & 0xF)*256 + packetBuffer[offset + 1]))/10.0),sensornumber); + DoExtraTemp(ConvertTempCToUser((sign * ((packetBuffer[offset + 2] & 0xF) * 256 + packetBuffer[offset + 1])) / 10.0), sensornumber); // dew point if ((packetBuffer[offset + 5] & 0x80) == 0x80) @@ -1545,12 +1546,12 @@ private void ProcessHistoryDataPacket() sign = 1; } - DoExtraDP(ConvertTempCToUser((sign*((packetBuffer[offset + 5] & 0xF)*256 + packetBuffer[offset + 4]))/10.0),sensornumber); + DoExtraDP(ConvertTempCToUser((sign * ((packetBuffer[offset + 5] & 0xF) * 256 + packetBuffer[offset + 4])) / 10.0), sensornumber); } } catch (Exception ex) { - cumulus.LogMessage("History packet too short. Sensor count = " + sensorcount); + cumulus.LogErrorMessage("History packet too short. Sensor count = " + sensorcount); cumulus.LogMessage(ex.Message); } } @@ -1563,33 +1564,33 @@ private void ProcessHistoryDataPacket() // bother calling anyway if (TempReadyToPlot) { - DoWindChill(OutdoorTemperature,timestamp); + DoWindChill(OutdoorTemperature, timestamp); } } else { // wind chill is in Fahrenheit! - var wc = MeteoLib.FtoC((packetBuffer[25] + (packetBuffer[26] & 0xF)*256)/10.0); + var wc = MeteoLib.FtoC((packetBuffer[25] + (packetBuffer[26] & 0xF) * 256) / 10.0); if (cumulus.Units.Rain == 0) { wc = MeteoLib.FtoC(wc); } - DoWindChill(wc,timestamp); + DoWindChill(wc, timestamp); } // rain - var counter = ((packetBuffer[14]*256) + packetBuffer[13])/100.0; + var counter = ((packetBuffer[14] * 256) + packetBuffer[13]) / 100.0; - var rate = ((packetBuffer[8]*256) + packetBuffer[7])/100.0; + var rate = ((packetBuffer[8] * 256) + packetBuffer[7]) / 100.0; - DoRain(ConvertRainINToUser(counter),ConvertRainINToUser(rate),timestamp); + DoRain(ConvertRainINToUser(counter), ConvertRainINToUser(rate), timestamp); // UV if (packetBuffer[27] != 0xFF) { - DoUV(packetBuffer[27] & 0xFF,timestamp); + DoUV(packetBuffer[27] & 0xFF, timestamp); } // do solar rad, even though there's no sensor, @@ -1601,7 +1602,7 @@ private void ProcessHistoryDataPacket() DoHumidex(timestamp); DoCloudBaseHeatIndex(timestamp); - cumulus.DoLogFile(timestamp,false); + cumulus.DoLogFile(timestamp, false); cumulus.MySqlRealtimeFile(999, false, timestamp); cumulus.DoCustomIntervalLogs(timestamp); @@ -1736,7 +1737,7 @@ private void ProcessWMR200Packet() } else { - cumulus.LogMessage("WMR200: Invalid CRC"); + cumulus.LogErrorMessage("WMR200: Invalid CRC"); } if (gettingHistory) diff --git a/CumulusMX/WMR928Station.cs b/CumulusMX/WMR928Station.cs index c47ff9e9..69d0bf5e 100644 --- a/CumulusMX/WMR928Station.cs +++ b/CumulusMX/WMR928Station.cs @@ -23,7 +23,7 @@ internal class WMR928Station : WeatherStation private bool stop; - private readonly int[] WMR928PacketLength = {11, 16, 9, 9, 7, 13, 14, 0, 0, 0, 0, 0, 0, 0, 5, 9, 255}; + private readonly int[] WMR928PacketLength = { 11, 16, 9, 9, 7, 13, 14, 0, 0, 0, 0, 0, 0, 0, 5, 9, 255 }; public WMR928Station(Cumulus cumulus) : base(cumulus) { @@ -44,7 +44,7 @@ public override void startReadingHistoryData() try { - comport = new SerialPort(cumulus.ComportName, 9600, Parity.None, 8, StopBits.One) {Handshake = Handshake.None, RtsEnable = true, DtrEnable = true}; + comport = new SerialPort(cumulus.ComportName, 9600, Parity.None, 8, StopBits.One) { Handshake = Handshake.None, RtsEnable = true, DtrEnable = true }; comport.Open(); cumulus.NormalRunning = true; @@ -56,7 +56,7 @@ public override void startReadingHistoryData() } catch (Exception ex) { - cumulus.LogMessage($"Error opening com port [{cumulus.ComportName}]: {ex.Message}"); + cumulus.LogErrorMessage($"Error opening com port [{cumulus.ComportName}]: {ex.Message}"); cumulus.LogConsoleMessage($"Error opening com port [{cumulus.ComportName}]: {ex.Message}"); } } @@ -293,7 +293,7 @@ private void WMR928ExtraTempOnly(List buff) if ((channel > 3) || (channel < 1)) { - cumulus.LogMessage("WMR928 channel error, ch=" + channel); + cumulus.LogErrorMessage("WMR928 channel error, ch=" + channel); channel = 1; } @@ -309,7 +309,7 @@ private void WMR928ExtraTempOnly(List buff) if (cumulus.WMR928TempChannel == channel) { // use this sensor as main temp sensor - TempBattStatus = buff[3]/16; + TempBattStatus = buff[3] / 16; DoOutdoorTemp(WMR928ExtraTempValues[channel], DateTime.Now); @@ -358,7 +358,7 @@ private void WMR928ExtraOutdoor(List buff) if ((channel > 3) || (channel < 1)) { - cumulus.LogMessage("WMR928 channel error, ch=" + channel); + cumulus.LogErrorMessage("WMR928 channel error, ch=" + channel); channel = 1; } @@ -380,7 +380,7 @@ private void WMR928ExtraOutdoor(List buff) if (cumulus.WMR928TempChannel == channel) { // use this sensor as main temp and hum sensor - TempBattStatus = buff[3]/16; + TempBattStatus = buff[3] / 16; // Extract humidity DoOutdoorHumidity(BCDchartoint(buff[6]), DateTime.Now); @@ -403,18 +403,18 @@ private void WMR928Wind(List buff) // Wind Chill W1W2 (WS gives sign) // Checksum C1C2 - WindBattStatus = buff[3]/16; + WindBattStatus = buff[3] / 16; - double current = (double) (BCDchartoint(buff[5])/10 + (BCDchartoint(buff[6])*10))/10; - double average = (double) (BCDchartoint(buff[7]) + ((BCDchartoint(buff[8])%10)*100))/10; - int bearing = BCDchartoint(buff[4]) + ((BCDchartoint(buff[5])%10)*100); + double current = (double) (BCDchartoint(buff[5]) / 10 + (BCDchartoint(buff[6]) * 10)) / 10; + double average = (double) (BCDchartoint(buff[7]) + ((BCDchartoint(buff[8]) % 10) * 100)) / 10; + int bearing = BCDchartoint(buff[4]) + ((BCDchartoint(buff[5]) % 10) * 100); DoWind(ConvertWindMSToUser(current), bearing, ConvertWindMSToUser(average), DateTime.Now); // Extract wind chill double wc = BCDchartoint(buff[9]); - if (buff[9]/16 == 8) wc = -wc; + if (buff[9] / 16 == 8) wc = -wc; DoWindChill(ConvertTempCToUser(wc), DateTime.Now); } @@ -436,11 +436,11 @@ private void WMR928Rain(List buff) // Checksum C1C2 // the unknown byte has values such as 0x80 or 0x90 - RainBattStatus = buff[3]/16; + RainBattStatus = buff[3] / 16; // MainForm.Rainbatt.Position := (15-rain_batt_status)*100 DIV 15; - double raincounter = ConvertRainMMToUser(BCDchartoint(buff[6]) + (BCDchartoint(buff[7])*100)); - double rainrate = ConvertRainMMToUser(BCDchartoint(buff[4]) + ((BCDchartoint(buff[5])%10)*100)); + double raincounter = ConvertRainMMToUser(BCDchartoint(buff[6]) + (BCDchartoint(buff[7]) * 100)); + double rainrate = ConvertRainMMToUser(BCDchartoint(buff[4]) + ((BCDchartoint(buff[5]) % 10) * 100)); DoRain(raincounter, rainrate, DateTime.Now); } @@ -459,7 +459,7 @@ private void WMR928Outdoor(List buff) if (cumulus.WMR928TempChannel == 0) { - TempBattStatus = buff[3]/16; + TempBattStatus = buff[3] / 16; // Extract humidity int hum = BCDchartoint(buff[6]); @@ -495,12 +495,12 @@ private void WMR928Indoor(List buff) // Checksum C1C2 // the unknown byte seems to be fixed at 00 - IndoorBattStatus = buff[3]/16; + IndoorBattStatus = buff[3] / 16; // Extract temp (tenths of deg C) in BCD; bytes 4 (LSB) and 5 (MSB) - double temp10 = BCDchartoint(buff[4]) + (BCDchartoint(buff[5])*100); + double temp10 = BCDchartoint(buff[4]) + (BCDchartoint(buff[5]) * 100); - DoIndoorTemp(temp10/10); + DoIndoorTemp(temp10 / 10); // humidity in BCD; byte 6 DoIndoorHumidity(BCDchartoint(buff[6])); @@ -508,8 +508,8 @@ private void WMR928Indoor(List buff) // local pressure (not BCD); byte 8, with 856mb offset double loc = buff[8] + 856; StationPressure = ConvertPressMBToUser(loc); - double num = BCDchartoint((buff[10])/10) + BCDchartoint(buff[11])*10 + (BCDchartoint(buff[12])*1000); - double slcorr = num/10.0 - 600; + double num = BCDchartoint((buff[10]) / 10) + BCDchartoint(buff[11]) * 10 + (BCDchartoint(buff[12]) * 1000); + double slcorr = num / 10.0 - 600; DoPressure(ConvertPressMBToUser(loc + slcorr), DateTime.Now); @@ -518,7 +518,7 @@ private void WMR928Indoor(List buff) string forecast = String.Empty; // forecast - top 4 bits of byte 9 - int fcnum = buff[9]/16; + int fcnum = buff[9] / 16; switch (fcnum) { case 2: @@ -553,7 +553,7 @@ private void WMR928Indoor2(List buff) // Checksum C1C2 // the unknown byte seems to be fixed at 00 - IndoorBattStatus = buff[3]/16; + IndoorBattStatus = buff[3] / 16; //MainForm.Indoorbatt.Position := (15-indoor_batt_status)*100 DIV 15; // Extract temp (tenths of deg C) in BCD; @@ -568,7 +568,7 @@ private void WMR928Indoor2(List buff) double loc = buff[8] + 795; StationPressure = ConvertPressMBToUser(loc); // SL pressure correction; bytes 10 (LSB) and 11 (MSB) - double num = BCDchartoint(buff[10]/10) + (BCDchartoint(buff[11])*10) + buff[8]; + double num = BCDchartoint(buff[10] / 10) + (BCDchartoint(buff[11]) * 10) + buff[8]; DoPressure(num, DateTime.Now); UpdatePressureTrendString(); diff --git a/CumulusMX/WS2300Station.cs b/CumulusMX/WS2300Station.cs index ecef0705..c4cb3efe 100644 --- a/CumulusMX/WS2300Station.cs +++ b/CumulusMX/WS2300Station.cs @@ -51,7 +51,7 @@ public WS2300Station(Cumulus cumulus) : base(cumulus) } catch (Exception ex) { - cumulus.LogMessage(ex.Message); + cumulus.LogErrorMessage("error opening COM port: " + ex.Message); //MessageBox.Show(ex.Message); } @@ -176,7 +176,7 @@ public override void getAndProcessHistoryData() histData.outTemp = outtemp; histData.pressure = press; histData.rainTotal = raincount; - histData.windBearing = (int)bearing; + histData.windBearing = (int) bearing; histData.windGust = windspeed; histData.windSpeed = windspeed; histData.dewpoint = dewpoint; @@ -562,7 +562,7 @@ private int Ws2300ReadHistoryRecord(int record, out int address, out double temp pressure = ConvertPressMBToUser(pressure); - humindoor = (int)((tempint - (tempint % 10000)) / 10000.0); + humindoor = (int) ((tempint - (tempint % 10000)) / 10000.0); humoutdoor = (data[5] >> 4) * 10 + (data[5] & 0xF); @@ -612,7 +612,7 @@ private void GetAndProcessData() } // Outdoor humidity ==================================================================== - if(!stop) + if (!stop) { int outhum = Ws2300OutdoorHumidity(); if ((outhum > 0) && (outhum <= 100) && ((previoushum == 999) || (Math.Abs(outhum - previoushum) < cumulus.Spike.HumidityDiff))) @@ -667,7 +667,7 @@ private void GetAndProcessData() if ((Pressure > 850) && (Pressure < 1200)) { - StationPressure = ConvertPressMBToUser(pressure * pressure + cumulus.Calib.Press.Mult2 + pressure * cumulus.Calib.Press.Mult + ConvertPressMBToUser(cumulus.Calib.Press.Offset)); + StationPressure = ConvertPressMBToUser(cumulus.Calib.Press.Calibrate(pressure)); // AltimeterPressure := ConvertOregonPress(StationToAltimeter(PressureHPa(StationPressure),AltitudeM(Altitude))); } } @@ -690,7 +690,7 @@ private void GetAndProcessData() if ((wind > -1) && ((previouswind == 999) || (Math.Abs(wind - previouswind) < cumulus.Spike.WindDiff))) { previouswind = wind; - DoWind(ConvertWindMSToUser(wind), (int)direction, -1, now); + DoWind(ConvertWindMSToUser(wind), (int) direction, -1, now); } else { @@ -1294,7 +1294,7 @@ private int Ws2300ReadData(int address, int numberofbytes, byte[] readData, byte /// private byte ws2300commandChecksum4(int number) { - return (byte)(number + 0x30); + return (byte) (number + 0x30); } /// @@ -1312,7 +1312,7 @@ private byte dataChecksum(byte[] data, int numberofbytes) checksum += data[i]; } - return (byte)(checksum & 0xFF); + return (byte) (checksum & 0xFF); } /// @@ -1324,7 +1324,7 @@ private byte ws2300encodeNumberOfBytes(int number) { byte encodednumber; - encodednumber = (byte)(0xC2 + number * 4); + encodednumber = (byte) (0xC2 + number * 4); if (encodednumber > 0xfe) encodednumber = 0xfe; @@ -1340,7 +1340,7 @@ private byte ws2300encodeNumberOfBytes(int number) /// private byte ws2300commandChecksum0to3(byte command, int sequence) { - return (byte)(sequence * 16 + ((command) - 0x82) / 4); + return (byte) (sequence * 16 + ((command) - 0x82) / 4); } /* @@ -1371,8 +1371,8 @@ private void ws2300EncodeAddress(int addressIn, byte[] addressOut) for (int i = 0; i < numbytes; i++) { - byte nibble = (byte)((addressIn >> (4 * (3 - i))) & 0x0F); - addressOut[i] = (byte)(0x82 + (nibble * 4)); + byte nibble = (byte) ((addressIn >> (4 * (3 - i))) & 0x0F); + addressOut[i] = (byte) (0x82 + (nibble * 4)); } } @@ -1428,7 +1428,7 @@ private int Ws2300ReadSerial(out byte answer) cumulus.LogDataMessage("ReadSerial"); try { - answer = (byte)comport.ReadByte(); + answer = (byte) comport.ReadByte(); } catch (Exception ex) { diff --git a/CumulusMX/WeatherData.cs b/CumulusMX/WeatherData.cs index bd2ae3de..898126f3 100644 --- a/CumulusMX/WeatherData.cs +++ b/CumulusMX/WeatherData.cs @@ -3,75 +3,75 @@ namespace CumulusMX { - internal class WeatherData - { - public DateTime DT { get; set; } + internal class WeatherData + { + public DateTime DT { get; set; } - public double WindSpeed - { - get => windspeed; - set - { - windspeed = value; - var handler = PropertyChanged; - handler?.Invoke(this, new PropertyChangedEventArgs("WindSpeed")); - } - } + public double WindSpeed + { + get => windspeed; + set + { + windspeed = value; + var handler = PropertyChanged; + handler?.Invoke(this, new PropertyChangedEventArgs("WindSpeed")); + } + } - private double windspeed; + private double windspeed; - public double WindAverage - { - get => windaverage; - set - { - windaverage = value; - var handler = PropertyChanged; - handler?.Invoke(this, new PropertyChangedEventArgs("WindAverage")); - } - } + public double WindAverage + { + get => windaverage; + set + { + windaverage = value; + var handler = PropertyChanged; + handler?.Invoke(this, new PropertyChangedEventArgs("WindAverage")); + } + } - private double windaverage; + private double windaverage; - public double OutdoorTemp - { - get => outdoortemp; - set - { - outdoortemp = value; - var handler = PropertyChanged; - handler?.Invoke(this, new PropertyChangedEventArgs("OutdoorTemp")); - } - } + public double OutdoorTemp + { + get => outdoortemp; + set + { + outdoortemp = value; + var handler = PropertyChanged; + handler?.Invoke(this, new PropertyChangedEventArgs("OutdoorTemp")); + } + } - private double outdoortemp; + private double outdoortemp; - public double Pressure - { - get => pressure; - set - { - pressure = value; - var handler = PropertyChanged; - handler?.Invoke(this, new PropertyChangedEventArgs("Pressure")); - } - } + public double Pressure + { + get => pressure; + set + { + pressure = value; + var handler = PropertyChanged; + handler?.Invoke(this, new PropertyChangedEventArgs("Pressure")); + } + } - private double pressure; + private double pressure; - public double Raintotal - { - get => raintotal; - set - { - raintotal = value; - var handler = PropertyChanged; - handler?.Invoke(this, new PropertyChangedEventArgs("Raintotal")); - } - } + public double Raintotal + { + get => raintotal; + set + { + raintotal = value; + var handler = PropertyChanged; + handler?.Invoke(this, new PropertyChangedEventArgs("Raintotal")); + } + } - private double raintotal; + private double raintotal; - public event PropertyChangedEventHandler PropertyChanged; - } + public event PropertyChangedEventHandler PropertyChanged; + } } diff --git a/CumulusMX/WeatherDataCollection.cs b/CumulusMX/WeatherDataCollection.cs index 45f7b91f..0f8c7774 100644 --- a/CumulusMX/WeatherDataCollection.cs +++ b/CumulusMX/WeatherDataCollection.cs @@ -2,12 +2,12 @@ namespace CumulusMX { - internal class WeatherDataCollection : ObservableCollection - { - public WeatherDataCollection() - { + internal class WeatherDataCollection : ObservableCollection + { + public WeatherDataCollection() + { - } + } - } + } } diff --git a/CumulusMX/WeatherLink.cs b/CumulusMX/WeatherLink.cs index bd4f0de3..c22ad6ad 100644 --- a/CumulusMX/WeatherLink.cs +++ b/CumulusMX/WeatherLink.cs @@ -3,906 +3,906 @@ namespace CumulusMX { - // The VPLoopData class extracts and stores the weather data from the array of bytes returned from the Vantage weather station - // The array is generated from the return of the LOOP command. - // - // Contents of the character array (LOOP packet from Vantage): - // - // Field Offset Size Explanation - // "L" 0 1 - // "O" 1 1 - // "O" 2 1 Spells out "LOO" for Rev B packets and "LOOP" for Rev A packets. Identifies a LOOP packet - // "P" (Rev A), Bar Trend (Rev B) 3 1 Signed byte that indicates the current 3-hour barometer trend. It is one of these values: - // -60 = Falling Rapidly = 196 (as an unsigned byte) - // -20 = Falling Slowly = 236 (as an unsigned byte) - // 0 = Steady - // 20 = Rising Slowly - // 60 = Rising Rapidly - // 80 = ASCII "P" = Rev A firmware, no trend info is available. - // Any other value means that the Vantage does not have the 3 hours of bar data needed - // to determine the bar trend. - // Packet Type 4 1 Has the value zero. LOOP2 packets are set to 1. - // Next Record 5 2 Location in the archive memory where the next data packet will be written. This can be - // monitored to detect when a new record is created. - // Pressure 7 2 Current Pressure. Units are (in Hg / 1000). The barometric value should be between 20 inches - // and 32.5 inches in Vantage Pro and between 20 inches and 32.5 inches in both Vantage Pro - // Vantage Pro2. Values outside these ranges will not be logged. - // Inside Temperature 9 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. - // Inside Humidity 11 1 This is the relative humidity in %, such as 50 is returned for 50%. - // Outside Temperature 12 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. - // Wind Speed 14 1 It is a byte unsigned value in mph. If the wind speed is dashed because it lost synchronization - // with the radio or due to some other reason, the wind speed is forced to be 0. - // 10 Min Avg Wind Speed 15 1 It is a byte unsigned value in mph. - // Wind Direction 16 2 It is a two byte unsigned value from 0 to 360 degrees. - // (0° is North, 90° is East, 180° is South and 270° is West.) - // Extra Temperatures 18 7 This field supports seven extra temperature stations. Each byte is one extra temperature value - // in whole degrees F with an offset of 90 degrees. For example, a value of 0 = -90°F ; - // a value of 100 = 10°F ; and a value of 169 = 79°F. - // Soil Temperatures 25 4 This field supports four soil temperature sensors, in the same format as the Extra Temperature - // field above - // Leaf Temperatures 29 4 This field supports four leaf temperature sensors, in the same format as the Extra Temperature - // field above - // Outside Humidity 33 1 This is the relative humidity in %. - // Extra Humidities 34 7 Relative humidity in % for extra seven humidity stations. - // Rain Rate 41 2 This value is sent as 100th of a inch per hour. For example, 256 represent 2.56 inches/hour. - // UV 43 1 The unit is in UV index. - // Solar Radiation 44 2 The unit is in watt/meter2. - // Storm Rain 46 2 The storm is stored as 100th of an inch. - // Start Date of current Storm 48 2 Bit 15 to bit 12 is the month, bit 11 to bit 7 is the day and bit 6 to bit 0 is the year offset - // by 2000. - // Day Rain 50 2 This value is sent as the 100th of an inch. - // Month Rain 52 2 This value is sent as the 100th of an inch. - // Year Rain 54 2 This value is sent as the 100th of an inch. - // Day ET 56 2 This value is sent as the 100th of an inch. - // Month ET 58 2 This value is sent as the 100th of an inch. - // Year ET 60 2 This value is sent as the 100th of an inch. - // Soil Moistures 62 4 The unit is in centibar. It supports four soil sensors. - // Leaf Wetnesses 66 4 This is a scale number from 0 to 15 with 0 meaning very dry and 15 meaning very wet. It supports - // four leaf sensors. - // Inside Alarms 70 1 Currently active inside alarms. See the table below - // Rain Alarms 71 1 Currently active rain alarms. See the table below - // Outside Alarms 72 2 Currently active outside alarms. See the table below - // Extra Temp/Hum Alarms 74 8 Currently active extra temp/hum alarms. See the table below - // Soil & Leaf Alarms 82 4 Currently active soil/leaf alarms. See the table below - // Transmitter Battery Status 86 1 - // Console Battery Voltage 87 2 Voltage = ((Data * 300)/512)/100.0 - // Forecast Icons 89 1 - // Forecast Rule number 90 1 - // Time of Sunrise 91 2 The time is stored as hour * 100 + min. - // Time of Sunset 93 2 The time is stored as hour * 100 + min. - // "\n" = 0x0A 95 1 - // "\r" = 0x0D 96 1 - // CRC 97 2 - // Total Length 99 - public class VPLoopData - { - public int PressureTrend { get; private set; } + // The VPLoopData class extracts and stores the weather data from the array of bytes returned from the Vantage weather station + // The array is generated from the return of the LOOP command. + // + // Contents of the character array (LOOP packet from Vantage): + // + // Field Offset Size Explanation + // "L" 0 1 + // "O" 1 1 + // "O" 2 1 Spells out "LOO" for Rev B packets and "LOOP" for Rev A packets. Identifies a LOOP packet + // "P" (Rev A), Bar Trend (Rev B) 3 1 Signed byte that indicates the current 3-hour barometer trend. It is one of these values: + // -60 = Falling Rapidly = 196 (as an unsigned byte) + // -20 = Falling Slowly = 236 (as an unsigned byte) + // 0 = Steady + // 20 = Rising Slowly + // 60 = Rising Rapidly + // 80 = ASCII "P" = Rev A firmware, no trend info is available. + // Any other value means that the Vantage does not have the 3 hours of bar data needed + // to determine the bar trend. + // Packet Type 4 1 Has the value zero. LOOP2 packets are set to 1. + // Next Record 5 2 Location in the archive memory where the next data packet will be written. This can be + // monitored to detect when a new record is created. + // Pressure 7 2 Current Pressure. Units are (in Hg / 1000). The barometric value should be between 20 inches + // and 32.5 inches in Vantage Pro and between 20 inches and 32.5 inches in both Vantage Pro + // Vantage Pro2. Values outside these ranges will not be logged. + // Inside Temperature 9 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. + // Inside Humidity 11 1 This is the relative humidity in %, such as 50 is returned for 50%. + // Outside Temperature 12 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. + // Wind Speed 14 1 It is a byte unsigned value in mph. If the wind speed is dashed because it lost synchronization + // with the radio or due to some other reason, the wind speed is forced to be 0. + // 10 Min Avg Wind Speed 15 1 It is a byte unsigned value in mph. + // Wind Direction 16 2 It is a two byte unsigned value from 0 to 360 degrees. + // (0° is North, 90° is East, 180° is South and 270° is West.) + // Extra Temperatures 18 7 This field supports seven extra temperature stations. Each byte is one extra temperature value + // in whole degrees F with an offset of 90 degrees. For example, a value of 0 = -90°F ; + // a value of 100 = 10°F ; and a value of 169 = 79°F. + // Soil Temperatures 25 4 This field supports four soil temperature sensors, in the same format as the Extra Temperature + // field above + // Leaf Temperatures 29 4 This field supports four leaf temperature sensors, in the same format as the Extra Temperature + // field above + // Outside Humidity 33 1 This is the relative humidity in %. + // Extra Humidities 34 7 Relative humidity in % for extra seven humidity stations. + // Rain Rate 41 2 This value is sent as 100th of a inch per hour. For example, 256 represent 2.56 inches/hour. + // UV 43 1 The unit is in UV index. + // Solar Radiation 44 2 The unit is in watt/meter2. + // Storm Rain 46 2 The storm is stored as 100th of an inch. + // Start Date of current Storm 48 2 Bit 15 to bit 12 is the month, bit 11 to bit 7 is the day and bit 6 to bit 0 is the year offset + // by 2000. + // Day Rain 50 2 This value is sent as the 100th of an inch. + // Month Rain 52 2 This value is sent as the 100th of an inch. + // Year Rain 54 2 This value is sent as the 100th of an inch. + // Day ET 56 2 This value is sent as the 100th of an inch. + // Month ET 58 2 This value is sent as the 100th of an inch. + // Year ET 60 2 This value is sent as the 100th of an inch. + // Soil Moistures 62 4 The unit is in centibar. It supports four soil sensors. + // Leaf Wetnesses 66 4 This is a scale number from 0 to 15 with 0 meaning very dry and 15 meaning very wet. It supports + // four leaf sensors. + // Inside Alarms 70 1 Currently active inside alarms. See the table below + // Rain Alarms 71 1 Currently active rain alarms. See the table below + // Outside Alarms 72 2 Currently active outside alarms. See the table below + // Extra Temp/Hum Alarms 74 8 Currently active extra temp/hum alarms. See the table below + // Soil & Leaf Alarms 82 4 Currently active soil/leaf alarms. See the table below + // Transmitter Battery Status 86 1 + // Console Battery Voltage 87 2 Voltage = ((Data * 300)/512)/100.0 + // Forecast Icons 89 1 + // Forecast Rule number 90 1 + // Time of Sunrise 91 2 The time is stored as hour * 100 + min. + // Time of Sunset 93 2 The time is stored as hour * 100 + min. + // "\n" = 0x0A 95 1 + // "\r" = 0x0D 96 1 + // CRC 97 2 + // Total Length 99 + public class VPLoopData + { + public int PressureTrend { get; private set; } - public int CurrentWindSpeed { get; private set; } + public int CurrentWindSpeed { get; private set; } - public int AvgWindSpeed { get; private set; } + public int AvgWindSpeed { get; private set; } - public int InsideHumidity { get; private set; } + public int InsideHumidity { get; private set; } - public int OutsideHumidity { get; private set; } + public int OutsideHumidity { get; private set; } - public double Pressure { get; private set; } + public double Pressure { get; private set; } - public double InsideTemperature { get; private set; } + public double InsideTemperature { get; private set; } - public double OutsideTemperature { get; private set; } + public double OutsideTemperature { get; private set; } - public double DailyRain { get; private set; } + public double DailyRain { get; private set; } - public int WindDirection { get; private set; } + public int WindDirection { get; private set; } - public DateTime SunRise { get; private set; } + public DateTime SunRise { get; private set; } - public DateTime SunSet { get; private set; } + public DateTime SunSet { get; private set; } - public double MonthRain { get; private set; } + public double MonthRain { get; private set; } - public double YearRain { get; private set; } + public double YearRain { get; private set; } - public double RainRate { get; private set; } + public double RainRate { get; private set; } - public double StormRain { get; private set; } + public double StormRain { get; private set; } - public DateTime StormRainStart { get; private set; } + public DateTime StormRainStart { get; private set; } - public int SoilMoisture1 { get; private set; } + public int SoilMoisture1 { get; private set; } - public int SoilMoisture2 { get; private set; } + public int SoilMoisture2 { get; private set; } - public int SoilMoisture3 { get; private set; } + public int SoilMoisture3 { get; private set; } - public int SoilMoisture4 { get; private set; } + public int SoilMoisture4 { get; private set; } - public int ExtraTemp1 { get; private set; } + public int ExtraTemp1 { get; private set; } - public int ExtraTemp2 { get; private set; } + public int ExtraTemp2 { get; private set; } - public int ExtraTemp3 { get; private set; } + public int ExtraTemp3 { get; private set; } - public int ExtraTemp4 { get; private set; } + public int ExtraTemp4 { get; private set; } - public int ExtraTemp5 { get; private set; } + public int ExtraTemp5 { get; private set; } - public int ExtraTemp6 { get; private set; } + public int ExtraTemp6 { get; private set; } - public int ExtraTemp7 { get; private set; } + public int ExtraTemp7 { get; private set; } - public int ExtraHum1 { get; private set; } + public int ExtraHum1 { get; private set; } - public int ExtraHum2 { get; private set; } + public int ExtraHum2 { get; private set; } - public int ExtraHum3 { get; private set; } + public int ExtraHum3 { get; private set; } - public int ExtraHum4 { get; private set; } + public int ExtraHum4 { get; private set; } - public int ExtraHum5 { get; private set; } + public int ExtraHum5 { get; private set; } - public int ExtraHum6 { get; private set; } + public int ExtraHum6 { get; private set; } - public int ExtraHum7 { get; private set; } + public int ExtraHum7 { get; private set; } - public int SoilTemp1 { get; private set; } + public int SoilTemp1 { get; private set; } - public int SoilTemp2 { get; private set; } + public int SoilTemp2 { get; private set; } - public int SoilTemp3 { get; private set; } + public int SoilTemp3 { get; private set; } - public int SoilTemp4 { get; private set; } + public int SoilTemp4 { get; private set; } - public double AnnualET { get; private set; } + public double AnnualET { get; private set; } - public double UVIndex { get; private set; } + public double UVIndex { get; private set; } - public int LeafWetness4 { get; private set; } + public int LeafWetness4 { get; private set; } - public int LeafWetness3 { get; private set; } + public int LeafWetness3 { get; private set; } - public int LeafWetness2 { get; private set; } + public int LeafWetness2 { get; private set; } - public int LeafWetness1 { get; private set; } + public int LeafWetness1 { get; private set; } - public int LeafTemp4 { get; private set; } + public int LeafTemp4 { get; private set; } - public int LeafTemp3 { get; private set; } + public int LeafTemp3 { get; private set; } - public int LeafTemp2 { get; private set; } + public int LeafTemp2 { get; private set; } - public int LeafTemp1 { get; private set; } + public int LeafTemp1 { get; private set; } - public int ForecastRule { get; private set; } + public int ForecastRule { get; private set; } - public int HiSolarRad { get; private set; } + public int HiSolarRad { get; private set; } - public int SolarRad { get; private set; } + public int SolarRad { get; private set; } - public byte TXbattStatus { get; private set; } + public byte TXbattStatus { get; private set; } - public double ConBatVoltage { get; private set; } + public double ConBatVoltage { get; private set; } - // Load - disassembles the byte array passed in and loads it into local data that the accessors can use. - // Actual data is in the format to the right of the assignments - I convert it to make it easier to use - // When bytes have to be assembled into 2-byte, 16-bit numbers, I convert two bytes from the array into - // an Int16 (16-bit integer). When a single byte is all that's needed, I just convert it to an Int32. - // In the end, all integers are cast to Int32 for return. - public void Load(byte[] byteArray) - { - PressureTrend = Convert.ToInt32((sbyte) byteArray[3]); // Sbyte - signed byte - Pressure = (double) (BitConverter.ToInt16(byteArray, 7))/1000; // Uint16 - InsideTemperature = (double) (BitConverter.ToInt16(byteArray, 9))/10; // Uint16 - InsideHumidity = Convert.ToInt32(byteArray[11]); // Byte - unsigned byte - OutsideTemperature = (double) (BitConverter.ToInt16(byteArray, 12))/10; // Uint16 - OutsideHumidity = Convert.ToInt32(byteArray[33]); // Byte - unsigned byte - WindDirection = BitConverter.ToInt16(byteArray, 16); // Uint16 - CurrentWindSpeed = Convert.ToInt32(byteArray[14]); // Byte - unsigned byte - AvgWindSpeed = Convert.ToInt32(byteArray[15]); // Byte - unsigned byte - DailyRain = (double) (BitConverter.ToInt16(byteArray, 50)); // Uint16 - MonthRain = (double) (BitConverter.ToInt16(byteArray, 52)); // Uint16 - YearRain = (double) (BitConverter.ToInt16(byteArray, 54)); // Uint16 - RainRate = (double) (BitConverter.ToInt16(byteArray, 41)); // Uint16 - StormRain = (double)(BitConverter.ToInt16(byteArray, 46)); - ForecastRule = Convert.ToInt32(byteArray[90]); - LeafTemp1 = Convert.ToInt32(byteArray[29]); - LeafTemp2 = Convert.ToInt32(byteArray[30]); - LeafTemp3 = Convert.ToInt32(byteArray[31]); - LeafTemp4 = Convert.ToInt32(byteArray[32]); - LeafWetness1 = Convert.ToInt32(byteArray[66]); - LeafWetness2 = Convert.ToInt32(byteArray[67]); - LeafWetness3 = Convert.ToInt32(byteArray[68]); - LeafWetness4 = Convert.ToInt32(byteArray[69]); - SoilTemp1 = Convert.ToInt32(byteArray[25]); - SoilTemp2 = Convert.ToInt32(byteArray[26]); - SoilTemp3 = Convert.ToInt32(byteArray[27]); - SoilTemp4 = Convert.ToInt32(byteArray[28]); - ExtraHum1 = Convert.ToInt32(byteArray[34]); - ExtraHum2 = Convert.ToInt32(byteArray[35]); - ExtraHum3 = Convert.ToInt32(byteArray[36]); - ExtraHum4 = Convert.ToInt32(byteArray[37]); - ExtraHum5 = Convert.ToInt32(byteArray[38]); - ExtraHum6 = Convert.ToInt32(byteArray[39]); - ExtraHum7 = Convert.ToInt32(byteArray[40]); - ExtraTemp1 = Convert.ToInt32(byteArray[18]); - ExtraTemp2 = Convert.ToInt32(byteArray[19]); - ExtraTemp3 = Convert.ToInt32(byteArray[20]); - ExtraTemp4 = Convert.ToInt32(byteArray[21]); - ExtraTemp5 = Convert.ToInt32(byteArray[22]); - ExtraTemp6 = Convert.ToInt32(byteArray[23]); - ExtraTemp7 = Convert.ToInt32(byteArray[24]); - SoilMoisture1 = Convert.ToInt32(byteArray[62]); - SoilMoisture2 = Convert.ToInt32(byteArray[63]); - SoilMoisture3 = Convert.ToInt32(byteArray[64]); - SoilMoisture4 = Convert.ToInt32(byteArray[65]); - UVIndex = (double) (Convert.ToInt32(byteArray[43]))/10; - SolarRad = BitConverter.ToInt16(byteArray, 44); - var dayET = (double)(BitConverter.ToInt16(byteArray, 56)) / 1000; - var yearET = (double)(BitConverter.ToInt16(byteArray,60)) / 100; - // It appears that the annual ET in the loop data does not include today - AnnualET = yearET + dayET; - TXbattStatus = byteArray[86]; - ConBatVoltage = ((BitConverter.ToInt16(byteArray, 87)*300)/512.0)/100.0; - - try - { - var stormRainStart = BitConverter.ToInt16(byteArray, 48); - var srMonth = (stormRainStart & 0xF000) >> 12; - var srDay = (stormRainStart & 0x0F80) >> 7; - var srYear = (stormRainStart & 0x007F) + 2000; - if (srMonth < 13 && srDay < 32) // Exception suppression! - { - StormRainStart = new DateTime(srYear, srMonth, srDay); - } - else - { - StormRainStart = DateTime.MinValue; - } - } - catch (Exception) - { - StormRainStart = DateTime.MinValue; - } - - // get the current date and time - // DateTime currTime = DateTime.Now; - - //int timevalue = BitConverter.ToInt16(byteArray, 91); - //int minutes; - //int hours = Math.DivRem(timevalue, 100, out minutes); - - // Create a new Datetime instance - use current year, month and day - //SunRise = new DateTime(currTime.Year, currTime.Month, currTime.Day, hours, minutes, 0); - - //timevalue = BitConverter.ToInt16(byteArray, 93); - //hours = Math.DivRem(timevalue, 100, out minutes); - //SunSet = new DateTime(currTime.Year, currTime.Month, currTime.Day, hours, minutes, 0); - } - - // This procedure displays the data that we've captured from the Vantage and processed already. - public string DebugString() - { - StringBuilder outputString = new StringBuilder(); - - // Format the string for output - outputString.Append("Pressure: " + Pressure.ToString("f2") + "in. " + PressureTrendText() + Environment.NewLine); - outputString.Append("Inside Temp: " + InsideTemperature.ToString() + Environment.NewLine); - outputString.Append("Inside Humidity: " + InsideHumidity.ToString() + "%" + Environment.NewLine); - outputString.Append("Outside Temp: " + OutsideTemperature.ToString() + Environment.NewLine); - outputString.Append("Outside Humidity: " + OutsideHumidity.ToString() + "%" + Environment.NewLine); - outputString.Append("Wind Direction: " + WindDirectionText() + " @ " + WindDirection.ToString() + " degrees" + Environment.NewLine); - outputString.Append("Current Wind Speed: " + CurrentWindSpeed.ToString() + "MPH" + Environment.NewLine); - outputString.Append("10 Minute Average Wind Speed: " + AvgWindSpeed.ToString() + "MPH" + Environment.NewLine); - outputString.Append("Daily Rain: " + DailyRain.ToString() + "in" + Environment.NewLine); - outputString.Append("Sunrise: " + SunRise.ToString("t") + Environment.NewLine); - outputString.Append("Sunset: " + SunSet.ToString("t") + Environment.NewLine); - - return (outputString.ToString()); - } - - private string WindDirectionText() - { - string windDirString; - - // The wind direction is given in degrees - 0-359 - convert to string representing the direction - if (WindDirection >= 337 || WindDirection <= 23) - windDirString = "N"; - else if (WindDirection > 292) - windDirString = "NW"; - else if (WindDirection >= 247) - windDirString = "W"; - else if (WindDirection > 203) - windDirString = "SW"; - else if (WindDirection >= 157) - windDirString = "S"; - else if (WindDirection > 113) - windDirString = "SE"; - else if (WindDirection >= 67) - windDirString = "E"; - else if (WindDirection > 23) - windDirString = "NE"; - else - windDirString = "??"; - - return windDirString; - } - - private string PressureTrendText() - { - // The barometric trend is in signed integer values. Convert these to something meaningful. - switch (PressureTrend) - { - case (-60): - return "Falling Rapidly"; - case (-20): - return "Falling Slowly"; - case (0): - return "Steady"; - case (20): - return "Rising Slowly"; - case (60): - return "Rising Rapidly"; - default: - return "??"; - } - } - - public VPLoopData() - { - PressureTrend = 0; - CurrentWindSpeed = 0; - AvgWindSpeed = 0; - InsideHumidity = 0; - OutsideHumidity = 0; - InsideTemperature = 0.0F; - OutsideTemperature = 0.0F; - DailyRain = 0.0F; - WindDirection = 0; - SunRise = DateTime.Now; - SunSet = DateTime.Now; - MonthRain = 0.0F; - YearRain = 0.0F; - RainRate = 0.0F; - } - } - - // The WeatherCalibrationData class extracts and stores the weather data from the array of bytes returned from the Vantage weather station - // The array is generated from the return of the CALED command. - // - // Field Offset Size Explanation - // Inside Temperature 0 2 - // Outside Temperature 2 2 - // Extra Temperature 4 14 (7 * 2) - // Soil Temperatures 18 8 (4 * 2) - // Leaf Temperatures 26 8 (4 * 2) - // Inside Humidity 34 1 - // Outside Humidity 35 1 - // Extra Humidities 36 7 - // Total Length 43 - public class WeatherCalibrationData - { - // Not yet implemented - } - - public class VPArchiveData - { - // Format of the archive records read from the VP data logger - // Field Offset Size Dash Value Explanation - // Date Stamp 0 2 N/A These 16 bits hold the date that the archive was - // written in the following format: - // Year (7 bits) | Month (4 bits) | Day (5 bits) or: - // day + month*32 + (year-2000)*512) - // Time Stamp 2 2 N/A Time on the Vantage that the archive record was written: - // (Hour * 100) + minute. - // Outside Temperature 4 2 32767 Either the Average Outside Temperature, or the - // Final Outside Temperature over the archive period. - // Units are (°F / 10) - // High Out Temperature 6 2 -32768 Highest Outside Temp over the archive period. - // Low Out Temperature 8 2 32767 Lowest Outside Temp over the archive period. - // Rainfall 10 2 0 Number of rain clicks over the archive period - // High Rain Rate 12 2 0 Highest rain rate over the archive period, or the rate - // shown on the console at the end of the period if there - // was no rain. Units are (rain clicks / hour) - // Barometer 14 2 0 Barometer reading at the end of the archive period. - // Units are (in Hg / 1000). - // Solar Radiation 16 2 32767 Average Solar Rad over the archive period. - // Units are (Watts / m2) - // Number of Wind Samples 18 2 0 Number of packets containing wind speed data - // received from the ISS or wireless anemometer. - // Inside Temperature 20 2 32767 Either the Average Inside Temperature, or the Final - // Inside Temperature over the archive period. - // Units are (°F / 10) - // Inside Humidity 22 1 255 Inside Humidity at the end of the archive period - // Outside Humidity 23 1 255 Outside Humidity at the end of the archive period - // Average Wind Speed 24 1 255 Average Wind Speed over the archive interval. - // Units are (MPH) - // High Wind Speed 25 1 0 Highest Wind Speed over the archive interval. - // Units are (MPH) - // Direction of Hi Wind Speed 26 1 32767 Direction code of the High Wind speed. - // 0=N, 1=NNE, 2=NE, … 14=NW, 15=NNW, 255=Dashed - // Prevailing Wind Direction 27 1 32767 Prevailing or Dominant Wind Direction code. - // 0=N, 1=NNE, 2=NE, … 14=NW, 15=NNW, 255=Dashed - // Average UV Index 28 1 255 Average UV Index. Units are (UV Index / 10) - // ET 29 1 0 ET accumulated over the last hour. Only records "on - // the hour" will have a non-zero value. - // Units are (in / 1000) - // High Solar Radiation 30 2 0 Highest Solar Rad value over the archive period. - // Units are (Watts / m2) - // High UV Index 32 1 0 Highest UV Index value over the archive period. - // Units are (Watts / m2) - // Forecast Rule 33 1 193 Weather forecast rule at the end of the archive period. - // Leaf Temperature 34 2 255 2 Leaf Temperature values. Units are (°F + 90) - // Leaf Wetnesses 36 2 255 2 Leaf Wetness values. Range is 0 – 15 - // Soil Temperatures 38 4 255 4 Soil Temperatures. Units are (°F + 90) - // Download Record Type 42 1 N/A 0xFF = Rev A, 0x00 = Rev B archive record - // Extra Humidities 43 2 255 2 Extra Humidity values - // Extra Temperatures 45 3 32767 3 Extra Temperature values. Units are (°F + 90) - // Soil Moistures 48 4 255 4 Soil Moisture values. Units are (cb) - - public int SoilMoisture1 { get; private set; } - - public int SoilMoisture2 { get; private set; } - - public int SoilMoisture3 { get; private set; } - - public int SoilMoisture4 { get; private set; } - - public int ExtraTemp1 { get; private set; } - - public int ExtraTemp2 { get; private set; } - - public int ExtraTemp3 { get; private set; } - - public int ExtraHum1 { get; private set; } - - public int ExtraHum2 { get; private set; } - - public int SoilTemp1 { get; private set; } - - public int SoilTemp2 { get; private set; } - - public int SoilTemp3 { get; private set; } - - public int SoilTemp4 { get; private set; } - - public double ET { get; private set; } - - public double HiUVIndex { get; private set; } - - public double AvgUVIndex { get; private set; } - - public int LeafWetness2 { get; private set; } - - public int LeafWetness1 { get; private set; } - - public int LeafTemp2 { get; private set; } - - public int LeafTemp1 { get; private set; } - - public int ForecastRule { get; private set; } - - public int HiSolarRad { get; private set; } - - public int SolarRad { get; private set; } - - public DateTime Timestamp { get; private set; } - - public double LoOutsideTemp { get; private set; } - - public double HiOutsideTemp { get; private set; } - - public double HiRainRate { get; private set; } - - public double Rainfall { get; private set; } - - public double OutsideTemperature { get; private set; } - - public double InsideTemperature { get; private set; } - - public double Pressure { get; private set; } - - public int WindDirection { get; private set; } - - public int HiWindDirection { get; private set; } - - public int OutsideHumidity { get; private set; } - - public int InsideHumidity { get; private set; } - - public int HiWindSpeed { get; private set; } - - public int AvgWindSpeed { get; private set; } - - public void Load(byte[] byteArray, out DateTime lastArchiveDate) - { - Pressure = (double) (BitConverter.ToInt16(byteArray, 14))/1000.0; // Uint16 - InsideTemperature = (double) (BitConverter.ToInt16(byteArray, 20))/10.0; // Uint16 - InsideHumidity = Convert.ToInt32(byteArray[22]); // Byte - unsigned byte - OutsideTemperature = (double) (BitConverter.ToInt16(byteArray, 4))/10.0; // Uint16 - HiOutsideTemp = (double) (BitConverter.ToInt16(byteArray, 6))/10.0; // Uint16 - LoOutsideTemp = (double) (BitConverter.ToInt16(byteArray, 8))/10.0; // Uint16 - OutsideHumidity = Convert.ToInt32(byteArray[23]); // Byte - unsigned byte - HiWindDirection = Convert.ToInt32(byteArray[26]); // Uint16 - WindDirection = Convert.ToInt32(byteArray[27]); // Uint16 - SolarRad = BitConverter.ToInt16(byteArray, 16); // Uint16 - HiSolarRad = BitConverter.ToInt16(byteArray, 30); // Uint16 - AvgWindSpeed = Convert.ToInt32(byteArray[24]); // Byte - unsigned byte - HiWindSpeed = Convert.ToInt32(byteArray[25]); // Byte - unsigned byte - Rainfall = (double) (BitConverter.ToInt16(byteArray, 10)); // Uint16 - HiRainRate = (double) (BitConverter.ToInt16(byteArray, 12)); // Uint16 - ForecastRule = Convert.ToInt32(byteArray[33]); - LeafTemp1 = Convert.ToInt32(byteArray[34]); - LeafTemp2 = Convert.ToInt32(byteArray[35]); - LeafWetness1 = Convert.ToInt32(byteArray[36]); - LeafWetness2 = Convert.ToInt32(byteArray[37]); - SoilTemp1 = Convert.ToInt32(byteArray[38]); - SoilTemp2 = Convert.ToInt32(byteArray[39]); - SoilTemp3 = Convert.ToInt32(byteArray[40]); - SoilTemp4 = Convert.ToInt32(byteArray[41]); - ExtraHum1 = Convert.ToInt32(byteArray[43]); - ExtraHum2 = Convert.ToInt32(byteArray[44]); - ExtraTemp1 = Convert.ToInt32(byteArray[45]); - ExtraTemp2 = Convert.ToInt32(byteArray[46]); - ExtraTemp3 = Convert.ToInt32(byteArray[47]); - SoilMoisture1 = Convert.ToInt32(byteArray[48]); - SoilMoisture2 = Convert.ToInt32(byteArray[49]); - SoilMoisture3 = Convert.ToInt32(byteArray[50]); - SoilMoisture4 = Convert.ToInt32(byteArray[51]); - AvgUVIndex = Convert.ToInt32(byteArray[28])/10.0; - ET = (double) (Convert.ToInt32(byteArray[29]))/1000; - HiUVIndex = Convert.ToInt32(byteArray[32])/10.0; - - // Get timestamp - int datevalue = BitConverter.ToInt16(byteArray, 0); - int day; - int year = Math.DivRem(datevalue, 512, out datevalue) + 2000; - int month = Math.DivRem(datevalue, 32, out day); - - int timevalue = BitConverter.ToInt16(byteArray, 2); - int minutes; - int hours = Math.DivRem(timevalue, 100, out minutes); - - try - { - Timestamp = new DateTime(year, month, day, hours, minutes, 0); - } - catch (Exception) - { - Timestamp = new DateTime(1900, 1, 1); - } - lastArchiveDate = Timestamp; - } - - public VPArchiveData() - { - HiUVIndex = 0; - ET = 0.0F; - LeafWetness1 = 0; - LeafWetness2 = 0; - LeafTemp1 = 0; - LeafTemp2 = 0; - SoilTemp1 = 0; - SoilTemp2 = 0; - SoilTemp3 = 0; - SoilTemp4 = 0; - SoilMoisture1 = 0; - SoilMoisture2 = 0; - SoilMoisture3 = 0; - SoilMoisture4 = 0; - ExtraTemp1 = 0; - ExtraTemp2 = 0; - ExtraTemp3 = 0; - ExtraHum1 = 0; - ExtraHum2 = 0; - ForecastRule = 0; - HiSolarRad = 0; - SolarRad = 0; - Timestamp = DateTime.Now; - LoOutsideTemp = 0.0F; - HiOutsideTemp = 0.0F; - HiRainRate = 0.0F; - OutsideTemperature = 0.0F; - InsideTemperature = 0.0F; - Pressure = 0.0F; - WindDirection = 0; - HiWindDirection = 0; - OutsideHumidity = 0; - InsideHumidity = 0; - HiWindSpeed = 0; - AvgWindSpeed = 0; - AvgUVIndex = 0.0F; - Rainfall = 0.0F; - } - -// internal void Process(DavisStation station) // Processes the archive data once it's been loaded -// { - /* station.IndoorTemperature = station.ConvertTempFToUser(InsideTemperature); - station.OutdoorTemperature = station.ConvertTempFToUser(OutsideTemperature); - station.IndoorHumidity = InsideHumidity; - station.OutdoorHumidity = OutsideHumidity; - station.Pressure = station.ConvertPressToInternal(Pressure); - station.WindLatest = station.ConvertWindToInternal(HiWindSpeed); - station.WindAverage = station.ConvertWindToInternal(AvgWindSpeed); - station.Bearing = (int) (WindDirection*22.5F); - station.BearingText = station.CompassPoint(WindDirection); - station.Raincounter += station.ConvertRainClicksToInternal(Rainfall); - station.RainRate = station.ConvertRainClicksToInternal(HiRainRate); - - station.UV = AvgUVIndex; - station.SolarRad = SolarRad; - station.ET = station.ConvertRainMMToUser(ET); - - station.ExtraHum[1] = ExtraHum1; - station.ExtraHum[2] = ExtraHum2; - station.ExtraTemp[1] = ExtraTemp1; - station.ExtraTemp[2] = ExtraTemp2; - station.ExtraTemp[3] = ExtraTemp3; - - station.ExtraDewPoint[1] = (double) MeteoLib.DewPoint(ExtraTemp1, ExtraHum1); - station.ExtraDewPoint[2] = (double) MeteoLib.DewPoint(ExtraTemp2, ExtraHum2); - - station.SoilMoisture1 = SoilMoisture1; - station.SoilMoisture2 = SoilMoisture2; - station.SoilMoisture3 = SoilMoisture3; - station.SoilMoisture4 = SoilMoisture4; - - station.SoilTemp1 = SoilTemp1; - station.SoilTemp2 = SoilTemp2; - station.SoilTemp3 = SoilTemp3; - station.SoilTemp4 = SoilTemp4; - - station.LeafWetness1 = LeafWetness1; - station.LeafWetness2 = LeafWetness2; - - station.LeafTemp1 = LeafTemp1; - station.LeafTemp1 = LeafTemp2; - - if (!station.gotraindaystart) - { - // we haven't got the start of day rain counter yet - // try to find it - double sodrain = station.getStartOfDayRainCounter(Timestamp); - - if (sodrain >= 0) - { - // found it - station.raindaystart = sodrain; - } - else - { - // can't find it - use current value - station.raindaystart = station.Raincounter; - } - - station.gotraindaystart = true; - } - - if (station.haveReadData) - { - if (station.lastDataReadTime.Day != Timestamp.Day) - { - // current data is for a new day - // reset start of day rain counter - station.raindaystart = station.Raincounter; - } - } - - station.haveReadData = true; - - station.RainToday = station.Raincounter - station.raindaystart; - - //String dbfile = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData) + @"\Cumulus\cumulus.s3db"; - //using(SQLiteConnection conn = new SQLiteConnection("Data Source=" + dbfile)) - //using (SQLiteCommand insertcmd = new SQLiteCommand(conn)) - //{ - // conn.Open(); - // insertcmd.CommandText = "INSERT OR IGNORE INTO [weather] ([timestamp], [outsideTemp], [hiOutsideTemp], [loOutsideTemp], [insideTemp], [barometer], [outsideHum], [insideHum], [rainfall], [hiRainRate], [avgWindSpeed], [hiWindSpeed], [windDirection], [hiWindDirection], [solarRad], [hiSolarRad], [avgUVIndex], [hiUVIndex], [ET], [leafTemp1], [leafTemp2], [soilTemp1], [soilTemp2], [soilTemp3], [soilTemp4], [leafWetness1], [leafWetness2], [extraHum1], [extraHum2], [extraTemp1], [extraTemp2], [extraTemp3], [soilMoisture1], [soilMoisture2], [soilMoisture3], [soilMoisture4]) VALUES (@timestamp, @outsideTemp, @hiOutsideTemp, @loOutsideTemp, @insideTemp, @barometer, @outsideHum, @insideHum, @rainfall, @hiRainRate, @avgWindSpeed, @hiWindSpeed, @windDirection, @hiWindDirection, @solarRad, @hiSolarRad, @avgUVIndex, @hiUVIndex, @ET, @leafTemp1, @leafTemp2, @soilTemp1, @soilTemp2, @soilTemp3, @soilTemp4, @leafWetness1, @leafWetness2, @extraHum1, @extraHum2, @extraTemp1, @extraTemp2, @extraTemp3, @soilMoisture1, @soilMoisture2, @soilMoisture3, @soilMoisture4)"; - // // set all the parameters - // insertcmd.Parameters.AddWithValue("@timestamp", Timestamp); - // insertcmd.Parameters.AddWithValue("@outsideTemp", OutsideTemperature); - // insertcmd.Parameters.AddWithValue("@hiOutsideTemp", HiOutsideTemp); - // insertcmd.Parameters.AddWithValue("@loOutsideTemp", LoOutsideTemp); - // insertcmd.Parameters.AddWithValue("@insideTemp", InsideTemperature); - // insertcmd.Parameters.AddWithValue("@barometer", Barometer); - // insertcmd.Parameters.AddWithValue("@outsideHum", OutsideHumidity); - // insertcmd.Parameters.AddWithValue("@insideHum", InsideHumidity); - // insertcmd.Parameters.AddWithValue("@rainfall", Rainfall); - // insertcmd.Parameters.AddWithValue("@hiRainRate", HiRainRate); - // insertcmd.Parameters.AddWithValue("@avgWindSpeed", AvgWindSpeed); - // insertcmd.Parameters.AddWithValue("@hiWindSpeed", HiWindSpeed); - // insertcmd.Parameters.AddWithValue("@windDirection", WindDirection); - // insertcmd.Parameters.AddWithValue("@hiWindDirection", HiWindDirection); - // insertcmd.Parameters.AddWithValue("@solarRad", SolarRad); - // insertcmd.Parameters.AddWithValue("@hiSolarRad", HiSolarRad); - // insertcmd.Parameters.AddWithValue("@avgUVIndex", AvgUVIndex); - // insertcmd.Parameters.AddWithValue("@hiUVIndex", HiUVIndex); - // insertcmd.Parameters.AddWithValue("@ET", ET); - // insertcmd.Parameters.AddWithValue("@leafTemp1", LeafTemp1); - // insertcmd.Parameters.AddWithValue("@leafTemp2", LeafTemp2); - // insertcmd.Parameters.AddWithValue("@soilTemp1", SoilTemp1); - // insertcmd.Parameters.AddWithValue("@soilTemp2", SoilTemp2); - // insertcmd.Parameters.AddWithValue("@soilTemp3", SoilTemp3); - // insertcmd.Parameters.AddWithValue("@soilTemp4", SoilTemp4); - // insertcmd.Parameters.AddWithValue("@leafWetness1", LeafWetness1); - // insertcmd.Parameters.AddWithValue("@leafWetness2", LeafWetness2); - // insertcmd.Parameters.AddWithValue("@extraHum1", ExtraHum); - // insertcmd.Parameters.AddWithValue("@extraHum2", ExtraHum2); - // insertcmd.Parameters.AddWithValue("@extraTemp1", ExtraTemp1); - // insertcmd.Parameters.AddWithValue("@extraTemp2", ExtraTemp2); - // insertcmd.Parameters.AddWithValue("@extraTemp3", ExtraTemp3); - // insertcmd.Parameters.AddWithValue("@soilMoisture1", SoilMoisture1); - // insertcmd.Parameters.AddWithValue("@soilMoisture2", SoilMoisture2); - // insertcmd.Parameters.AddWithValue("@soilMoisture3", SoilMoisture3); - // insertcmd.Parameters.AddWithValue("@soilMoisture4", SoilMoisture4); - // try - // { - // insertcmd.ExecuteNonQuery(); - // } - // catch (Exception ex) - // { - // cumulus.LogMessage(ex.ToString()); - // Trace.Flush(); - // } - //} - } */ - // } - } - - // The VPLoop2Data class extracts and stores the weather data from the array of bytes returned from the Vantage weather station - // The array is generated from the return of the LPS command. - // - // Contents of the character array (LOOP2 packet from Vantage): - // - // Field Offset Size Explanation - // "L" 0 1 - // "O" 1 1 - // "O" 2 1 Spells out "LOO" Identifies a LOOP packet - // "Bar Trend 3 1 Signed byte that indicates the current 3-hour barometer trend. It is one of these values: - // -60 = Falling Rapidly = 196 (as an unsigned byte) - // -20 = Falling Slowly = 236 (as an unsigned byte) - // 0 = Steady - // 20 = Rising Slowly - // 60 = Rising Rapidly - // 80 = ASCII "P" = Rev A firmware, no trend info is available. - // Any other value means that the Vantage does not have the 3 hours of bar data needed - // to determine the bar trend. - // Packet Type 4 1 Has the value 1, indicating a LOOP2 packet - // Unused 5 2 Unused, contains 0x7FFF - // Pressure 7 2 Current Pressure. Units are (in Hg / 1000). The barometric value should be between 20 inches - // and 32.5 inches in Vantage Pro and between 20 inches and 32.5 inches in both Vantage Pro - // Vantage Pro2. Values outside these ranges will not be logged. - // Inside Temperature 9 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. - // Inside Humidity 11 1 This is the relative humidity in %, such as 50 is returned for 50%. - // Outside Temperature 12 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. - // Wind Speed 14 1 It is a byte unsigned value in mph. If the wind speed is dashed because it lost synchronization - // with the radio or due to some other reason, the wind speed is forced to be 0. - // Unused 15 1 Unused, contains 0xFF - // Wind Direction 16 2 It is a two byte unsigned value from 0 to 360 degrees. - // (0° is North, 90° is East, 180° is South and 270° is West.) - // 10-Min Avg Wind Speed 18 2 It is a two-byte unsigned value. - // 2-Min Avg Wind Speed 20 2 It is a two-byte unsigned value. - // 10-Min Wind Gust 22 2 It is a two-byte unsigned value. - // Wind Direction for the 24 2 It is a two-byte unsigned value from 1 to 360 degrees. - // 10-Min Wind Gust (0° is no wind data, 90° is East, 180° is South, 270° is West and 360° is north) - // Unused 26 2 Unused field, filled with 0x7FFF - // Unused 28 2 Unused field, filled with 0x7FFF - // Dew Point 30 2 The value is a signed two byte value in whole degrees F. 255 = dashed data - // Unused 32 1 Unused field, filled with 0xFF - // Outside Humidity 33 1 This is the relative humidity in %, such as 50 is returned for 50%. - // Unused 34 1 Unused field, filled with 0xFF - // Heat Index 35 2 The value is a signed two byte value in whole degrees F. 255 = dashed data - // Wind Chill 37 2 The value is a signed two byte value in whole degrees F. 255 = dashed data - // THSW Index 39 2 The value is a signed two byte value in whole degrees F. 255 = dashed data - // Rain Rate 41 2 In rain clicks per hour. - // UV 43 1 Unit is in UV Index - // Solar Radiation 44 2 The unit is in watt/meter2. - // Storm Rain 46 2 The storm is stored as number of rain clicks. (0.2mm or 0.01in) - // Start Date of current Storm 48 2 Bit 15 to bit 12 is the month, bit 11 to bit 7 is the day and bit 6 to bit 0 is the year offset by 2000. - // Daily Rain 50 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) - // Last 15-min Rain 52 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) - // Last Hour Rain 54 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) - // Daily ET 56 2 This value is sent as the 1000th of an inch. - // Last 24-Hour Rain 58 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) - // Barometric Reduction Method 60 1 Bar reduction method: 0 - user offset 1- Altimeter Setting 2- NOAA Bar Reduction. For VP2, this will always be 2. - // User-entered Barometric Offset 61 2 Barometer calibration number in 1000th of an inch - // Barometric calibration number 63 2 Calibration offset in 1000th of an inch - // Barometric Sensor Raw Reading 65 2 In 1000th of an inch - // Absolute Barometric Pressure 67 2 In 1000th of an inch, equals to the raw sensor reading plus user entered offset - // Altimeter Setting 69 2 In 1000th of an inch - // Unused 71 1 Unused field, filled with 0xFF - // Unused 72 1 Undefined - // Next 10-min Wind Speed Graph - // Pointer 73 1 Points to the next 10-minute wind speed graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next 15-min Wind Speed Graph - // Pointer 74 1 Points to the next 15-minute wind speed graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next Hourly Wind Speed Graph - // Pointer 75 1 Points to the next hour wind speed graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next Daily Wind Speed Graph - // Pointer 76 1 Points to the next daily wind speed graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next Minute Rain Graph Pointer 77 1 Points to the next minute rain graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next Rain Storm Graph Pointer 78 1 Points to the next rain storm graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 254on Vantage Vue console) - // Index to the Minute within - // an Hour 79 1 It keeps track of the minute within an hour for the rain calculation. (range from 0 to 59) - // Next Monthly Rain 80 1 Points to the next monthly rain graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next Yearly Rain 81 1 Points to the next yearly rain graph point. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Next Seasonal Rain 82 1 Points to the next seasonal rain graph point. Yearly rain always resets at the beginning of the calendar, - // but seasonal rain resets when rain season begins. For current graph point, - // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) - // Unused 83 2 Unused field, filled with 0x7FFF - // Unused 85 2 Unused field, filled with 0x7FFF - // Unused 87 2 Unused field, filled with 0x7FFF - // Unused 89 2 Unused field, filled with 0x7FFF - // Unused 91 2 Unused field, filled with 0x7FFF - // Unused 93 2 Unused field, filled with 0x7FFF - // "\n" = 0x0A 95 1 - // "\r" = 0x0D 96 1 - // CRC 97 2 - // Total Length 99 - public class VPLoop2Data - { - //public int BarTrend { get; private set; } - //public int IndoorHum { get; private set; } - //public double Temperature { get; private set; } - public int CurrentWindSpeed { get; private set; } - public int WindDirection { get; private set; } - //public double WindAverage { get; private set; } - //public double WindAverage2Min { get; private set; } - public int WindGust10Min { get; private set; } - public int WindGustDir { get; private set; } - //public int Humidity { get; private set; } - //public int HeatIndex { get; private set; } - //public int WindChill { get; private set; } - public int THSWindex { get; private set; } - //public int RainRate { get; private set; } - //public int UV { get; private set; } - // public int Solar { get; private set; } - //public int StormRain { get; private set; } - //public int DailyRain { get; private set; } - //public int Last15mRain { get; private set; } - //public int LastHourRain { get; private set; } - //public double DailyET { get; private set; } - //public int Last24hRain { get; private set; } - public double AbsolutePressure { get; private set; } - - // Load - disassembles the byte array passed in and loads it into local data that the accessors can use. - // Actual data is in the format to the right of the assignments - I convert it to make it easier to use - // When bytes have to be assembled into 2-byte, 16-bit numbers, I convert two bytes from the array into - // an Int16 (16-bit integer). When a single byte is all that's needed, I just convert it to an Int32. - // In the end, all integers are cast to Int32 for return. - public void Load(byte[] byteArray) - { - //BarTrend = Convert.ToInt32(byteArray[3]); - //IndoorHum = Convert.ToInt32(byteArray[11]); - //Temperature = (double)BitConverter.ToInt16(byteArray, 12) / 10; - CurrentWindSpeed = Convert.ToInt32(byteArray[14]); // Byte - unsigned byte - WindDirection = BitConverter.ToInt16(byteArray, 16); // Uint16 - //WindAverage = (double)BitConverter.ToInt16(byteArray, 18) / 10; - //WindAverage2Min = (double)BitConverter.ToInt16(byteArray, 20) / 10; - WindGust10Min = Convert.ToInt32(byteArray[22]); - WindGustDir = BitConverter.ToInt16(byteArray, 24); // Uint16 - //Humidity = Convert.ToInt32(byteArray[33]); - //HeatIndex = BitConverter.ToInt16(byteArray, 35); - //WindChill = BitConverter.ToInt16(byteArray, 37); - THSWindex = BitConverter.ToInt16(byteArray, 39); - //RainRate = BitConverter.ToInt16(byteArray, 41); // clicks per hour - //UV = Convert.ToInt32(byteArray[43]); - //Solar = BitConverter.ToInt16(byteArray, 44); - //StormRain = BitConverter.ToInt16(byteArray, 46); - //DailyRain = BitConverter.ToInt16(byteArray, 50); - //Last15mRain = BitConverter.ToInt16(byteArray, 52); - //LastHourRain = BitConverter.ToInt16(byteArray, 54); - //DailyET = (double)BitConverter.ToInt16(byteArray, 56) / 1000; - //Last24hRain = BitConverter.ToInt16(byteArray, 58); - AbsolutePressure = (double) (BitConverter.ToInt16(byteArray, 67)) / 1000; // Uint16 - } - } + // Load - disassembles the byte array passed in and loads it into local data that the accessors can use. + // Actual data is in the format to the right of the assignments - I convert it to make it easier to use + // When bytes have to be assembled into 2-byte, 16-bit numbers, I convert two bytes from the array into + // an Int16 (16-bit integer). When a single byte is all that's needed, I just convert it to an Int32. + // In the end, all integers are cast to Int32 for return. + public void Load(byte[] byteArray) + { + PressureTrend = Convert.ToInt32((sbyte) byteArray[3]); // Sbyte - signed byte + Pressure = (double) (BitConverter.ToInt16(byteArray, 7)) / 1000; // Uint16 + InsideTemperature = (double) (BitConverter.ToInt16(byteArray, 9)) / 10; // Uint16 + InsideHumidity = Convert.ToInt32(byteArray[11]); // Byte - unsigned byte + OutsideTemperature = (double) (BitConverter.ToInt16(byteArray, 12)) / 10; // Uint16 + OutsideHumidity = Convert.ToInt32(byteArray[33]); // Byte - unsigned byte + WindDirection = BitConverter.ToInt16(byteArray, 16); // Uint16 + CurrentWindSpeed = Convert.ToInt32(byteArray[14]); // Byte - unsigned byte + AvgWindSpeed = Convert.ToInt32(byteArray[15]); // Byte - unsigned byte + DailyRain = (double) (BitConverter.ToInt16(byteArray, 50)); // Uint16 + MonthRain = (double) (BitConverter.ToInt16(byteArray, 52)); // Uint16 + YearRain = (double) (BitConverter.ToInt16(byteArray, 54)); // Uint16 + RainRate = (double) (BitConverter.ToInt16(byteArray, 41)); // Uint16 + StormRain = (double) (BitConverter.ToInt16(byteArray, 46)); + ForecastRule = Convert.ToInt32(byteArray[90]); + LeafTemp1 = Convert.ToInt32(byteArray[29]); + LeafTemp2 = Convert.ToInt32(byteArray[30]); + LeafTemp3 = Convert.ToInt32(byteArray[31]); + LeafTemp4 = Convert.ToInt32(byteArray[32]); + LeafWetness1 = Convert.ToInt32(byteArray[66]); + LeafWetness2 = Convert.ToInt32(byteArray[67]); + LeafWetness3 = Convert.ToInt32(byteArray[68]); + LeafWetness4 = Convert.ToInt32(byteArray[69]); + SoilTemp1 = Convert.ToInt32(byteArray[25]); + SoilTemp2 = Convert.ToInt32(byteArray[26]); + SoilTemp3 = Convert.ToInt32(byteArray[27]); + SoilTemp4 = Convert.ToInt32(byteArray[28]); + ExtraHum1 = Convert.ToInt32(byteArray[34]); + ExtraHum2 = Convert.ToInt32(byteArray[35]); + ExtraHum3 = Convert.ToInt32(byteArray[36]); + ExtraHum4 = Convert.ToInt32(byteArray[37]); + ExtraHum5 = Convert.ToInt32(byteArray[38]); + ExtraHum6 = Convert.ToInt32(byteArray[39]); + ExtraHum7 = Convert.ToInt32(byteArray[40]); + ExtraTemp1 = Convert.ToInt32(byteArray[18]); + ExtraTemp2 = Convert.ToInt32(byteArray[19]); + ExtraTemp3 = Convert.ToInt32(byteArray[20]); + ExtraTemp4 = Convert.ToInt32(byteArray[21]); + ExtraTemp5 = Convert.ToInt32(byteArray[22]); + ExtraTemp6 = Convert.ToInt32(byteArray[23]); + ExtraTemp7 = Convert.ToInt32(byteArray[24]); + SoilMoisture1 = Convert.ToInt32(byteArray[62]); + SoilMoisture2 = Convert.ToInt32(byteArray[63]); + SoilMoisture3 = Convert.ToInt32(byteArray[64]); + SoilMoisture4 = Convert.ToInt32(byteArray[65]); + UVIndex = (double) (Convert.ToInt32(byteArray[43])) / 10; + SolarRad = BitConverter.ToInt16(byteArray, 44); + var dayET = (double) (BitConverter.ToInt16(byteArray, 56)) / 1000; + var yearET = (double) (BitConverter.ToInt16(byteArray, 60)) / 100; + // It appears that the annual ET in the loop data does not include today + AnnualET = yearET + dayET; + TXbattStatus = byteArray[86]; + ConBatVoltage = ((BitConverter.ToInt16(byteArray, 87) * 300) / 512.0) / 100.0; + + try + { + var stormRainStart = BitConverter.ToInt16(byteArray, 48); + var srMonth = (stormRainStart & 0xF000) >> 12; + var srDay = (stormRainStart & 0x0F80) >> 7; + var srYear = (stormRainStart & 0x007F) + 2000; + if (srMonth < 13 && srDay < 32) // Exception suppression! + { + StormRainStart = new DateTime(srYear, srMonth, srDay); + } + else + { + StormRainStart = DateTime.MinValue; + } + } + catch (Exception) + { + StormRainStart = DateTime.MinValue; + } + + // get the current date and time + // DateTime currTime = DateTime.Now; + + //int timevalue = BitConverter.ToInt16(byteArray, 91); + //int minutes; + //int hours = Math.DivRem(timevalue, 100, out minutes); + + // Create a new Datetime instance - use current year, month and day + //SunRise = new DateTime(currTime.Year, currTime.Month, currTime.Day, hours, minutes, 0); + + //timevalue = BitConverter.ToInt16(byteArray, 93); + //hours = Math.DivRem(timevalue, 100, out minutes); + //SunSet = new DateTime(currTime.Year, currTime.Month, currTime.Day, hours, minutes, 0); + } + + // This procedure displays the data that we've captured from the Vantage and processed already. + public string DebugString() + { + StringBuilder outputString = new StringBuilder(); + + // Format the string for output + outputString.Append("Pressure: " + Pressure.ToString("f2") + "in. " + PressureTrendText() + Environment.NewLine); + outputString.Append("Inside Temp: " + InsideTemperature.ToString() + Environment.NewLine); + outputString.Append("Inside Humidity: " + InsideHumidity.ToString() + "%" + Environment.NewLine); + outputString.Append("Outside Temp: " + OutsideTemperature.ToString() + Environment.NewLine); + outputString.Append("Outside Humidity: " + OutsideHumidity.ToString() + "%" + Environment.NewLine); + outputString.Append("Wind Direction: " + WindDirectionText() + " @ " + WindDirection.ToString() + " degrees" + Environment.NewLine); + outputString.Append("Current Wind Speed: " + CurrentWindSpeed.ToString() + "MPH" + Environment.NewLine); + outputString.Append("10 Minute Average Wind Speed: " + AvgWindSpeed.ToString() + "MPH" + Environment.NewLine); + outputString.Append("Daily Rain: " + DailyRain.ToString() + "in" + Environment.NewLine); + outputString.Append("Sunrise: " + SunRise.ToString("t") + Environment.NewLine); + outputString.Append("Sunset: " + SunSet.ToString("t") + Environment.NewLine); + + return (outputString.ToString()); + } + + private string WindDirectionText() + { + string windDirString; + + // The wind direction is given in degrees - 0-359 - convert to string representing the direction + if (WindDirection >= 337 || WindDirection <= 23) + windDirString = "N"; + else if (WindDirection > 292) + windDirString = "NW"; + else if (WindDirection >= 247) + windDirString = "W"; + else if (WindDirection > 203) + windDirString = "SW"; + else if (WindDirection >= 157) + windDirString = "S"; + else if (WindDirection > 113) + windDirString = "SE"; + else if (WindDirection >= 67) + windDirString = "E"; + else if (WindDirection > 23) + windDirString = "NE"; + else + windDirString = "??"; + + return windDirString; + } + + private string PressureTrendText() + { + // The barometric trend is in signed integer values. Convert these to something meaningful. + switch (PressureTrend) + { + case (-60): + return "Falling Rapidly"; + case (-20): + return "Falling Slowly"; + case (0): + return "Steady"; + case (20): + return "Rising Slowly"; + case (60): + return "Rising Rapidly"; + default: + return "??"; + } + } + + public VPLoopData() + { + PressureTrend = 0; + CurrentWindSpeed = 0; + AvgWindSpeed = 0; + InsideHumidity = 0; + OutsideHumidity = 0; + InsideTemperature = 0.0F; + OutsideTemperature = 0.0F; + DailyRain = 0.0F; + WindDirection = 0; + SunRise = DateTime.Now; + SunSet = DateTime.Now; + MonthRain = 0.0F; + YearRain = 0.0F; + RainRate = 0.0F; + } + } + + // The WeatherCalibrationData class extracts and stores the weather data from the array of bytes returned from the Vantage weather station + // The array is generated from the return of the CALED command. + // + // Field Offset Size Explanation + // Inside Temperature 0 2 + // Outside Temperature 2 2 + // Extra Temperature 4 14 (7 * 2) + // Soil Temperatures 18 8 (4 * 2) + // Leaf Temperatures 26 8 (4 * 2) + // Inside Humidity 34 1 + // Outside Humidity 35 1 + // Extra Humidities 36 7 + // Total Length 43 + public class WeatherCalibrationData + { + // Not yet implemented + } + + public class VPArchiveData + { + // Format of the archive records read from the VP data logger + // Field Offset Size Dash Value Explanation + // Date Stamp 0 2 N/A These 16 bits hold the date that the archive was + // written in the following format: + // Year (7 bits) | Month (4 bits) | Day (5 bits) or: + // day + month*32 + (year-2000)*512) + // Time Stamp 2 2 N/A Time on the Vantage that the archive record was written: + // (Hour * 100) + minute. + // Outside Temperature 4 2 32767 Either the Average Outside Temperature, or the + // Final Outside Temperature over the archive period. + // Units are (°F / 10) + // High Out Temperature 6 2 -32768 Highest Outside Temp over the archive period. + // Low Out Temperature 8 2 32767 Lowest Outside Temp over the archive period. + // Rainfall 10 2 0 Number of rain clicks over the archive period + // High Rain Rate 12 2 0 Highest rain rate over the archive period, or the rate + // shown on the console at the end of the period if there + // was no rain. Units are (rain clicks / hour) + // Barometer 14 2 0 Barometer reading at the end of the archive period. + // Units are (in Hg / 1000). + // Solar Radiation 16 2 32767 Average Solar Rad over the archive period. + // Units are (Watts / m2) + // Number of Wind Samples 18 2 0 Number of packets containing wind speed data + // received from the ISS or wireless anemometer. + // Inside Temperature 20 2 32767 Either the Average Inside Temperature, or the Final + // Inside Temperature over the archive period. + // Units are (°F / 10) + // Inside Humidity 22 1 255 Inside Humidity at the end of the archive period + // Outside Humidity 23 1 255 Outside Humidity at the end of the archive period + // Average Wind Speed 24 1 255 Average Wind Speed over the archive interval. + // Units are (MPH) + // High Wind Speed 25 1 0 Highest Wind Speed over the archive interval. + // Units are (MPH) + // Direction of Hi Wind Speed 26 1 32767 Direction code of the High Wind speed. + // 0=N, 1=NNE, 2=NE, … 14=NW, 15=NNW, 255=Dashed + // Prevailing Wind Direction 27 1 32767 Prevailing or Dominant Wind Direction code. + // 0=N, 1=NNE, 2=NE, … 14=NW, 15=NNW, 255=Dashed + // Average UV Index 28 1 255 Average UV Index. Units are (UV Index / 10) + // ET 29 1 0 ET accumulated over the last hour. Only records "on + // the hour" will have a non-zero value. + // Units are (in / 1000) + // High Solar Radiation 30 2 0 Highest Solar Rad value over the archive period. + // Units are (Watts / m2) + // High UV Index 32 1 0 Highest UV Index value over the archive period. + // Units are (Watts / m2) + // Forecast Rule 33 1 193 Weather forecast rule at the end of the archive period. + // Leaf Temperature 34 2 255 2 Leaf Temperature values. Units are (°F + 90) + // Leaf Wetnesses 36 2 255 2 Leaf Wetness values. Range is 0 – 15 + // Soil Temperatures 38 4 255 4 Soil Temperatures. Units are (°F + 90) + // Download Record Type 42 1 N/A 0xFF = Rev A, 0x00 = Rev B archive record + // Extra Humidities 43 2 255 2 Extra Humidity values + // Extra Temperatures 45 3 32767 3 Extra Temperature values. Units are (°F + 90) + // Soil Moistures 48 4 255 4 Soil Moisture values. Units are (cb) + + public int SoilMoisture1 { get; private set; } + + public int SoilMoisture2 { get; private set; } + + public int SoilMoisture3 { get; private set; } + + public int SoilMoisture4 { get; private set; } + + public int ExtraTemp1 { get; private set; } + + public int ExtraTemp2 { get; private set; } + + public int ExtraTemp3 { get; private set; } + + public int ExtraHum1 { get; private set; } + + public int ExtraHum2 { get; private set; } + + public int SoilTemp1 { get; private set; } + + public int SoilTemp2 { get; private set; } + + public int SoilTemp3 { get; private set; } + + public int SoilTemp4 { get; private set; } + + public double ET { get; private set; } + + public double HiUVIndex { get; private set; } + + public double AvgUVIndex { get; private set; } + + public int LeafWetness2 { get; private set; } + + public int LeafWetness1 { get; private set; } + + public int LeafTemp2 { get; private set; } + + public int LeafTemp1 { get; private set; } + + public int ForecastRule { get; private set; } + + public int HiSolarRad { get; private set; } + + public int SolarRad { get; private set; } + + public DateTime Timestamp { get; private set; } + + public double LoOutsideTemp { get; private set; } + + public double HiOutsideTemp { get; private set; } + + public double HiRainRate { get; private set; } + + public double Rainfall { get; private set; } + + public double OutsideTemperature { get; private set; } + + public double InsideTemperature { get; private set; } + + public double Pressure { get; private set; } + + public int WindDirection { get; private set; } + + public int HiWindDirection { get; private set; } + + public int OutsideHumidity { get; private set; } + + public int InsideHumidity { get; private set; } + + public int HiWindSpeed { get; private set; } + + public int AvgWindSpeed { get; private set; } + + public void Load(byte[] byteArray, out DateTime lastArchiveDate) + { + Pressure = (double) (BitConverter.ToInt16(byteArray, 14)) / 1000.0; // Uint16 + InsideTemperature = (double) (BitConverter.ToInt16(byteArray, 20)) / 10.0; // Uint16 + InsideHumidity = Convert.ToInt32(byteArray[22]); // Byte - unsigned byte + OutsideTemperature = (double) (BitConverter.ToInt16(byteArray, 4)) / 10.0; // Uint16 + HiOutsideTemp = (double) (BitConverter.ToInt16(byteArray, 6)) / 10.0; // Uint16 + LoOutsideTemp = (double) (BitConverter.ToInt16(byteArray, 8)) / 10.0; // Uint16 + OutsideHumidity = Convert.ToInt32(byteArray[23]); // Byte - unsigned byte + HiWindDirection = Convert.ToInt32(byteArray[26]); // Uint16 + WindDirection = Convert.ToInt32(byteArray[27]); // Uint16 + SolarRad = BitConverter.ToInt16(byteArray, 16); // Uint16 + HiSolarRad = BitConverter.ToInt16(byteArray, 30); // Uint16 + AvgWindSpeed = Convert.ToInt32(byteArray[24]); // Byte - unsigned byte + HiWindSpeed = Convert.ToInt32(byteArray[25]); // Byte - unsigned byte + Rainfall = (double) (BitConverter.ToInt16(byteArray, 10)); // Uint16 + HiRainRate = (double) (BitConverter.ToInt16(byteArray, 12)); // Uint16 + ForecastRule = Convert.ToInt32(byteArray[33]); + LeafTemp1 = Convert.ToInt32(byteArray[34]); + LeafTemp2 = Convert.ToInt32(byteArray[35]); + LeafWetness1 = Convert.ToInt32(byteArray[36]); + LeafWetness2 = Convert.ToInt32(byteArray[37]); + SoilTemp1 = Convert.ToInt32(byteArray[38]); + SoilTemp2 = Convert.ToInt32(byteArray[39]); + SoilTemp3 = Convert.ToInt32(byteArray[40]); + SoilTemp4 = Convert.ToInt32(byteArray[41]); + ExtraHum1 = Convert.ToInt32(byteArray[43]); + ExtraHum2 = Convert.ToInt32(byteArray[44]); + ExtraTemp1 = Convert.ToInt32(byteArray[45]); + ExtraTemp2 = Convert.ToInt32(byteArray[46]); + ExtraTemp3 = Convert.ToInt32(byteArray[47]); + SoilMoisture1 = Convert.ToInt32(byteArray[48]); + SoilMoisture2 = Convert.ToInt32(byteArray[49]); + SoilMoisture3 = Convert.ToInt32(byteArray[50]); + SoilMoisture4 = Convert.ToInt32(byteArray[51]); + AvgUVIndex = Convert.ToInt32(byteArray[28]) / 10.0; + ET = (double) (Convert.ToInt32(byteArray[29])) / 1000; + HiUVIndex = Convert.ToInt32(byteArray[32]) / 10.0; + + // Get timestamp + int datevalue = BitConverter.ToInt16(byteArray, 0); + int day; + int year = Math.DivRem(datevalue, 512, out datevalue) + 2000; + int month = Math.DivRem(datevalue, 32, out day); + + int timevalue = BitConverter.ToInt16(byteArray, 2); + int minutes; + int hours = Math.DivRem(timevalue, 100, out minutes); + + try + { + Timestamp = new DateTime(year, month, day, hours, minutes, 0); + } + catch (Exception) + { + Timestamp = new DateTime(1900, 1, 1); + } + lastArchiveDate = Timestamp; + } + + public VPArchiveData() + { + HiUVIndex = 0; + ET = 0.0F; + LeafWetness1 = 0; + LeafWetness2 = 0; + LeafTemp1 = 0; + LeafTemp2 = 0; + SoilTemp1 = 0; + SoilTemp2 = 0; + SoilTemp3 = 0; + SoilTemp4 = 0; + SoilMoisture1 = 0; + SoilMoisture2 = 0; + SoilMoisture3 = 0; + SoilMoisture4 = 0; + ExtraTemp1 = 0; + ExtraTemp2 = 0; + ExtraTemp3 = 0; + ExtraHum1 = 0; + ExtraHum2 = 0; + ForecastRule = 0; + HiSolarRad = 0; + SolarRad = 0; + Timestamp = DateTime.Now; + LoOutsideTemp = 0.0F; + HiOutsideTemp = 0.0F; + HiRainRate = 0.0F; + OutsideTemperature = 0.0F; + InsideTemperature = 0.0F; + Pressure = 0.0F; + WindDirection = 0; + HiWindDirection = 0; + OutsideHumidity = 0; + InsideHumidity = 0; + HiWindSpeed = 0; + AvgWindSpeed = 0; + AvgUVIndex = 0.0F; + Rainfall = 0.0F; + } + + // internal void Process(DavisStation station) // Processes the archive data once it's been loaded + // { + /* station.IndoorTemperature = station.ConvertTempFToUser(InsideTemperature); + station.OutdoorTemperature = station.ConvertTempFToUser(OutsideTemperature); + station.IndoorHumidity = InsideHumidity; + station.OutdoorHumidity = OutsideHumidity; + station.Pressure = station.ConvertPressToInternal(Pressure); + station.WindLatest = station.ConvertWindToInternal(HiWindSpeed); + station.WindAverage = station.ConvertWindToInternal(AvgWindSpeed); + station.Bearing = (int) (WindDirection*22.5F); + station.BearingText = station.CompassPoint(WindDirection); + station.Raincounter += station.ConvertRainClicksToInternal(Rainfall); + station.RainRate = station.ConvertRainClicksToInternal(HiRainRate); + + station.UV = AvgUVIndex; + station.SolarRad = SolarRad; + station.ET = station.ConvertRainMMToUser(ET); + + station.ExtraHum[1] = ExtraHum1; + station.ExtraHum[2] = ExtraHum2; + station.ExtraTemp[1] = ExtraTemp1; + station.ExtraTemp[2] = ExtraTemp2; + station.ExtraTemp[3] = ExtraTemp3; + + station.ExtraDewPoint[1] = (double) MeteoLib.DewPoint(ExtraTemp1, ExtraHum1); + station.ExtraDewPoint[2] = (double) MeteoLib.DewPoint(ExtraTemp2, ExtraHum2); + + station.SoilMoisture1 = SoilMoisture1; + station.SoilMoisture2 = SoilMoisture2; + station.SoilMoisture3 = SoilMoisture3; + station.SoilMoisture4 = SoilMoisture4; + + station.SoilTemp1 = SoilTemp1; + station.SoilTemp2 = SoilTemp2; + station.SoilTemp3 = SoilTemp3; + station.SoilTemp4 = SoilTemp4; + + station.LeafWetness1 = LeafWetness1; + station.LeafWetness2 = LeafWetness2; + + station.LeafTemp1 = LeafTemp1; + station.LeafTemp1 = LeafTemp2; + + if (!station.gotraindaystart) + { + // we haven't got the start of day rain counter yet + // try to find it + double sodrain = station.getStartOfDayRainCounter(Timestamp); + + if (sodrain >= 0) + { + // found it + station.raindaystart = sodrain; + } + else + { + // can't find it - use current value + station.raindaystart = station.Raincounter; + } + + station.gotraindaystart = true; + } + + if (station.haveReadData) + { + if (station.lastDataReadTime.Day != Timestamp.Day) + { + // current data is for a new day + // reset start of day rain counter + station.raindaystart = station.Raincounter; + } + } + + station.haveReadData = true; + + station.RainToday = station.Raincounter - station.raindaystart; + + //String dbfile = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData) + @"\Cumulus\cumulus.s3db"; + //using(SQLiteConnection conn = new SQLiteConnection("Data Source=" + dbfile)) + //using (SQLiteCommand insertcmd = new SQLiteCommand(conn)) + //{ + // conn.Open(); + // insertcmd.CommandText = "INSERT OR IGNORE INTO [weather] ([timestamp], [outsideTemp], [hiOutsideTemp], [loOutsideTemp], [insideTemp], [barometer], [outsideHum], [insideHum], [rainfall], [hiRainRate], [avgWindSpeed], [hiWindSpeed], [windDirection], [hiWindDirection], [solarRad], [hiSolarRad], [avgUVIndex], [hiUVIndex], [ET], [leafTemp1], [leafTemp2], [soilTemp1], [soilTemp2], [soilTemp3], [soilTemp4], [leafWetness1], [leafWetness2], [extraHum1], [extraHum2], [extraTemp1], [extraTemp2], [extraTemp3], [soilMoisture1], [soilMoisture2], [soilMoisture3], [soilMoisture4]) VALUES (@timestamp, @outsideTemp, @hiOutsideTemp, @loOutsideTemp, @insideTemp, @barometer, @outsideHum, @insideHum, @rainfall, @hiRainRate, @avgWindSpeed, @hiWindSpeed, @windDirection, @hiWindDirection, @solarRad, @hiSolarRad, @avgUVIndex, @hiUVIndex, @ET, @leafTemp1, @leafTemp2, @soilTemp1, @soilTemp2, @soilTemp3, @soilTemp4, @leafWetness1, @leafWetness2, @extraHum1, @extraHum2, @extraTemp1, @extraTemp2, @extraTemp3, @soilMoisture1, @soilMoisture2, @soilMoisture3, @soilMoisture4)"; + // // set all the parameters + // insertcmd.Parameters.AddWithValue("@timestamp", Timestamp); + // insertcmd.Parameters.AddWithValue("@outsideTemp", OutsideTemperature); + // insertcmd.Parameters.AddWithValue("@hiOutsideTemp", HiOutsideTemp); + // insertcmd.Parameters.AddWithValue("@loOutsideTemp", LoOutsideTemp); + // insertcmd.Parameters.AddWithValue("@insideTemp", InsideTemperature); + // insertcmd.Parameters.AddWithValue("@barometer", Barometer); + // insertcmd.Parameters.AddWithValue("@outsideHum", OutsideHumidity); + // insertcmd.Parameters.AddWithValue("@insideHum", InsideHumidity); + // insertcmd.Parameters.AddWithValue("@rainfall", Rainfall); + // insertcmd.Parameters.AddWithValue("@hiRainRate", HiRainRate); + // insertcmd.Parameters.AddWithValue("@avgWindSpeed", AvgWindSpeed); + // insertcmd.Parameters.AddWithValue("@hiWindSpeed", HiWindSpeed); + // insertcmd.Parameters.AddWithValue("@windDirection", WindDirection); + // insertcmd.Parameters.AddWithValue("@hiWindDirection", HiWindDirection); + // insertcmd.Parameters.AddWithValue("@solarRad", SolarRad); + // insertcmd.Parameters.AddWithValue("@hiSolarRad", HiSolarRad); + // insertcmd.Parameters.AddWithValue("@avgUVIndex", AvgUVIndex); + // insertcmd.Parameters.AddWithValue("@hiUVIndex", HiUVIndex); + // insertcmd.Parameters.AddWithValue("@ET", ET); + // insertcmd.Parameters.AddWithValue("@leafTemp1", LeafTemp1); + // insertcmd.Parameters.AddWithValue("@leafTemp2", LeafTemp2); + // insertcmd.Parameters.AddWithValue("@soilTemp1", SoilTemp1); + // insertcmd.Parameters.AddWithValue("@soilTemp2", SoilTemp2); + // insertcmd.Parameters.AddWithValue("@soilTemp3", SoilTemp3); + // insertcmd.Parameters.AddWithValue("@soilTemp4", SoilTemp4); + // insertcmd.Parameters.AddWithValue("@leafWetness1", LeafWetness1); + // insertcmd.Parameters.AddWithValue("@leafWetness2", LeafWetness2); + // insertcmd.Parameters.AddWithValue("@extraHum1", ExtraHum); + // insertcmd.Parameters.AddWithValue("@extraHum2", ExtraHum2); + // insertcmd.Parameters.AddWithValue("@extraTemp1", ExtraTemp1); + // insertcmd.Parameters.AddWithValue("@extraTemp2", ExtraTemp2); + // insertcmd.Parameters.AddWithValue("@extraTemp3", ExtraTemp3); + // insertcmd.Parameters.AddWithValue("@soilMoisture1", SoilMoisture1); + // insertcmd.Parameters.AddWithValue("@soilMoisture2", SoilMoisture2); + // insertcmd.Parameters.AddWithValue("@soilMoisture3", SoilMoisture3); + // insertcmd.Parameters.AddWithValue("@soilMoisture4", SoilMoisture4); + // try + // { + // insertcmd.ExecuteNonQuery(); + // } + // catch (Exception ex) + // { + // cumulus.LogMessage(ex.ToString()); + // Trace.Flush(); + // } + //} + } */ + // } + } + + // The VPLoop2Data class extracts and stores the weather data from the array of bytes returned from the Vantage weather station + // The array is generated from the return of the LPS command. + // + // Contents of the character array (LOOP2 packet from Vantage): + // + // Field Offset Size Explanation + // "L" 0 1 + // "O" 1 1 + // "O" 2 1 Spells out "LOO" Identifies a LOOP packet + // "Bar Trend 3 1 Signed byte that indicates the current 3-hour barometer trend. It is one of these values: + // -60 = Falling Rapidly = 196 (as an unsigned byte) + // -20 = Falling Slowly = 236 (as an unsigned byte) + // 0 = Steady + // 20 = Rising Slowly + // 60 = Rising Rapidly + // 80 = ASCII "P" = Rev A firmware, no trend info is available. + // Any other value means that the Vantage does not have the 3 hours of bar data needed + // to determine the bar trend. + // Packet Type 4 1 Has the value 1, indicating a LOOP2 packet + // Unused 5 2 Unused, contains 0x7FFF + // Pressure 7 2 Current Pressure. Units are (in Hg / 1000). The barometric value should be between 20 inches + // and 32.5 inches in Vantage Pro and between 20 inches and 32.5 inches in both Vantage Pro + // Vantage Pro2. Values outside these ranges will not be logged. + // Inside Temperature 9 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. + // Inside Humidity 11 1 This is the relative humidity in %, such as 50 is returned for 50%. + // Outside Temperature 12 2 The value is sent as 10th of a degree in F. For example, 795 is returned for 79.5°F. + // Wind Speed 14 1 It is a byte unsigned value in mph. If the wind speed is dashed because it lost synchronization + // with the radio or due to some other reason, the wind speed is forced to be 0. + // Unused 15 1 Unused, contains 0xFF + // Wind Direction 16 2 It is a two byte unsigned value from 0 to 360 degrees. + // (0° is North, 90° is East, 180° is South and 270° is West.) + // 10-Min Avg Wind Speed 18 2 It is a two-byte unsigned value. + // 2-Min Avg Wind Speed 20 2 It is a two-byte unsigned value. + // 10-Min Wind Gust 22 2 It is a two-byte unsigned value. + // Wind Direction for the 24 2 It is a two-byte unsigned value from 1 to 360 degrees. + // 10-Min Wind Gust (0° is no wind data, 90° is East, 180° is South, 270° is West and 360° is north) + // Unused 26 2 Unused field, filled with 0x7FFF + // Unused 28 2 Unused field, filled with 0x7FFF + // Dew Point 30 2 The value is a signed two byte value in whole degrees F. 255 = dashed data + // Unused 32 1 Unused field, filled with 0xFF + // Outside Humidity 33 1 This is the relative humidity in %, such as 50 is returned for 50%. + // Unused 34 1 Unused field, filled with 0xFF + // Heat Index 35 2 The value is a signed two byte value in whole degrees F. 255 = dashed data + // Wind Chill 37 2 The value is a signed two byte value in whole degrees F. 255 = dashed data + // THSW Index 39 2 The value is a signed two byte value in whole degrees F. 255 = dashed data + // Rain Rate 41 2 In rain clicks per hour. + // UV 43 1 Unit is in UV Index + // Solar Radiation 44 2 The unit is in watt/meter2. + // Storm Rain 46 2 The storm is stored as number of rain clicks. (0.2mm or 0.01in) + // Start Date of current Storm 48 2 Bit 15 to bit 12 is the month, bit 11 to bit 7 is the day and bit 6 to bit 0 is the year offset by 2000. + // Daily Rain 50 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) + // Last 15-min Rain 52 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) + // Last Hour Rain 54 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) + // Daily ET 56 2 This value is sent as the 1000th of an inch. + // Last 24-Hour Rain 58 2 This value is sent as number of rain clicks. (0.2mm or 0.01in) + // Barometric Reduction Method 60 1 Bar reduction method: 0 - user offset 1- Altimeter Setting 2- NOAA Bar Reduction. For VP2, this will always be 2. + // User-entered Barometric Offset 61 2 Barometer calibration number in 1000th of an inch + // Barometric calibration number 63 2 Calibration offset in 1000th of an inch + // Barometric Sensor Raw Reading 65 2 In 1000th of an inch + // Absolute Barometric Pressure 67 2 In 1000th of an inch, equals to the raw sensor reading plus user entered offset + // Altimeter Setting 69 2 In 1000th of an inch + // Unused 71 1 Unused field, filled with 0xFF + // Unused 72 1 Undefined + // Next 10-min Wind Speed Graph + // Pointer 73 1 Points to the next 10-minute wind speed graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next 15-min Wind Speed Graph + // Pointer 74 1 Points to the next 15-minute wind speed graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next Hourly Wind Speed Graph + // Pointer 75 1 Points to the next hour wind speed graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next Daily Wind Speed Graph + // Pointer 76 1 Points to the next daily wind speed graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next Minute Rain Graph Pointer 77 1 Points to the next minute rain graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next Rain Storm Graph Pointer 78 1 Points to the next rain storm graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 254on Vantage Vue console) + // Index to the Minute within + // an Hour 79 1 It keeps track of the minute within an hour for the rain calculation. (range from 0 to 59) + // Next Monthly Rain 80 1 Points to the next monthly rain graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next Yearly Rain 81 1 Points to the next yearly rain graph point. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Next Seasonal Rain 82 1 Points to the next seasonal rain graph point. Yearly rain always resets at the beginning of the calendar, + // but seasonal rain resets when rain season begins. For current graph point, + // just subtract 1 (range from 0 to 23 on VP/VP2 console and 0 to 24 on Vantage Vue console) + // Unused 83 2 Unused field, filled with 0x7FFF + // Unused 85 2 Unused field, filled with 0x7FFF + // Unused 87 2 Unused field, filled with 0x7FFF + // Unused 89 2 Unused field, filled with 0x7FFF + // Unused 91 2 Unused field, filled with 0x7FFF + // Unused 93 2 Unused field, filled with 0x7FFF + // "\n" = 0x0A 95 1 + // "\r" = 0x0D 96 1 + // CRC 97 2 + // Total Length 99 + public class VPLoop2Data + { + //public int BarTrend { get; private set; } + //public int IndoorHum { get; private set; } + //public double Temperature { get; private set; } + public int CurrentWindSpeed { get; private set; } + public int WindDirection { get; private set; } + //public double WindAverage { get; private set; } + //public double WindAverage2Min { get; private set; } + public int WindGust10Min { get; private set; } + public int WindGustDir { get; private set; } + //public int Humidity { get; private set; } + //public int HeatIndex { get; private set; } + //public int WindChill { get; private set; } + public int THSWindex { get; private set; } + //public int RainRate { get; private set; } + //public int UV { get; private set; } + // public int Solar { get; private set; } + //public int StormRain { get; private set; } + //public int DailyRain { get; private set; } + //public int Last15mRain { get; private set; } + //public int LastHourRain { get; private set; } + //public double DailyET { get; private set; } + //public int Last24hRain { get; private set; } + public double AbsolutePressure { get; private set; } + + // Load - disassembles the byte array passed in and loads it into local data that the accessors can use. + // Actual data is in the format to the right of the assignments - I convert it to make it easier to use + // When bytes have to be assembled into 2-byte, 16-bit numbers, I convert two bytes from the array into + // an Int16 (16-bit integer). When a single byte is all that's needed, I just convert it to an Int32. + // In the end, all integers are cast to Int32 for return. + public void Load(byte[] byteArray) + { + //BarTrend = Convert.ToInt32(byteArray[3]); + //IndoorHum = Convert.ToInt32(byteArray[11]); + //Temperature = (double)BitConverter.ToInt16(byteArray, 12) / 10; + CurrentWindSpeed = Convert.ToInt32(byteArray[14]); // Byte - unsigned byte + WindDirection = BitConverter.ToInt16(byteArray, 16); // Uint16 + //WindAverage = (double)BitConverter.ToInt16(byteArray, 18) / 10; + //WindAverage2Min = (double)BitConverter.ToInt16(byteArray, 20) / 10; + WindGust10Min = Convert.ToInt32(byteArray[22]); + WindGustDir = BitConverter.ToInt16(byteArray, 24); // Uint16 + //Humidity = Convert.ToInt32(byteArray[33]); + //HeatIndex = BitConverter.ToInt16(byteArray, 35); + //WindChill = BitConverter.ToInt16(byteArray, 37); + THSWindex = BitConverter.ToInt16(byteArray, 39); + //RainRate = BitConverter.ToInt16(byteArray, 41); // clicks per hour + //UV = Convert.ToInt32(byteArray[43]); + //Solar = BitConverter.ToInt16(byteArray, 44); + //StormRain = BitConverter.ToInt16(byteArray, 46); + //DailyRain = BitConverter.ToInt16(byteArray, 50); + //Last15mRain = BitConverter.ToInt16(byteArray, 52); + //LastHourRain = BitConverter.ToInt16(byteArray, 54); + //DailyET = (double)BitConverter.ToInt16(byteArray, 56) / 1000; + //Last24hRain = BitConverter.ToInt16(byteArray, 58); + AbsolutePressure = (double) (BitConverter.ToInt16(byteArray, 67)) / 1000; // Uint16 + } + } } diff --git a/CumulusMX/WeatherLinkDotCom.cs b/CumulusMX/WeatherLinkDotCom.cs index 0dcc3032..d7e13f70 100644 --- a/CumulusMX/WeatherLinkDotCom.cs +++ b/CumulusMX/WeatherLinkDotCom.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Reflection; -using System.Security.Cryptography; using System.Text; namespace CumulusMX @@ -24,7 +23,92 @@ public class WlHistorySensor } - // Data Structure type 11 = ISS archive record + // Data Structure type 3 & 4 - VP2 ISS archive record Type A & B + public class WlHistorySensorDataType3_4 + { + public int tx_id { get; set; } + public long ts { get; set; } + public int arch_int { get; set; } + public int rev_type { get; set; } + + public double? temp_out { get; set; } + public double? temp_out_hi { get; set; } + public double? temp_out_lo { get; set; } + public double? temp_in { get; set; } + public int? hum_in { get; set; } + public int? hum_out { get; set; } + public int? rainfall_clicks { get; set; } + public double? rainfall_in { get; set; } + public double? rainfall_mm { get; set; } + public int? rain_rate_hi_clicks { get; set; } + public double? rain_rate_hi_in { get; set; } + public double? rain_rate_hi_mm { get; set; } + public double? et { get; set; } + public double? bar { get; set; } + public int? solar_rad_avg { get; set; } + public int? solar_rad_hi { get; set; } + public double? uv_index_avg { get; set; } + public double? uv_index_hi { get; set; } + public int? wind_num_samples { get; set; } + public int? wind_speed_avg { get; set; } + public int? wind_speed_hi { get; set; } + public int? wind_dir_of_hi { get; set; } // direction code: 0=N, 1=NNE, ... 14=NW, 15=NNW + public int? wind_dir_of_prevail { get; set; } // direction code: 0=N, 1=NNE, ... 14=NW, 15=NNW + public int? moist_soil_1 { get; set; } + public int? moist_soil_2 { get; set; } + public int? moist_soil_3 { get; set; } + public int? moist_soil_4 { get; set; } + public double? temp_soil_1 { get; set; } + public double? temp_soil_2 { get; set; } + public double? temp_soil_3 { get; set; } + public double? temp_soil_4 { get; set; } + public int? wet_leaf_1 { get; set; } + public int? wet_leaf_2 { get; set; } + // public int? temp_leaf_1 { get; set; } + // public int? temp_leaf_2 { get; set; } + public double? temp_extra_1 { get; set; } + public double? temp_extra_2 { get; set; } + public double? temp_extra_3 { get; set; } + public double? temp_extra_4 { get; set; } + public int? hum_extra_1 { get; set; } + public int? hum_extra_2 { get; set; } + public int? hum_extra_3 { get; set; } + public int? hum_extra_4 { get; set; } + public int? forecast_rule { get; set; } + public string forecast_desc { get; set; } + public double? abs_press { get; set; } + public double? bar_noaa { get; set; } + public double? bar_alt { get; set; } + public double? air_density { get; set; } + public double? dew_point_out { get; set; } + public double? dew_point_in { get; set; } + public double? emc { get; set; } + public double? heat_index_out { get; set; } + public double? heat_index_in { get; set; } + public double? wind_chill { get; set; } + public double? wind_run { get; set; } + public double? deg_days_heat { get; set; } + public double? deg_days_cool { get; set; } + public double? solar_energy { get; set; } + public double? uv_dose { get; set; } + public double? thw_index { get; set; } + public double? thsw_index { get; set; } + public double? wet_bulb { get; set; } + public double? night_cloud_cover { get; set; } + public double? iss_reception { get; set; } + + public object this[string name] + { + get + { + Type myType = typeof(WlHistorySensorDataType13); + PropertyInfo myPropInfo = myType.GetProperty(name); + return myPropInfo.GetValue(this, null); + } + } + } + + // Data Structure type 11 = WeatherLink Live ISS archive record public class WlHistorySensorDataType11 { public int tx_id { get; set; } @@ -106,8 +190,7 @@ public class WlHistorySensorDataType11 public double heating_degree_days { get; set; } } - - // Data structure type 13 = Non-IIS Archive record + // Data structure type 13 = WeatherLink Live Non-IIS Archive record public class WlHistorySensorDataType13 { public int tx_id { get; set; } @@ -177,23 +260,7 @@ public object this[string name] } } - public class WlHistoryHealthType11_13 - { - public int afc { get; set; } - public int arch_int { get; set; } - public int error_packets { get; set; } - public double? et { get; set; } - public int good_packets_streak { get; set; } - public int reception { get; set; } - public int resynchs { get; set; } - public double? solar_volt_last { get; set; } - public double? supercap_volt_last { get; set; } - public int rssi { get; set; } - public uint trans_battery_flag { get; set; } - public long ts { get; set; } - public int tx_id { get; set; } - } - + // Data structure type 13 = WeatherLink Live Internal Barometer Archive record public class WlHistorySensorDataType13Baro { public int arch_int { get; set; } @@ -207,6 +274,7 @@ public class WlHistorySensorDataType13Baro } + // Data structure type 13 = WeatherLink Live Internal Temperature Archive record public class WlHistorySensorDataType13Temp { public long ts { get; set; } @@ -224,38 +292,6 @@ public class WlHistorySensorDataType13Temp public double heat_index_in { get; set; } } - - // Data structure type 15 = WeatherLink Live Health record - public class WlHistorySensorDataType15 - { - public long ts { get; set; } - public int health_version { get; set; } - public long firmware_version { get; set; } - public long bluetooth_version { get; set; } - public long radio_version { get; set; } - public long espressif_version { get; set; } - public int battery_voltage { get; set; } - public int input_voltage { get; set; } - public double uptime { get; set; } - public int bgn { get; set; } - public int network_type { get; set; } - public int ip_address_type { get; set; } - public string ip_v4_address { get; set; } - public string ip_v4_gateway { get; set; } - public string ip_v4_netmask { get; set; } - public int dns_type_used { get; set; } - public long rx_bytes { get; set; } - public long tx_bytes { get; set; } - public long local_api_queries { get; set; } - public long rapid_records_sent { get; set; } - public int? wifi_rssi { get; set; } - public double link_uptime { get; set; } - public int network_error { get; set; } - public int touchpad_wakeups { get; set; } - public long bootloader_version { get; set; } - } - - // Data structure type 17 = AirLink Archive record public class WlHistorySensorDataType17 { @@ -347,6 +383,413 @@ public class WlHistorySensorDataType18 public int? wifi_rssi { get; set; } } + // Data Structure type 24 = WeatherLink Console ISS Archive record + public class WlHistorySensorDataType24 : WlHistorySensorDataType11 + { + public double hdd { get; set; } + public double cdd { get; set; } + public int crc_errors { get; set; } + public int resyncs { get; set; } + public int packets_received_streak { get; set; } + public int packets_missed_streak { get; set; } + public int packets_received { get; set; } + public int packets_missed { get; set; } + public int freq_error_avg { get; set; } + public int freq_error_total { get; set; } + public double trans_battery_volt { get; set; } + public double spars_volt_last { get; set; } + public int spars_rpm_last { get; set; } + public double latitude { get; set; } + public double longitude { get; set; } + public double elevation { get; set; } + public double gnss_clock { get; set; } + public double gnss_fix { get; set; } + } + + // Data Strucrure Type 26 = WeatherLink Console Non-ISS Archive record + public class WlHistorySensorDataType26 : WlHistorySensorDataType13 + { + public double temp_last_volt_1 { get; set; } + public double temp_last_volt_2 { get; set; } + public double temp_last_volt_3 { get; set; } + public double temp_last_volt_4 { get; set; } + public double moist_soil_last_volt_1 { get; set; } + public double moist_soil_last_volt_2 { get; set; } + public double moist_soil_last_volt_3 { get; set; } + public double moist_soil_last_volt_4 { get; set; } + public int reception { get; set; } + public int rssi { get; set; } + public int crc_errors { get; set; } + public int resyncs { get; set; } + public int packets_received_streak { get; set; } + public int packets_missed_streak { get; set; } + public int packets_received { get; set; } + public int packets_missed { get; set; } + public int freq_error_avg { get; set; } + public int freq_error_total { get; set; } + public int trans_battery_flag { get; set; } + } + + + + + // Data Structure Type 11 = ISS Health record + // Data Structure Type 13 = Non-ISS Health Record + public class WlHealthDataType11_13 + { + public int afc { get; set; } + public int arch_int { get; set; } + public int error_packets { get; set; } + public double? et { get; set; } + public int good_packets_streak { get; set; } + public int reception { get; set; } + public int resynchs { get; set; } + public double? solar_volt_last { get; set; } + public double? supercap_volt_last { get; set; } + public int rssi { get; set; } + public int trans_battery_flag { get; set; } + public long ts { get; set; } + public int tx_id { get; set; } + } + + // Data Structure Type 15 = WeatherLink Live Health record + public class WlHealthDataType15 + { + public long ts { get; set; } + public int health_version { get; set; } + public long firmware_version { get; set; } + public long bluetooth_version { get; set; } + public long radio_version { get; set; } + public long espressif_version { get; set; } + public int battery_voltage { get; set; } + public int input_voltage { get; set; } + public double uptime { get; set; } + public int bgn { get; set; } + public int network_type { get; set; } + public int ip_address_type { get; set; } + public string ip_v4_address { get; set; } + public string ip_v4_gateway { get; set; } + public string ip_v4_netmask { get; set; } + public int dns_type_used { get; set; } + public long rx_bytes { get; set; } + public long tx_bytes { get; set; } + public long local_api_queries { get; set; } + public long rapid_records_sent { get; set; } + public int? wifi_rssi { get; set; } + public double link_uptime { get; set; } + public int network_error { get; set; } + public int touchpad_wakeups { get; set; } + public long bootloader_version { get; set; } + } + + // Data Structure Type 27 = WeatherLink Console Health record + public class WlHealthDataType27 + { + public int? health_version { get; set; } + public string console_sw_version { get; set; } + public string console_radio_version { get; set; } + public long? console_api_level { get; set; } + public int? battery_voltage { get; set; } + public int? battery_percent { get; set; } + public int? battery_condition { get; set; } + public double? battery_current { get; set; } + public double? battery_temp { get; set; } + public int? charger_plugged { get; set; } + public int? battery_status { get; set; } + public int? os_uptime { get; set; } + public int? app_uptime { get; set; } + public int? bgn { get; set; } + public int? ip_address_type { get; set; } + public string ip_v4_address { get; set; } + public string ip_v4_gateway { get; set; } + public string ip_v4_mask { get; set; } + public int? dns_type_used { get; set; } + public long? rx_kilobytes { get; set; } + public long? tx_kilobytes { get; set; } + public long? local_api_queries { get; set; } + public int? wifi_rssi { get; set; } + public long? link_uptime { get; set; } + public long? connection_uptime { get; set; } + public long? bootloader_version { get; set; } + public int? clock_source { get; set; } + public int? gnss_sip_tx_id { get; set; } + public double? free_mem { get; set; } + public double? internal_free_space { get; set; } + public double? system_free_space { get; set; } + public double? queue_kilobytes { get; set; } + public double? database_kilobytes { get; set; } + public double? battery_cycle_count { get; set; } + public string console_os_version { get; set; } + } + + + + + + public class WlCurrent + { + public int station_id { get; set; } + public long generated_at { get; set; } + + // We have no idea what data structures are going to be in here in advance = dynamic + public List sensors { get; set; } + } + + public class WlCurrentSensor + { + public int lsid { get; set; } + public int sensor_type { get; set; } + public int? data_structure_type { get; set; } + + // We have no idea what data structures are going to be in here in advance + public string[] data { get; set; } + } + + + + // Data Structure 1 (rev A) = VP2 ISS current record + // Data Structure 1 (rev B) = VP2 ISS current record (rev b just adds bar_trend which we do not use) + public class WLCurrentSensordDataType1_2 + { + public int tx_id { get; set; } + //public int bar_trend { get; set; } + public double? bar { get; set; } + public double? temp_in { get; set; } + public int? hum_in { get; set; } + public double? temp_out { get; set; } + public int? wind_speed { get; set; } + public int? wind_speed_10_min_avg { get; set; } + public int? wind_dir { get; set; } + public int? temp_extra_1 { get; set; } + public int? temp_extra_2 { get; set; } + public int? temp_extra_3 { get; set; } + public int? temp_extra_4 { get; set; } + public int? temp_extra_5 { get; set; } + public int? temp_extra_6 { get; set; } + public int? temp_extra_7 { get; set; } + public int? temp_soil_1 { get; set; } + public int? temp_soil_2 { get; set; } + public int? temp_soil_3 { get; set; } + public int? temp_soil_4 { get; set; } + //public int temp_leaf_1 { get; set; } + //public int temp_leaf_2 { get; set; } + //public int temp_leaf_3 { get; set; } + //public int temp_leaf_4 { get; set; } + public int? hum_out { get; set; } + public int? hum_extra_1 { get; set; } + public int? hum_extra_2 { get; set; } + public int? hum_extra_3 { get; set; } + public int? hum_extra_4 { get; set; } + public int? hum_extra_5 { get; set; } + public int? hum_extra_6 { get; set; } + public int? hum_extra_7 { get; set; } + public int? rain_rate_clicks { get; set; } + public double? rain_rate_in { get; set; } + public double? rain_rate_mm { get; set; } + public int? uv { get; set; } + public int? solar_rad { get; set; } + public int? rain_storm_clicks { get; set; } + public double? rain_storm_in { get; set; } + public double? rain_storm_mm { get; set; } + public int? rain_storm_start_date { get; set; } + public int? rain_day_clicks { get; set; } + public double? rain_day_in { get; set; } + public double? rain_day_mm { get; set; } + public int? rain_month_clicks { get; set; } + public double? rain_month_in { get; set; } + public double? rain_month_mm { get; set; } + public int? rain_year_clicks { get; set; } + public double? rain_year_in { get; set; } + public double? rain_year_mm { get; set; } + public double? et_day { get; set; } + public double? et_month { get; set; } + public double? et_year { get; set; } + public int? moist_soil_1 { get; set; } + public int? moist_soil_2 { get; set; } + public int? moist_soil_3 { get; set; } + public int? moist_soil_4 { get; set; } + public int? wet_leaf_1 { get; set; } + public int? wet_leaf_2 { get; set; } + public int? wet_leaf_3 { get; set; } + public int? wet_leaf_4 { get; set; } + public int? forecast_rule { get; set; } + public string forescast_desc { get; set; } + public double? dew_point { get; set; } + public double? heat_index { get; set; } + public double? wind_chill { get; set; } + public int? wind_gust_10_min { get; set; } + public long ts { get; set; } + } + + // TODO? + // Add strucure type 5 = VP2 High/Low records + + // Data Structure 10 = WeatherLink Live ISS current record + // Data Structure 23 = WeatherLink Console current record (additions to type 10 noted below) + public class WLCurrentSensorDataType10_23 + { + public int tx_id { get; set; } + public double? temp { get; set; } + public double? hum { get; set; } + public double? dew_point { get; set; } + public double wet_bulb { get; set; } + public double? heat_index { get; set; } + public double? wind_chill { get; set; } + public double? thw_index { get; set; } + public double? thsw_index { get; set; } + public double? wbgt { get; set; } // Type 23 only + public double? wind_speed_last { get; set; } + public int? wind_dir_last { get; set; } + public double? wind_speed_avg_last_1_min { get; set; } + public int? wind_dir_scalar_avg_last_1_min { get; set; } + public double? wind_speed_avg_last_2_min { get; set; } + public int wind_dir_scalar_avg_last_2_min { get; set; } + public double? wind_speed_hi_last_2_min { get; set; } + public int? wind_dir_at_hi_speed_last_2_min { get; set; } + public double? wind_speed_avg_last_10_min { get; set; } + public double wind_dir_scalar_avg_last_10_min { get; set; } + public int? wind_speed_hi_last_10_min { get; set; } + public double wind_dir_at_hi_speed_last_10_min { get; set; } + public double? wind_run_day { get; set; } // Type 23 only + public int? rain_size { get; set; } + public int? rain_rate_last_clicks { get; set; } + public double rain_rate_last_in { get; set; } + public double rain_rate_last_mm { get; set; } + public int rain_rate_hi_clicks { get; set; } + public double rain_rate_hi_in { get; set; } + public double rain_rate_hi_mm { get; set; } + public int rainfall_last_15_min_clicks { get; set; } + public double rainfall_last_15_min_in { get; set; } + public double rainfall_last_15_min_mm { get; set; } + public int rain_rate_hi_last_15_min_clicks { get; set; } + public double rain_rate_hi_last_15_min_in { get; set; } + public double rain_rate_hi_last_15_min_mm { get; set; } + public int rainfall_last_60_min_clicks { get; set; } + public double rainfall_last_60_min_in { get; set; } + public double rainfall_last_60_min_mm { get; set; } + public double rainfall_last_24_hr_clicks { get; set; } + public double rainfall_last_24_hr_in { get; set; } + public double rainfall_last_24_hr_mm { get; set; } + public double? rain_storm_clicks { get; set; } + public double rain_storm_in { get; set; } + public double rain_storm_mm { get; set; } + public long? rain_storm_start_at { get; set; } + public int? solar_rad { get; set; } + public double? solar_energy_day { get; set; } // Type 23 only + public double? et_day { get; set; } // Type 23 only + public double? et_month { get; set; } // Type 23 only + public double? et_year { get; set; } // Type 23 only + public double? uv_index { get; set; } + public double? uv_dose_day { get; set; } // Type 23 only + public double? hdd_day { get; set; } // Type 23 only + public double? cdd_day { get; set; } // Type 23 only + public int? reception_day { get; set; } // Type 23 only + public int? rssi_last { get; set; } // Type 23 only + public int? crc_errors_day { get; set; } // Type 23 only + public int? resyncs_day { get; set; } // Type 23 only + public int? packets_received_day { get; set; } // Type 23 only + public int? packets_received_streak { get; set; } // Type 23 only + public int? packets_missed_day { get; set; } // Type 23 only + public int? packets_missed_streak { get; set; } // Type 23 only + public int? packets_received_streak_hi_day { get; set; } // Type 23 only + public int? packets_missed_streak_hi_day { get; set; } // Type 23 only + public int rx_state { get; set; } + public int? freq_error_current { get; set; } // Type 23 only + public int? freq_error_total { get; set; } // Type 23 only + public int? freq_index { get; set; } // Type 23 only + public long? last_packet_received_timestamp { get; set; } // Type 23 only + public int? trans_battery_flag { get; set; } + public double? trans_battery_volt { get; set; } // Type 23 only + public double? solar_panel_volt { get; set; } // Type 23 only + public double? supercap_volt { get; set; } // Type 23 only + public double? spars_volt { get; set; } // Type 23 only + public double? spars_rpm { get; set; } // Type 23 only + public int rainfall_daily_clicks { get; set; } + public double rainfall_daily_in { get; set; } + public double rainfall_daily_mm { get; set; } + public int rainfall_monthly_clicks { get; set; } + public double rainfall_monthly_in { get; set; } + public double rainfall_monthly_mm { get; set; } + public int? rainfall_year_clicks { get; set; } + public double rainfall_year_in { get; set; } + public double rainfall_year_mm { get; set; } + public int rain_storm_last_clicks { get; set; } + public double rain_storm_last_in { get; set; } + public double rain_storm_last_mm { get; set; } + public int rain_storm_last_start_at { get; set; } + public int rain_storm_last_end_at { get; set; } + public long ts { get; set; } + } + + // Data Structure 12 = WeatherLink Live Leaf/Soil current record + // Data Structure 25 = WeatherLink console Leaf/Soil current record + public class WLCurrentSensorDataType12_25 + { + public double? temp_1 { get; set; } + public double? temp_2 { get; set; } + public double? temp_3 { get; set; } + public double? temp_4 { get; set; } + public double? moist_soil_1 { get; set; } + public double? moist_soil_2 { get; set; } + public double? moist_soil_3 { get; set; } + public double? moist_soil_4 { get; set; } + public double? wet_leaf_1 { get; set; } + public double? wet_leaf_2 { get; set; } + public int? reception_day { get; set; } // Type 25 only + public int? rssi_last { get; set; } // Type 25 only + public int? crc_errors_day { get; set; } // Type 25 only + public int? resyncs_day { get; set; } // Type 25 only + public int? packets_received_day { get; set; } // Type 25 only + public int? packets_received_streak { get; set; } // Type 25 only + public int? packets_missed_day { get; set; } // Type 25 only + public int? packets_missed_streak { get; set; } // Type 25 only + public int? packets_received_streak_hi_day { get; set; } // Type 25 only + public int? packets_missed_streak_hi_day { get; set; } // Type 25 only + public int? rx_state { get; set; } // Type 25 only + public int? freq_error_current { get; set; } // Type 25 only + public int? freq_error_total { get; set; } // Type 25 only + public int? freq_index { get; set; } // Type 25 only + public long? last_packet_received_timestamp { get; set; } // Type 25 only + public int? trans_battery_flag { get; set; } // Type 25 only + + public object this[string name] + { + get + { + Type myType = typeof(WlHistorySensorDataType13); + PropertyInfo myPropInfo = myType.GetProperty(name); + return myPropInfo.GetValue(this, null); + } + } + } + + // Data structure 12 for sensor Type 242 = WeatherLink Live Baro current record + // Data structure 19 for sensor Type 242 = WeatherLink Console Baro current record + public class WlCurrentSensorDataType12_19Baro + { + public double? bar_sea_level { get; set; } + public double? bar_trend { get; set; } + public double? bar_absolute { get; set; } + public double? bar_offset { get; set; } + public long ts { get; set; } + } + + // Data structure 12 for sensor Type 243 = WeatherLink Live Inside Temp current record + // Data Structure 21 for sensor type 365 = WeatherLink Console Inside Temp current record + public class WlCurrentSensorDataType12_21Temp + { + public double? temp_in { get; set; } + public double? hum_in { get; set; } + public double? dew_point_in { get; set; } + public double? heat_index_in { get; set; } + public double? wet_bulb_in { get; set; } // Type 21 only + public double? wbgt_in { get; set; } // Type 21 only + public long ts { get; set; } + } + + + + public class WlErrorResponse { public int code { get; set; } @@ -435,7 +878,7 @@ public WlSensor(int sensorType, int lsid, int parentId, string name, string pare // WeatherLink.com status public class WlComSystemStatus { - public WlComSystemStatusResult result {get; set;} + public WlComSystemStatusResult result { get; set; } public string ToString(bool PrintFullMessage) { diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 8b1f59ba..5375fe1b 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -13,16 +13,20 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; using System.Timers; -using SQLite; -using Timer = System.Timers.Timer; -using ServiceStack.Text; using System.Web; -using System.Threading.Tasks; +using System.Web.Compilation; + using EmbedIO.Utilities; + using FluentFTP.Helpers; -using CumulusMX; -using static System.Collections.Specialized.BitVector32; + +using ServiceStack.Text; + +using SQLite; + +using Timer = System.Timers.Timer; namespace CumulusMX { @@ -43,6 +47,7 @@ public struct TWindVec public DateTime Timestamp; } + public int DataTimeoutMins = 1; private readonly Object monthIniThreadLock = new Object(); public readonly Object yearIniThreadLock = new Object(); public readonly Object alltimeIniThreadLock = new Object(); @@ -415,6 +420,7 @@ public WeatherStation(Cumulus cumulus) //RecentDataDb = new SQLiteConnection(":memory:", true); //RecentDataDb = new SQLiteConnection(cumulus.dbfile, false); RecentDataDb = new SQLiteConnection(new SQLiteConnectionString(cumulus.dbfile, flags, false, null, null, null, null, "yyyy-MM-dd HH:mm:ss")); + CheckSqliteDatabase(false); RecentDataDb.CreateTable(); RecentDataDb.CreateTable(); RecentDataDb.CreateTable(); @@ -431,6 +437,56 @@ public WeatherStation(Cumulus cumulus) SensorReception = new Dictionary(); } + private void CheckSqliteDatabase(bool giveup) + { + bool rebuild = false; + int errorCount = 0; + try + { + cumulus.LogMessage("Checking SQLite integrity..."); + var cmd = RecentDataDb.CreateCommand("PRAGMA quick_check;"); + var res = cmd.ExecuteQueryScalars(); + + errorCount = res.Count(); + + if (errorCount == 1 && res.First() == "ok") + { + cumulus.LogMessage("SQLite integrity check OK"); + return; + } + + foreach (var row in res) + { + cumulus.LogErrorMessage("SQLite integrity check result: " + row.Replace("\n", "\n ")); + if (row == "database disk image is malformed") + rebuild = true; + } + + } + catch (Exception ex) + { + cumulus.LogErrorMessage("SQLite integrity check failed - " + ex.Message); + rebuild = true; + } + + if (rebuild || giveup) + { + 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")); + } + 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); + } + } + public void ReloadFailedMySQLCommands() { while (cumulus.MySqlFailedList.TryDequeue(out var tmp)) @@ -499,7 +555,7 @@ private void GetRainCounter() } catch (Exception E) { - cumulus.LogMessage("Error on line " + linenum + " of " + LogFile + ": " + E.Message); + cumulus.LogErrorMessage("Error on line " + linenum + " of " + LogFile + ": " + E.Message); } } @@ -897,8 +953,8 @@ public void WriteTodayFile(DateTime timestamp, bool Log) if (Log) { - cumulus.LogMessage("Writing today.ini, LastUpdateTime = " + cumulus.LastUpdateTime + " raindaystart = " + raindaystart.ToString() + " rain counter = " + - Raincounter.ToString()); + cumulus.LogMessage("Writing today.ini, LastUpdateTime = " + cumulus.LastUpdateTime + " raindaystart = " + raindaystart.ToString("F2") + " rain counter = " + + Raincounter.ToString("F2")); if (cumulus.FineOffsetStation) { @@ -1205,6 +1261,7 @@ private string FormatDateTime(string fmt, DateTime timestamp) /// Average wind speed /// public double WindAverage { get; set; } = 0; + private double WindAverageUncalibrated; /// /// Peak wind gust in last 10 minutes @@ -1507,7 +1564,7 @@ public void SecondTimer(object sender, ElapsedEventArgs e) } // send current data to web-socket every 5 seconds, unless it has already been sent within the 10 seconds - if (LastDataReadTimestamp.AddSeconds(5) < timeNow && (int)timeNow.TimeOfDay.TotalMilliseconds % 10000 <= 500) + if (LastDataReadTimestamp.AddSeconds(5) < timeNow && (int) timeNow.TimeOfDay.TotalMilliseconds % 10000 <= 500) { _ = sendWebSocketData(); } @@ -1592,8 +1649,14 @@ private string getTimeString(TimeSpan timespan, string format = "HH:mm") private void RemoveOldRecentData(DateTime ts) { var deleteTime = ts.AddDays(-7); - - RecentDataDb.Execute("delete from RecentData where Timestamp < ?", deleteTime); + try + { + RecentDataDb.Execute("delete from RecentData where Timestamp < ?", deleteTime); + } + catch (Exception ex) + { + cumulus.LogErrorMessage("RemoveOldRecentData: Failed to delete - " + ex.Message); + } } private void ClearAlarms() @@ -1680,10 +1743,10 @@ private void MinuteChanged(DateTime now) TempTotalToday += OutdoorTemperature; } - AddRecentDataWithAq(now, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, OutdoorHumidity, - Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); DoTrendValues(now); DoPressTrend("Enable Cumulus pressure trend"); + AddRecentDataWithAq(now, WindAverage, RecentMaxGust, WindLatest, Bearing, AvgBearing, OutdoorTemperature, WindChill, OutdoorDewpoint, HeatIndex, OutdoorHumidity, + Pressure, RainToday, SolarRad, UV, Raincounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate); // calculate ET just before the hour so it is included in the correct day at roll over - only affects 9am met days really if (cumulus.StationOptions.CalculatedET && now.Minute == 59) @@ -1758,7 +1821,7 @@ private void MinuteChanged(DateTime now) // We also want to kick off DoHTMLFiles if local copy is enabled else if (cumulus.FtpOptions.LocalCopyEnabled && cumulus.SynchronisedWebUpdate && (now.Minute % cumulus.UpdateInterval == 0)) { - cumulus.ftpThread = new Thread(() =>cumulus.DoHTMLFiles()); + cumulus.ftpThread = new Thread(() => cumulus.DoHTMLFiles()); cumulus.ftpThread.IsBackground = true; cumulus.ftpThread.Start(); } @@ -1778,7 +1841,7 @@ private void MinuteChanged(DateTime now) cumulus.UpdateWindGuru(now); } - if (cumulus.AWEKAS.Enabled && (now.Minute % ((double)cumulus.AWEKAS.Interval / 60) == 0) && cumulus.AWEKAS.SynchronisedUpdate && !String.IsNullOrWhiteSpace(cumulus.AWEKAS.ID)) + if (cumulus.AWEKAS.Enabled && (now.Minute % ((double) cumulus.AWEKAS.Interval / 60) == 0) && cumulus.AWEKAS.SynchronisedUpdate && !String.IsNullOrWhiteSpace(cumulus.AWEKAS.ID)) { cumulus.UpdateAwekas(now); } @@ -1863,7 +1926,7 @@ private void MinuteChanged(DateTime now) } else { - cumulus.LogMessage("Minimum data set of pressure, temperature, and wind is not available and NoSensorCheck is not enabled. Skip processing"); + cumulus.LogErrorMessage("Minimum data set of pressure, temperature, and wind is not available and NoSensorCheck is not enabled. Skip processing"); } } @@ -1918,12 +1981,6 @@ private void HourChanged(DateTime now) DoForecast("", true); } - if (now.Hour == 0) - { - ResetMidnightRain(now); - //RecalcSolarFactor(now); - } - int rollHour = Math.Abs(cumulus.GetHourInc()); if (now.Hour == rollHour) @@ -1933,6 +1990,8 @@ private void HourChanged(DateTime now) if (now.Hour == 0) { + ResetMidnightRain(now); + //RecalcSolarFactor(now); ResetSunshineHours(now); ResetMidnightTemperatures(now); } @@ -1943,7 +2002,7 @@ private void HourChanged(DateTime now) private void CheckForDataStopped() { // Check whether we have read data since the last clock minute. - if ((LastDataReadTimestamp != DateTime.MinValue) && (LastDataReadTimestamp == SavedLastDataReadTimestamp) && (LastDataReadTimestamp < DateTime.Now)) + if ((LastDataReadTimestamp != DateTime.MinValue) && (LastDataReadTimestamp == SavedLastDataReadTimestamp) && (LastDataReadTimestamp < DateTime.Now) && (DateTime.Now.Subtract(LastDataReadTimestamp) > TimeSpan.FromMinutes(DataTimeoutMins))) { // Data input appears to have has stopped if (!DataStopped) @@ -1960,7 +2019,7 @@ private void CheckForDataStopped() }*/ if (cumulus.ReportDataStoppedErrors) { - cumulus.LogMessage("*** Data input appears to have stopped"); + cumulus.LogErrorMessage("*** Data input appears to have stopped"); } } else @@ -1992,7 +2051,7 @@ private void ReadBlakeLarsenData() } catch (Exception ex) { - cumulus.LogMessage("Error reading SRsunshine.dat: " + ex.Message); + cumulus.LogErrorMessage("Error reading SRsunshine.dat: " + ex.Message); } } } @@ -2086,7 +2145,7 @@ public void CalculateEvaoptranspiration(DateTime date) public void CreateGraphDataFiles() { // Chart data for Highcharts graphs - string json = ""; + string json; for (var i = 0; i < cumulus.GraphDataFiles.Length; i++) { if (cumulus.GraphDataFiles[i].Create && cumulus.GraphDataFiles[i].CreateRequired) @@ -2111,7 +2170,7 @@ public void CreateGraphDataFiles() } catch (Exception ex) { - cumulus.LogMessage($"Error writing {cumulus.GraphDataFiles[i].LocalFileName}: {ex}"); + cumulus.LogErrorMessage($"Error writing {cumulus.GraphDataFiles[i].LocalFileName}: {ex}"); } } } @@ -2185,7 +2244,7 @@ public void CreateEodGraphDataFiles() } catch (Exception ex) { - cumulus.LogMessage($"Error writing {cumulus.GraphDataEodFiles[i].LocalFileName}: {ex}"); + cumulus.LogErrorMessage($"Error writing {cumulus.GraphDataEodFiles[i].LocalFileName}: {ex}"); } } @@ -2216,7 +2275,7 @@ public void CreateDailyGraphDataFiles() } catch (Exception ex) { - cumulus.LogMessage($"Error writing {cumulus.GraphDataFiles[i].LocalFileName}: {ex}"); + cumulus.LogErrorMessage($"Error writing {cumulus.GraphDataFiles[i].LocalFileName}: {ex}"); } } @@ -2273,16 +2332,18 @@ public string GetSolarGraphData(bool incremental, bool local, DateTime? start = for (var i = 0; i < data.Count; i++) { + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); + if (cumulus.GraphOptions.Visible.UV.IsVisible(local)) { - sbUv.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].UV.ToString(cumulus.UVFormat, InvC)}],"); + sbUv.Append($"[{jsTime},{data[i].UV.ToString(cumulus.UVFormat, InvC)}],"); } if (cumulus.GraphOptions.Visible.Solar.IsVisible(local)) { - sbSol.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{(int)data[i].SolarRad}],"); + sbSol.Append($"[{jsTime},{(int) data[i].SolarRad}],"); - sbMax.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{(int)data[i].SolarMax}],"); + sbMax.Append($"[{jsTime},{(int) data[i].SolarMax}],"); } } @@ -2341,9 +2402,11 @@ public string GetRainGraphData(bool incremental, DateTime? start = null) for (var i = 0; i < data.Count; i++) { - sbRain.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].RainToday.ToString(cumulus.RainFormat, InvC)}],"); + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); - sbRate.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].RainRate.ToString(cumulus.RainFormat, InvC)}],"); + sbRain.Append($"[{jsTime},{data[i].RainToday.ToString(cumulus.RainFormat, InvC)}],"); + + sbRate.Append($"[{jsTime},{data[i].RainRate.ToString(cumulus.RainFormat, InvC)}],"); } if (sbRain[sbRain.Length - 1] == ',') @@ -2380,13 +2443,15 @@ public string GetHumGraphData(bool incremental, bool local, DateTime? start = nu for (var i = 0; i < data.Count; i++) { + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); + if (cumulus.GraphOptions.Visible.OutHum.IsVisible(local)) { - sbOut.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].Humidity}],"); + sbOut.Append($"[{jsTime},{data[i].Humidity}],"); } if (cumulus.GraphOptions.Visible.InHum.IsVisible(local)) { - sbIn.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].IndoorHumidity}],"); + sbIn.Append($"[{jsTime},{data[i].IndoorHumidity}],"); } } @@ -2437,9 +2502,11 @@ public string GetWindDirGraphData(bool incremental, DateTime? start = null) for (var i = 0; i < data.Count; i++) { - sb.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].WindDir}],"); + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); - sbAvg.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].WindAvgDir}],"); + sb.Append($"[{jsTime},{data[i].WindDir}],"); + + sbAvg.Append($"[{jsTime},{data[i].WindAvgDir}],"); } if (sb[sb.Length - 1] == ',') @@ -2475,9 +2542,10 @@ public string GetWindGraphData(bool incremental, DateTime? start = null) for (var i = 0; i < data.Count; i++) { - sb.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].WindGust.ToString(cumulus.WindFormat, InvC)}],"); + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); + sb.Append($"[{jsTime},{data[i].WindGust.ToString(cumulus.WindFormat, InvC)}],"); - sbSpd.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].WindSpeed.ToString(cumulus.WindAvgFormat, InvC)}],"); + sbSpd.Append($"[{jsTime},{data[i].WindSpeed.ToString(cumulus.WindAvgFormat, InvC)}],"); } if (sb[sb.Length - 1] == ',') @@ -2523,7 +2591,7 @@ public string GetPressGraphData(bool incremental, DateTime? start = null) return sb.ToString(); } - public string GetTempGraphData(bool incremental, bool local, DateTime? start=null) + public string GetTempGraphData(bool incremental, bool local, DateTime? start = null) { bool append = false; var InvC = new CultureInfo(""); @@ -2551,29 +2619,31 @@ public string GetTempGraphData(bool incremental, bool local, DateTime? start=nul for (var i = 0; i < data.Count; i++) { + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); + if (cumulus.GraphOptions.Visible.InTemp.IsVisible(local)) - sbIn.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].IndoorTemp.ToString(cumulus.TempFormat, InvC)}],"); + sbIn.Append($"[{jsTime},{data[i].IndoorTemp.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.DewPoint.IsVisible(local)) - sbDew.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].DewPoint.ToString(cumulus.TempFormat, InvC)}],"); + sbDew.Append($"[{jsTime},{data[i].DewPoint.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.AppTemp.IsVisible(local)) - sbApp.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].AppTemp.ToString(cumulus.TempFormat, InvC)}],"); + sbApp.Append($"[{jsTime},{data[i].AppTemp.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.FeelsLike.IsVisible(local)) - sbFeel.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].FeelsLike.ToString(cumulus.TempFormat, InvC)}],"); + sbFeel.Append($"[{jsTime},{data[i].FeelsLike.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.WindChill.IsVisible(local)) - sbChill.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].WindChill.ToString(cumulus.TempFormat, InvC)}],"); + sbChill.Append($"[{jsTime},{data[i].WindChill.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.HeatIndex.IsVisible(local)) - sbHeat.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].HeatIndex.ToString(cumulus.TempFormat, InvC)}],"); + sbHeat.Append($"[{jsTime},{data[i].HeatIndex.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.Temp.IsVisible(local)) - sbTemp.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].OutsideTemp.ToString(cumulus.TempFormat, InvC)}],"); + sbTemp.Append($"[{jsTime},{data[i].OutsideTemp.ToString(cumulus.TempFormat, InvC)}],"); if (cumulus.GraphOptions.Visible.Humidex.IsVisible(local)) - sbHumidex.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{data[i].Humidex.ToString(cumulus.TempFormat, InvC)}],"); + sbHumidex.Append($"[{jsTime},{data[i].Humidex.ToString(cumulus.TempFormat, InvC)}],"); } if (cumulus.GraphOptions.Visible.InTemp.IsVisible(local)) @@ -2669,8 +2739,8 @@ public string GetAqGraphData(bool incremental, DateTime? start = null) // Check if we are to generate AQ data at all. Only if a primary sensor is defined and it isn't the Indoor AirLink - if (cumulus.StationOptions.PrimaryAqSensor > (int)Cumulus.PrimaryAqSensor.Undefined - && cumulus.StationOptions.PrimaryAqSensor != (int)Cumulus.PrimaryAqSensor.AirLinkIndoor) + if (cumulus.StationOptions.PrimaryAqSensor > (int) Cumulus.PrimaryAqSensor.Undefined + && cumulus.StationOptions.PrimaryAqSensor != (int) Cumulus.PrimaryAqSensor.AirLinkIndoor) { DateTime dateFrom; if (incremental) @@ -2686,17 +2756,18 @@ public string GetAqGraphData(bool incremental, DateTime? start = null) for (var i = 0; i < data.Count; i++) { + var jsTime = Utils.ToPseudoJSTime(data[i].Timestamp); var val = data[i].Pm2p5 == -1 ? "null" : data[i].Pm2p5.ToString("F1", InvC); - sb2p5.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{val}],"); + sb2p5.Append($"[{jsTime},{val}],"); // Only the AirLink and Ecowitt CO2 servers provide PM10 values at the moment - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor || - cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkIndoor || - cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.EcowittCO2) + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor || + cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkIndoor || + cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.EcowittCO2) { append = true; val = data[i].Pm10 == -1 ? "null" : data[i].Pm10.ToString("F1", InvC); - sb10.Append($"[{Utils.ToPseudoJSTime(data[i].Timestamp)},{val}],"); + sb10.Append($"[{jsTime},{val}],"); } } @@ -2721,7 +2792,7 @@ public string GetAqGraphData(bool incremental, DateTime? start = null) return sb.ToString(); } - public string GetExtraTempGraphData(bool incremental, bool local, DateTime? start = null) + public string GetExtraTempGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; var InvC = new CultureInfo(""); @@ -2735,7 +2806,7 @@ public string GetExtraTempGraphData(bool incremental, bool local, DateTime? star } */ - StringBuilder[] sbExt= new StringBuilder[cumulus.GraphOptions.Visible.ExtraTemp.Vals.Length]; + StringBuilder[] sbExt = new StringBuilder[cumulus.GraphOptions.Visible.ExtraTemp.Vals.Length]; for (var i = 0; i < cumulus.GraphOptions.Visible.ExtraTemp.Vals.Length; i++) { @@ -2750,12 +2821,16 @@ public string GetExtraTempGraphData(bool incremental, bool local, DateTime? star { dateFrom = start ?? cumulus.GraphDataFiles[13].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] +1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -2808,10 +2883,11 @@ public string GetExtraTempGraphData(bool incremental, bool local, DateTime? star { // entry is from required period var temp = 0.0; + var jsTime = Utils.ToPseudoJSTime(entrydate); for (var i = 0; i < cumulus.GraphOptions.Visible.ExtraTemp.Vals.Length; i++) { if (cumulus.GraphOptions.Visible.ExtraTemp.ValVisible(i, local) && double.TryParse(st[i + 2], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString(cumulus.TempFormat, InvC)}],"); + sbExt[i].Append($"[{jsTime},{temp.ToString(cumulus.TempFormat, InvC)}],"); } } } @@ -2862,7 +2938,7 @@ public string GetExtraTempGraphData(bool incremental, bool local, DateTime? star return sb.ToString(); } - public string GetExtraDewPointGraphData(bool incremental, bool local, DateTime? start = null) + public string GetExtraDewPointGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; var InvC = new CultureInfo(""); @@ -2891,12 +2967,16 @@ public string GetExtraDewPointGraphData(bool incremental, bool local, DateTime? { dateFrom = start ?? cumulus.GraphDataFiles[15].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -2948,11 +3028,12 @@ public string GetExtraDewPointGraphData(bool incremental, bool local, DateTime? if (entrydate > dateFrom) { // entry is from required period + var jsTime = Utils.ToPseudoJSTime(entrydate); var temp = 0.0; for (var i = 0; i < cumulus.GraphOptions.Visible.ExtraDewPoint.Vals.Length; i++) { if (cumulus.GraphOptions.Visible.ExtraDewPoint.ValVisible(i, local) && double.TryParse(st[i + 22], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString(cumulus.TempFormat, InvC)}],"); + sbExt[i].Append($"[{jsTime},{temp.ToString(cumulus.TempFormat, InvC)}],"); } } } @@ -3003,10 +3084,9 @@ public string GetExtraDewPointGraphData(bool incremental, bool local, DateTime? return sb.ToString(); } - public string GetExtraHumGraphData(bool incremental, bool local, DateTime? start = null) + public string GetExtraHumGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; - var InvC = new CultureInfo(""); var sb = new StringBuilder("{", 10240); /* returns data in the form of an object with properties for each data series @@ -3033,12 +3113,16 @@ public string GetExtraHumGraphData(bool incremental, bool local, DateTime? start { dateFrom = start ?? cumulus.GraphDataFiles[14].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -3091,10 +3175,11 @@ public string GetExtraHumGraphData(bool incremental, bool local, DateTime? start { // entry is from required period var temp = 0; + var jsTime = Utils.ToPseudoJSTime(entrydate); for (var i = 0; i < cumulus.GraphOptions.Visible.ExtraHum.Vals.Length; i++) { if (cumulus.GraphOptions.Visible.ExtraHum.ValVisible(i, local) && int.TryParse(st[i + 12], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp}],"); + sbExt[i].Append($"[{jsTime},{temp}],"); } } } @@ -3145,7 +3230,7 @@ public string GetExtraHumGraphData(bool incremental, bool local, DateTime? start return sb.ToString(); } - public string GetSoilTempGraphData(bool incremental, bool local, DateTime? start = null) + public string GetSoilTempGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; var InvC = new CultureInfo(""); @@ -3174,12 +3259,16 @@ public string GetSoilTempGraphData(bool incremental, bool local, DateTime? start { dateFrom = start ?? cumulus.GraphDataFiles[16].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -3232,15 +3321,17 @@ public string GetSoilTempGraphData(bool incremental, bool local, DateTime? start { // entry is from required period var temp = 0.0; + var jsTime = Utils.ToPseudoJSTime(entrydate); + for (var i = 0; i < 4; i++) { if (cumulus.GraphOptions.Visible.SoilTemp.ValVisible(i, local) && double.TryParse(st[i + 32], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString(cumulus.TempFormat, InvC)}],"); + sbExt[i].Append($"[{jsTime},{temp.ToString(cumulus.TempFormat, InvC)}],"); } for (var i = 4; i < 16; i++) { if (cumulus.GraphOptions.Visible.SoilTemp.ValVisible(i, local) && double.TryParse(st[i + 40], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString(cumulus.TempFormat, InvC)}],"); + sbExt[i].Append($"[{jsTime},{temp.ToString(cumulus.TempFormat, InvC)}],"); } } } @@ -3291,10 +3382,9 @@ public string GetSoilTempGraphData(bool incremental, bool local, DateTime? start return sb.ToString(); } - public string GetSoilMoistGraphData(bool incremental, bool local, DateTime? start = null) + public string GetSoilMoistGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; - var InvC = new CultureInfo(""); var sb = new StringBuilder("{", 10240); /* returns data in the form of an object with properties for each data series @@ -3320,12 +3410,16 @@ public string GetSoilMoistGraphData(bool incremental, bool local, DateTime? star { dateFrom = start ?? cumulus.GraphDataFiles[17].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -3378,15 +3472,17 @@ public string GetSoilMoistGraphData(bool incremental, bool local, DateTime? star { // entry is from required period var temp = 0; + var jsTime = Utils.ToPseudoJSTime(entrydate); + for (var i = 0; i < 4; i++) { if (cumulus.GraphOptions.Visible.SoilMoist.ValVisible(i, local) && int.TryParse(st[i + 36], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp}],"); + sbExt[i].Append($"[{jsTime},{temp}],"); } for (var i = 4; i < 16; i++) { if (cumulus.GraphOptions.Visible.SoilMoist.ValVisible(i, local) && int.TryParse(st[i + 52], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp}],"); + sbExt[i].Append($"[{jsTime},{temp}],"); } } } @@ -3437,10 +3533,9 @@ public string GetSoilMoistGraphData(bool incremental, bool local, DateTime? star return sb.ToString(); } - public string GetLeafWetnessGraphData(bool incremental, bool local, DateTime? start = null) + public string GetLeafWetnessGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; - var InvC = new CultureInfo(""); var sb = new StringBuilder("{", 10240); /* returns data in the form of an object with properties for each data series @@ -3466,12 +3561,16 @@ public string GetLeafWetnessGraphData(bool incremental, bool local, DateTime? st { dateFrom = start ?? cumulus.GraphDataFiles[20].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -3524,10 +3623,12 @@ public string GetLeafWetnessGraphData(bool incremental, bool local, DateTime? st { // entry is from required period var temp = 0.0; + var jsTime = Utils.ToPseudoJSTime(entrydate); + for (var i = 0; i < 2; i++) { if (cumulus.GraphOptions.Visible.LeafWetness.ValVisible(i, local) && double.TryParse(st[i + 42], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp}],"); + sbExt[i].Append($"[{jsTime},{temp}],"); } } } @@ -3578,7 +3679,7 @@ public string GetLeafWetnessGraphData(bool incremental, bool local, DateTime? st return sb.ToString(); } - public string GetUserTempGraphData(bool incremental, bool local, DateTime? start = null) + public string GetUserTempGraphData(bool incremental, bool local, DateTime? start = null, DateTime? end = null) { bool append = false; var InvC = new CultureInfo(""); @@ -3607,12 +3708,16 @@ public string GetUserTempGraphData(bool incremental, bool local, DateTime? start { dateFrom = start ?? cumulus.GraphDataFiles[18].LastDataTime; } + else if (start.HasValue && end.HasValue) + { + dateFrom = start.Value; + } else { dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); } - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var dateto = end ?? DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; // get the log file name to start @@ -3665,10 +3770,12 @@ public string GetUserTempGraphData(bool incremental, bool local, DateTime? start { // entry is from required period var temp = 0.0; + var jsTime = Utils.ToPseudoJSTime(entrydate); + for (var i = 0; i < cumulus.GraphOptions.Visible.UserTemp.Vals.Length; i++) { if (cumulus.GraphOptions.Visible.UserTemp.ValVisible(i, local) && double.TryParse(st[i + 76], out temp)) - sbExt[i].Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString(cumulus.TempFormat, InvC)}],"); + sbExt[i].Append($"[{jsTime},{temp.ToString(cumulus.TempFormat, InvC)}],"); } } } @@ -3733,217 +3840,893 @@ public string GetCo2SensorGraphData(bool incremental, bool local, DateTime? star } */ - var sbCo2 = new StringBuilder($"\"CO2\":["); - var sbCo2Avg = new StringBuilder($"\"CO2 Average\":["); - var sbPm25 = new StringBuilder($"\"PM2.5\":["); - var sbPm25Avg = new StringBuilder($"\"PM 2.5 Average\":["); - var sbPm10 = new StringBuilder($"\"PM 10\":["); - var sbPm10Avg = new StringBuilder($"\"PM 10 Average\":["); - var sbTemp = new StringBuilder($"\"Temperature\":["); - var sbHum = new StringBuilder($"\"Humidity\":["); + var sbCo2 = new StringBuilder($"\"CO2\":["); + var sbCo2Avg = new StringBuilder($"\"CO2 Average\":["); + var sbPm25 = new StringBuilder($"\"PM2.5\":["); + var sbPm25Avg = new StringBuilder($"\"PM 2.5 Average\":["); + var sbPm10 = new StringBuilder($"\"PM 10\":["); + var sbPm10Avg = new StringBuilder($"\"PM 10 Average\":["); + var sbTemp = new StringBuilder($"\"Temperature\":["); + var sbHum = new StringBuilder($"\"Humidity\":["); + + + var finished = false; + var entrydate = new DateTime(); + DateTime dateFrom; + if (incremental) + { + dateFrom = start ?? cumulus.GraphDataFiles[19].LastDataTime; + } + else + { + dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); + } + + var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); + var fileDate = dateFrom; + + // get the log file name to start + var logFile = cumulus.GetExtraLogFileName(dateFrom); + + + // 0 Date in the form dd/mm/yy (the slash may be replaced by a dash in some cases) + // 1 Current time - hh:mm + // 2-11 Temperature 1-10 + // 12-21 Humidity 1-10 + // 22-31 Dew point 1-10 + // 32-35 Soil temp 1-4 + // 36-39 Soil moisture 1-4 + // 40-41 Leaf temp 1-2 + // 42-43 Leaf wetness 1-2 + // 44-55 Soil temp 5-16 + // 56-67 Soil moisture 5-16 + // 68-71 Air quality 1-4 + // 72-75 Air quality avg 1-4 + // 76-83 User temperature 1-8 + // 84 CO2 + // 85 CO2 avg + // 86 CO2 pm2.5 + // 87 CO2 pm2.5 avg + // 88 CO2 pm10 + // 89 CO2 pm10 avg + // 90 CO2 temp + // 91 CO2 hum + + while (!finished) + { + if (File.Exists(logFile)) + { + int linenum = 0; + int errorCount = 0; + double temp; + int tempInt; + + try + { + var lines = File.ReadAllLines(logFile); + + foreach (var line in lines) + { + try + { + // process each record in the file + linenum++; + var st = new List(Regex.Split(line, CultureInfo.CurrentCulture.TextInfo.ListSeparator)); + entrydate = Utils.ddmmyyhhmmStrToDate(st[0], st[1]); + + if (entrydate > dateFrom) + { + var jsTime = Utils.ToPseudoJSTime(entrydate); + + if (cumulus.GraphOptions.Visible.CO2Sensor.CO2.IsVisible(local) && double.TryParse(st[84], out temp)) + sbCo2.Append($"[{jsTime},{temp.ToString("F1", InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.CO2Avg.IsVisible(local) && double.TryParse(st[85], out temp)) + sbCo2Avg.Append($"[{jsTime},{temp.ToString("F1", InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25.IsVisible(local) && double.TryParse(st[86], out temp)) + sbPm25.Append($"[{jsTime},{temp.ToString("F1", InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25Avg.IsVisible(local) && double.TryParse(st[87], out temp)) + sbPm25Avg.Append($"[{jsTime},{temp.ToString("F1", InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10.IsVisible(local) && double.TryParse(st[88], out temp)) + sbPm10.Append($"[{jsTime},{temp.ToString("F1", InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10Avg.IsVisible(local) && double.TryParse(st[89], out temp)) + sbPm10Avg.Append($"[{jsTime},{temp.ToString("F1", InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.Temp.IsVisible(local) && double.TryParse(st[90], out temp)) + sbTemp.Append($"[{jsTime},{temp.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.CO2Sensor.Hum.IsVisible(local) && int.TryParse(st[91], out tempInt)) + sbHum.Append($"[{jsTime},{tempInt}],"); + } + } + catch (Exception ex) + { + errorCount++; + cumulus.LogErrorMessage($"GetCo2SensorGraphData: Error at line {linenum} of {logFile}. Error - {ex.Message}"); + if (errorCount > 10) + { + cumulus.LogMessage($"GetCo2SensorGraphData: More than 10 errors in the file {logFile}, aborting processing"); + finished = true; + break; + } + } + } + } + catch (Exception ex) + { + cumulus.LogErrorMessage($"GetCo2SensorGraphData: Error reading {logFile}. Error - {ex.Message}"); + } + } + + if (entrydate >= dateto || fileDate > dateto) + { + finished = true; + } + else + { + fileDate = fileDate.AddMonths(1); + logFile = cumulus.GetExtraLogFileName(fileDate); + } + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.CO2.IsVisible(local)) + { + if (sbCo2[sbCo2.Length - 1] == ',') + sbCo2.Length--; + + sbCo2.Append("]"); + sb.Append(sbCo2); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.CO2Avg.IsVisible(local)) + { + if (sbCo2Avg[sbCo2Avg.Length - 1] == ',') + sbCo2Avg.Length--; + + sbCo2Avg.Append("]"); + sb.Append((append ? "," : "") + sbCo2Avg); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25.IsVisible(local)) + { + if (sbPm25[sbPm25.Length - 1] == ',') + sbPm25.Length--; + + sbPm25.Append("]"); + sb.Append((append ? "," : "") + sbPm25); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25Avg.IsVisible(local)) + { + if (sbPm25Avg[sbPm25Avg.Length - 1] == ',') + sbPm25Avg.Length--; + + sbPm25Avg.Append("]"); + sb.Append((append ? "," : "") + sbPm25Avg); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10.IsVisible(local)) + { + if (sbPm10[sbPm10.Length - 1] == ',') + sbPm10.Length--; + + sbPm10.Append("]"); + sb.Append((append ? "," : "") + sbPm10); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10Avg.IsVisible(local)) + { + if (sbPm10Avg[sbPm10Avg.Length - 1] == ',') + sbPm10Avg.Length--; + + sbPm10Avg.Append("]"); + sb.Append((append ? "," : "") + sbPm10Avg); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.Temp.IsVisible(local)) + { + if (sbTemp[sbTemp.Length - 1] == ',') + sbTemp.Length--; + + sbTemp.Append("]"); + sb.Append((append ? "," : "") + sbTemp); + append = true; + } + + if (cumulus.GraphOptions.Visible.CO2Sensor.Hum.IsVisible(local)) + { + if (sbHum[sbHum.Length - 1] == ',') + sbHum.Length--; + + sbHum.Append("]"); + sb.Append((append ? "," : "") + sbHum); + } + + sb.Append('}'); + return sb.ToString(); + } + + + public string GetIntervalTempGraphData(bool local, DateTime? start = null, DateTime? end = null) + { + bool append = false; + var InvC = new CultureInfo(""); + var sb = new StringBuilder("{", 10240); + var sbIn = new StringBuilder("\"intemp\":["); + var sbDew = new StringBuilder("\"dew\":["); + var sbApp = new StringBuilder("\"apptemp\":["); + var sbFeel = new StringBuilder("\"feelslike\":["); + var sbChill = new StringBuilder("\"wchill\":["); + var sbHeat = new StringBuilder("\"heatindex\":["); + var sbTemp = new StringBuilder("\"temp\":["); + var sbHumidex = new StringBuilder("\"humidex\":["); + + var dateFrom = start ?? cumulus.RecordsBeganDateTime; + var dateTo = end ?? DateTime.Now.Date; + dateTo = dateTo.AddDays(1); + + var fileDate = dateFrom; + var logFile = cumulus.GetLogFileName(fileDate); + + var finished = false; + + while (!finished) + { + if (File.Exists(logFile)) + { + cumulus.LogDebugMessage($"GetIntervalTempGraphData: Processing log file - {logFile}"); + var linenum = 0; + try + { + var logfile = File.ReadAllLines(logFile); + + foreach (var line in logfile) + { + // process each record in the file + linenum++; + + var rec = ParseLogFileRec(line, true); + + if (rec.Date < dateFrom) + continue; + + if (rec.Date > dateTo) + { + finished = true; + cumulus.LogDebugMessage("GetIntervalTempGraphData: Finished processing the log files"); + break; + } + + var jsTime = Utils.ToPseudoJSTime(rec.Date); + + if (cumulus.GraphOptions.Visible.InTemp.IsVisible(local)) + sbIn.Append($"[{jsTime},{rec.IndoorTemperature.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.DewPoint.IsVisible(local)) + sbDew.Append($"[{jsTime},{rec.OutdoorDewpoint.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.AppTemp.IsVisible(local)) + sbApp.Append($"[{jsTime},{rec.ApparentTemperature.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.FeelsLike.IsVisible(local)) + sbFeel.Append($"[{jsTime},{rec.FeelsLike.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.WindChill.IsVisible(local)) + sbChill.Append($"[{jsTime},{rec.WindChill.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.HeatIndex.IsVisible(local)) + sbHeat.Append($"[{jsTime},{rec.HeatIndex.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.Temp.IsVisible(local)) + sbTemp.Append($"[{jsTime},{rec.OutdoorTemperature.ToString(cumulus.TempFormat, InvC)}],"); + + if (cumulus.GraphOptions.Visible.Humidex.IsVisible(local)) + sbHumidex.Append($"[{jsTime},{rec.Humidex.ToString(cumulus.TempFormat, InvC)}],"); + } + + } + catch (Exception e) + { + cumulus.LogErrorMessage($"GetIntervalTempGraphData: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogMessage("Please edit the file to correct the error"); + } + } + else + { + cumulus.LogDebugMessage($"GetIntervalTempGraphData: Log file not found - {logFile}"); + } + + cumulus.LogDebugMessage($"GetIntervalTempGraphData: Finished processing log file - {logFile}"); + + if (!finished) + { + fileDate = fileDate.AddMonths(1); + + if (fileDate.Year > dateTo.Year || (fileDate.Year == dateTo.Year && fileDate.Month > dateTo.Month)) + finished = true; + + logFile = cumulus.GetLogFileName(fileDate); + } + } + + if (cumulus.GraphOptions.Visible.InTemp.IsVisible(local)) + { + if (sbIn[sbIn.Length - 1] == ',') + sbIn.Length--; + + sbIn.Append(']'); + sb.Append(sbIn); + append = true; + } + + if (cumulus.GraphOptions.Visible.DewPoint.IsVisible(local)) + { + if (sbDew[sbDew.Length - 1] == ',') + sbDew.Length--; + + sbDew.Append(']'); + sb.Append((append ? "," : "") + sbDew); + append = true; + } + + if (cumulus.GraphOptions.Visible.AppTemp.IsVisible(local)) + { + if (sbApp[sbApp.Length - 1] == ',') + sbApp.Length--; + + sbApp.Append(']'); + sb.Append((append ? "," : "") + sbApp); + append = true; + } + + if (cumulus.GraphOptions.Visible.FeelsLike.IsVisible(local)) + { + if (sbFeel[sbFeel.Length - 1] == ',') + sbFeel.Length--; + + sbFeel.Append(']'); + sb.Append((append ? "," : "") + sbFeel); + append = true; + } + + if (cumulus.GraphOptions.Visible.WindChill.IsVisible(local)) + { + if (sbChill[sbChill.Length - 1] == ',') + sbChill.Length--; + + sbChill.Append(']'); + sb.Append((append ? "," : "") + sbChill); + append = true; + } + + if (cumulus.GraphOptions.Visible.HeatIndex.IsVisible(local)) + { + if (sbHeat[sbHeat.Length - 1] == ',') + sbHeat.Length--; + + sbHeat.Append(']'); + sb.Append((append ? "," : "") + sbHeat); + append = true; + } + + if (cumulus.GraphOptions.Visible.Temp.IsVisible(local)) + { + if (sbTemp[sbTemp.Length - 1] == ',') + sbTemp.Length--; + + sbTemp.Append(']'); + sb.Append((append ? "," : "") + sbTemp); + append = true; + } + + if (cumulus.GraphOptions.Visible.Humidex.IsVisible(local)) + { + if (sbHumidex[sbHumidex.Length - 1] == ',') + sbHumidex.Length--; + + sbHumidex.Append(']'); + sb.Append((append ? "," : "") + sbHumidex); + } + + sb.Append('}'); + return sb.ToString(); + } + + public string GetIntervalHumGraphData(bool local, DateTime? start = null, DateTime? end = null) + { + var sb = new StringBuilder("{", 10240); + var sbOut = new StringBuilder("\"hum\":["); + var sbIn = new StringBuilder("\"inhum\":["); + + var dateFrom = start ?? cumulus.RecordsBeganDateTime; + var dateTo = end ?? DateTime.Now.Date; + dateTo = dateTo.AddDays(1); + + var fileDate = dateFrom; + var logFile = cumulus.GetLogFileName(fileDate); + + var finished = false; + + + while (!finished) + { + if (File.Exists(logFile)) + { + cumulus.LogDebugMessage($"GetIntervalHumGraphData: Processing log file - {logFile}"); + var linenum = 0; + try + { + var logfile = File.ReadAllLines(logFile); + + foreach (var line in logfile) + { + // process each record in the file + linenum++; + + var rec = ParseLogFileRec(line, true); + + if (rec.Date < dateFrom) + continue; + + if (rec.Date > dateTo) + { + finished = true; + cumulus.LogDebugMessage("GetIntervalHumGraphData: Finished processing the log files"); + break; + } + + var jsTime = Utils.ToPseudoJSTime(rec.Date); + + if (cumulus.GraphOptions.Visible.OutHum.IsVisible(local)) + { + sbOut.Append($"[{jsTime},{rec.OutdoorHumidity}],"); + } + if (cumulus.GraphOptions.Visible.InHum.IsVisible(local)) + { + sbIn.Append($"[{jsTime},{rec.IndoorHumidity}],"); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"GetIntervalHumGraphData: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogMessage("Please edit the file to correct the error"); + } + } + else + { + cumulus.LogDebugMessage($"GetIntervalHumGraphData: Log file not found - {logFile}"); + } + + cumulus.LogDebugMessage($"GetIntervalHumGraphData: Finished processing log file - {logFile}"); + + if (!finished) + { + fileDate = fileDate.AddMonths(1); + + if (fileDate.Year > dateTo.Year || (fileDate.Year == dateTo.Year && fileDate.Month > dateTo.Month)) + finished = true; + + logFile = cumulus.GetLogFileName(fileDate); + } + } + + + if (cumulus.GraphOptions.Visible.OutHum.IsVisible(local)) + { + if (sbOut[sbOut.Length - 1] == ',') + sbOut.Length--; + + sbOut.Append(']'); + + sb.Append(sbOut); + } + + if (cumulus.GraphOptions.Visible.InHum.IsVisible(local)) + { + if (sbIn[sbIn.Length - 1] == ',') + sbIn.Length--; + + sbIn.Append(']'); + + if (cumulus.GraphOptions.Visible.OutHum.IsVisible(local)) + sb.Append(','); + + sb.Append(sbIn); + } + + sb.Append('}'); + return sb.ToString(); + + } + + public string GetIntervalSolarGraphData(bool local, DateTime? start = null, DateTime? end = null) + { + var InvC = new CultureInfo(""); + var sb = new StringBuilder("{"); + var sbUv = new StringBuilder("\"UV\":["); + var sbSol = new StringBuilder("\"SolarRad\":["); + var sbMax = new StringBuilder("\"CurrentSolarMax\":["); + + var dateFrom = start ?? cumulus.RecordsBeganDateTime; + var dateTo = end ?? DateTime.Now.Date; + dateTo = dateTo.AddDays(1); + + var fileDate = dateFrom; + var logFile = cumulus.GetLogFileName(fileDate); + + var finished = false; + + + while (!finished) + { + if (File.Exists(logFile)) + { + cumulus.LogDebugMessage($"GetIntervalSolarGraphData: Processing log file - {logFile}"); + var linenum = 0; + try + { + var logfile = File.ReadAllLines(logFile); + + foreach (var line in logfile) + { + // process each record in the file + linenum++; + + var rec = ParseLogFileRec(line, true); + + if (rec.Date < dateFrom) + continue; + + if (rec.Date > dateTo) + { + finished = true; + cumulus.LogDebugMessage("GetIntervalSolarGraphData: Finished processing the log files"); + break; + } + + var jsTime = Utils.ToPseudoJSTime(rec.Date); + + if (cumulus.GraphOptions.Visible.UV.IsVisible(local)) + { + sbUv.Append($"[{jsTime},{rec.UV.ToString(cumulus.UVFormat, InvC)}],"); + } + + if (cumulus.GraphOptions.Visible.Solar.IsVisible(local)) + { + sbSol.Append($"[{jsTime},{(int) rec.SolarRad}],"); + + sbMax.Append($"[{jsTime},{(int) rec.CurrentSolarMax}],"); + } + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"GetIntervalSolarGraphData: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogMessage("Please edit the file to correct the error"); + } + } + else + { + cumulus.LogDebugMessage($"GetIntervalSolarGraphData: Log file not found - {logFile}"); + } + + cumulus.LogDebugMessage($"GetIntervalSolarGraphData: Finished processing log file - {logFile}"); + + if (!finished) + { + fileDate = fileDate.AddMonths(1); + + if (fileDate.Year > dateTo.Year || (fileDate.Year == dateTo.Year && fileDate.Month > dateTo.Month)) + finished = true; + + logFile = cumulus.GetLogFileName(fileDate); + } + } + + if (cumulus.GraphOptions.Visible.UV.IsVisible(local)) + { + if (sbUv[sbUv.Length - 1] == ',') + sbUv.Length--; + + sbUv.Append(']'); + sb.Append(sbUv); + } + if (cumulus.GraphOptions.Visible.Solar.IsVisible(local)) + { + if (sbSol[sbSol.Length - 1] == ',') + { + sbSol.Length--; + sbMax.Length--; + } + + sbSol.Append(']'); + sbMax.Append(']'); + if (cumulus.GraphOptions.Visible.UV.IsVisible(local)) + { + sb.Append(','); + } + sb.Append(sbSol); + sb.Append(','); + sb.Append(sbMax); + } + + sb.Append('}'); + return sb.ToString(); + + } + + public string GetIntervaPressGraphData(bool local, DateTime? start = null, DateTime? end = null) + { + var InvC = new CultureInfo(""); + StringBuilder sb = new StringBuilder("{\"press\":["); + + var dateFrom = start ?? cumulus.RecordsBeganDateTime; + var dateTo = end ?? DateTime.Now.Date; + dateTo = dateTo.AddDays(1); + + var fileDate = dateFrom; + var logFile = cumulus.GetLogFileName(fileDate); + + var finished = false; + + + while (!finished) + { + if (File.Exists(logFile)) + { + cumulus.LogDebugMessage($"GetIntervaPressGraphData: Processing log file - {logFile}"); + var linenum = 0; + try + { + var logfile = File.ReadAllLines(logFile); + + foreach (var line in logfile) + { + // process each record in the file + linenum++; + + var rec = ParseLogFileRec(line, true); + + if (rec.Date < dateFrom) + continue; + + if (rec.Date > dateTo) + { + finished = true; + cumulus.LogDebugMessage("GetIntervaPressGraphData: Finished processing the log files"); + break; + } + + sb.Append($"[{Utils.ToPseudoJSTime(rec.Date)},{rec.Pressure.ToString(cumulus.PressFormat, InvC)}],"); + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"GetIntervaPressGraphData: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogMessage("Please edit the file to correct the error"); + } + } + else + { + cumulus.LogDebugMessage($"GetIntervaPressGraphData: Log file not found - {logFile}"); + } + + cumulus.LogDebugMessage($"GetIntervaPressGraphData: Finished processing log file - {logFile}"); + + if (!finished) + { + fileDate = fileDate.AddMonths(1); + + if (fileDate.Year > dateTo.Year || (fileDate.Year == dateTo.Year && fileDate.Month > dateTo.Month)) + finished = true; + + logFile = cumulus.GetLogFileName(fileDate); + } + } + + if (sb[sb.Length - 1] == ',') + sb.Length--; + + sb.Append("]}"); + return sb.ToString(); + + } + + public string GetIntervalWindGraphData(bool local, DateTime? start = null, DateTime? end = null) + { + var InvC = new CultureInfo(""); + var sb = new StringBuilder("{\"wgust\":["); + var sbSpd = new StringBuilder("\"wspeed\":["); + var sbBrg = new StringBuilder("\"bearing\":["); + var sbAvgBrg = new StringBuilder("\"avgbearing\":["); - var finished = false; - var entrydate = new DateTime(); - DateTime dateFrom; - if (incremental) - { - dateFrom = start ?? cumulus.GraphDataFiles[19].LastDataTime; - } - else - { - dateFrom = DateTime.Now.AddHours(-cumulus.GraphHours); - } + var dateFrom = start ?? cumulus.RecordsBeganDateTime; + var dateTo = end ?? DateTime.Now.Date; + dateTo = dateTo.AddDays(1); - var dateto = DateTime.Now.AddMinutes(-(cumulus.logints[cumulus.DataLogInterval] + 1)); var fileDate = dateFrom; + var logFile = cumulus.GetLogFileName(fileDate); - // get the log file name to start - var logFile = cumulus.GetExtraLogFileName(dateFrom); - + var finished = false; - // 0 Date in the form dd/mm/yy (the slash may be replaced by a dash in some cases) - // 1 Current time - hh:mm - // 2-11 Temperature 1-10 - // 12-21 Humidity 1-10 - // 22-31 Dew point 1-10 - // 32-35 Soil temp 1-4 - // 36-39 Soil moisture 1-4 - // 40-41 Leaf temp 1-2 - // 42-43 Leaf wetness 1-2 - // 44-55 Soil temp 5-16 - // 56-67 Soil moisture 5-16 - // 68-71 Air quality 1-4 - // 72-75 Air quality avg 1-4 - // 76-83 User temperature 1-8 - // 84 CO2 - // 85 CO2 avg - // 86 CO2 pm2.5 - // 87 CO2 pm2.5 avg - // 88 CO2 pm10 - // 89 CO2 pm10 avg - // 90 CO2 temp - // 91 CO2 hum while (!finished) { if (File.Exists(logFile)) { - int linenum = 0; - int errorCount = 0; - double temp; - int tempInt; - + cumulus.LogDebugMessage($"GetIntervalWindGraphData: Processing log file - {logFile}"); + var linenum = 0; try { - var lines = File.ReadAllLines(logFile); + var logfile = File.ReadAllLines(logFile); - foreach (var line in lines) + foreach (var line in logfile) { - try - { - // process each record in the file - linenum++; - var st = new List(Regex.Split(line, CultureInfo.CurrentCulture.TextInfo.ListSeparator)); - entrydate = Utils.ddmmyyhhmmStrToDate(st[0], st[1]); + // process each record in the file + linenum++; - if (entrydate > dateFrom) - { - if (cumulus.GraphOptions.Visible.CO2Sensor.CO2.IsVisible(local) && double.TryParse(st[84], out temp)) - sbCo2.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString("F1", InvC)}],"); + var rec = ParseLogFileRec(line, true); - if (cumulus.GraphOptions.Visible.CO2Sensor.CO2Avg.IsVisible(local) && double.TryParse(st[85], out temp)) - sbCo2Avg.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString("F1", InvC)}],"); + if (rec.Date < dateFrom) + continue; - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25.IsVisible(local) && double.TryParse(st[86], out temp)) - sbPm25.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString("F1", InvC)}],"); + if (rec.Date > dateTo) + { + finished = true; + cumulus.LogDebugMessage("GetIntervalWindGraphData: Finished processing the log files"); + break; + } - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25Avg.IsVisible(local) && double.TryParse(st[87], out temp)) - sbPm25Avg.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString("F1", InvC)}],"); + var jsTime = Utils.ToPseudoJSTime(rec.Date); - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10.IsVisible(local) && double.TryParse(st[88], out temp)) - sbPm10.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString("F1", InvC)}],"); + sb.Append($"[{jsTime},{rec.RecentMaxGust.ToString(cumulus.WindFormat, InvC)}],"); - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10Avg.IsVisible(local) && double.TryParse(st[89], out temp)) - sbPm10Avg.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString("F1", InvC)}],"); + sbSpd.Append($"[{jsTime},{rec.WindAverage.ToString(cumulus.WindAvgFormat, InvC)}],"); - if (cumulus.GraphOptions.Visible.CO2Sensor.Temp.IsVisible(local) && double.TryParse(st[90], out temp)) - sbTemp.Append($"[{Utils.ToPseudoJSTime(entrydate)},{temp.ToString(cumulus.TempFormat, InvC)}],"); + sbBrg.Append($"[{jsTime},{rec.Bearing}],"); - if (cumulus.GraphOptions.Visible.CO2Sensor.Hum.IsVisible(local) && int.TryParse(st[91], out tempInt)) - sbHum.Append($"[{Utils.ToPseudoJSTime(entrydate)},{tempInt}],"); - } - } - catch (Exception ex) - { - errorCount++; - cumulus.LogErrorMessage($"GetCo2SensorGraphData: Error at line {linenum} of {logFile}. Error - {ex.Message}"); - if (errorCount > 10) - { - cumulus.LogMessage($"GetCo2SensorGraphData: More than 10 errors in the file {logFile}, aborting processing"); - finished = true; - break; - } - } + sbAvgBrg.Append($"[{jsTime},{rec.AvgBearing}],"); } } - catch (Exception ex) + catch (Exception e) { - cumulus.LogErrorMessage($"GetCo2SensorGraphData: Error reading {logFile}. Error - {ex.Message}"); + cumulus.LogErrorMessage($"GetIntervalWindGraphData: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogMessage("Please edit the file to correct the error"); } } - - if (entrydate >= dateto || fileDate > dateto) + else { - finished = true; + cumulus.LogDebugMessage($"GetIntervalWindGraphData: Log file not found - {logFile}"); } - else + + cumulus.LogDebugMessage($"GetIntervalWindGraphData: Finished processing log file - {logFile}"); + + if (!finished) { fileDate = fileDate.AddMonths(1); - logFile = cumulus.GetExtraLogFileName(fileDate); + + if (fileDate.Year > dateTo.Year || (fileDate.Year == dateTo.Year && fileDate.Month > dateTo.Month)) + finished = true; + + logFile = cumulus.GetLogFileName(fileDate); } } - if (cumulus.GraphOptions.Visible.CO2Sensor.CO2.IsVisible(local)) + if (sb[sb.Length - 1] == ',') { - if (sbCo2[sbCo2.Length - 1] == ',') - sbCo2.Length--; - - sbCo2.Append("]"); - sb.Append(sbCo2); - append = true; + sb.Length--; + sbSpd.Length--; + sbBrg.Length--; + sbAvgBrg.Length--; } - if (cumulus.GraphOptions.Visible.CO2Sensor.CO2Avg.IsVisible(local)) - { - if (sbCo2Avg[sbCo2Avg.Length - 1] == ',') - sbCo2Avg.Length--; + sb.Append("],"); + sb.Append(sbSpd); + sb.Append("],"); + sb.Append(sbBrg); + sb.Append("],"); + sb.Append(sbAvgBrg); + sb.Append(']'); + sb.Append('}'); + return sb.ToString(); + } - sbCo2Avg.Append("]"); - sb.Append((append ? "," : "") + sbCo2Avg); - append = true; - } + public string GetIntervalRainGraphData(bool local, DateTime? start = null, DateTime? end = null) + { + var InvC = new CultureInfo(""); + var sb = new StringBuilder("{"); + var sbRain = new StringBuilder("\"rfall\":["); + var sbRate = new StringBuilder("\"rrate\":["); - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25.IsVisible(local)) - { - if (sbPm25[sbPm25.Length - 1] == ',') - sbPm25.Length--; + var dateFrom = start ?? cumulus.RecordsBeganDateTime; + var dateTo = end ?? DateTime.Now.Date; + dateTo = dateTo.AddDays(1); - sbPm25.Append("]"); - sb.Append((append ? "," : "") + sbPm25); - append = true; - } + var fileDate = dateFrom; + var logFile = cumulus.GetLogFileName(fileDate); - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm25Avg.IsVisible(local)) - { - if (sbPm25Avg[sbPm25Avg.Length - 1] == ',') - sbPm25Avg.Length--; + var finished = false; - sbPm25Avg.Append("]"); - sb.Append((append ? "," : "") + sbPm25Avg); - append = true; - } - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10.IsVisible(local)) + while (!finished) { - if (sbPm10[sbPm10.Length - 1] == ',') - sbPm10.Length--; + if (File.Exists(logFile)) + { + cumulus.LogDebugMessage($"GetIntervaRainGraphData: Processing log file - {logFile}"); + var linenum = 0; + try + { + var logfile = File.ReadAllLines(logFile); - sbPm10.Append("]"); - sb.Append((append ? "," : "") + sbPm10); - append = true; - } + foreach (var line in logfile) + { + // process each record in the file + linenum++; - if (cumulus.GraphOptions.Visible.CO2Sensor.Pm10Avg.IsVisible(local)) - { - if (sbPm10Avg[sbPm10Avg.Length - 1] == ',') - sbPm10Avg.Length--; + var rec = ParseLogFileRec(line, true); - sbPm10Avg.Append("]"); - sb.Append((append ? "," : "") + sbPm10Avg); - append = true; - } + if (rec.Date < dateFrom) + continue; - if (cumulus.GraphOptions.Visible.CO2Sensor.Temp.IsVisible(local)) - { - if (sbTemp[sbTemp.Length - 1] == ',') - sbTemp.Length--; + if (rec.Date > dateTo) + { + finished = true; + cumulus.LogDebugMessage("GetIntervaRainGraphData: Finished processing the log files"); + break; + } - sbTemp.Append("]"); - sb.Append((append ? "," : "") + sbTemp); - append = true; - } + var jsTime = Utils.ToPseudoJSTime(rec.Date); - if (cumulus.GraphOptions.Visible.CO2Sensor.Hum.IsVisible(local)) - { - if (sbHum[sbHum.Length - 1] == ',') - sbHum.Length--; + sbRain.Append($"[{jsTime},{rec.RainToday.ToString(cumulus.RainFormat, InvC)}],"); - sbHum.Append("]"); - sb.Append((append ? "," : "") + sbHum); - append = true; + sbRate.Append($"[{jsTime},{rec.RainRate.ToString(cumulus.RainFormat, InvC)}],"); + } + } + catch (Exception e) + { + cumulus.LogErrorMessage($"GetIntervaRainGraphData: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogMessage("Please edit the file to correct the error"); + } + } + else + { + cumulus.LogDebugMessage($"GetIntervaRainGraphData: Log file not found - {logFile}"); + } + + cumulus.LogDebugMessage($"GetIntervaRainGraphData: Finished processing log file - {logFile}"); + + if (!finished) + { + fileDate = fileDate.AddMonths(1); + + if (fileDate.Year > dateTo.Year || (fileDate.Year == dateTo.Year && fileDate.Month > dateTo.Month)) + finished = true; + + logFile = cumulus.GetLogFileName(fileDate); + } } + if (sbRain[sbRain.Length - 1] == ',') + { + sbRain.Length--; + sbRate.Length--; + } + sbRain.Append("],"); + sbRate.Append(']'); + sb.Append(sbRain); + sb.Append(sbRate); sb.Append('}'); return sb.ToString(); } @@ -3964,7 +4747,7 @@ public void AddRecentDataEntry(DateTime timestamp, double windAverage, double re OutsideTemp = outsidetemp, Pressure = pressure, RainToday = rainToday, - SolarRad = (int)solarRad, + SolarRad = (int) solarRad, UV = uv, WindAvgDir = avgBearing, WindGust = recentMaxGust, @@ -3978,7 +4761,7 @@ public void AddRecentDataEntry(DateTime timestamp, double windAverage, double re AppTemp = appTemp, IndoorTemp = insideTemp, IndoorHumidity = insideHum, - SolarMax = (int)solarMax, + SolarMax = (int) solarMax, RainRate = rainrate, Pm2p5 = pm2p5, Pm10 = pm10 @@ -4256,7 +5039,7 @@ public void DoIndoorHumidity(int hum) return; } - IndoorHumidity = (int)Math.Round((hum * cumulus.Calib.InHum.Mult) + cumulus.Calib.InHum.Offset); + IndoorHumidity = (int) Math.Round((hum * cumulus.Calib.InHum.Mult) + cumulus.Calib.InHum.Offset); HaveReadData = true; } @@ -4301,7 +5084,7 @@ public void DoOutdoorHumidity(int humpar, DateTime timestamp) } // apply offset and multipliers and round. This is different to C1, which truncates. I'm not sure why C1 does that - OutdoorHumidity = (int)Math.Round((OutdoorHumidity * OutdoorHumidity * cumulus.Calib.Hum.Mult2) + (OutdoorHumidity * cumulus.Calib.Hum.Mult) + cumulus.Calib.Hum.Offset); + OutdoorHumidity = (int) Math.Round((OutdoorHumidity * OutdoorHumidity * cumulus.Calib.Hum.Mult2) + (OutdoorHumidity * cumulus.Calib.Hum.Mult) + cumulus.Calib.Hum.Offset); if (OutdoorHumidity < 0) { @@ -4478,7 +5261,7 @@ public void DoCloudBaseHeatIndex(DateTime timestamp) var tempinC = ConvertUserTempToC(OutdoorTemperature); // Calculate cloud base - CloudBase = (int)Math.Floor((tempinF - ConvertUserTempToF(OutdoorDewpoint)) / 4.4 * 1000 / (cumulus.CloudBaseInFeet ? 1 : 3.2808399)); + CloudBase = (int) Math.Floor((tempinF - ConvertUserTempToF(OutdoorDewpoint)) / 4.4 * 1000 / (cumulus.CloudBaseInFeet ? 1 : 3.2808399)); if (CloudBase < 0) CloudBase = 0; @@ -4835,7 +5618,7 @@ public void DoPressure(double sl, DateTime timestamp) { if ((cumulus.StationType == StationTypes.VantagePro2 && !cumulus.DavisOptions.UseLoop2) || cumulus.StationType == StationTypes.VantagePro) { - // Loop2 data not available, or WLL, just use sea level (for now, anyway) + // Loop2 data not available, just use sea level (for now, anyway) AltimeterPressure = Pressure; } } @@ -5022,7 +5805,7 @@ public void DoRain(double total, double rate, DateTime timestamp) if (FirstChanceRainReset) // second consecutive reading with reset value { - cumulus.LogMessage(" ****Rain counter reset confirmed: raindaystart = " + raindaystart + ", Raincounter = " + Raincounter); + cumulus.LogWarningMessage(" ****Rain counter reset confirmed: raindaystart = " + raindaystart + ", Raincounter = " + Raincounter); // set the start of day figure so it reflects the rain // so far today @@ -5215,7 +5998,7 @@ public void DoExtraHum(double hum, int channel) { if ((channel > 0) && (channel < ExtraHum.Length)) { - ExtraHum[channel] = (int)hum; + ExtraHum[channel] = (int) hum; } } @@ -5369,12 +6152,11 @@ 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) { -#if DEBUG - cumulus.LogDebugMessage($"DoWind: gust={gustpar:F1}, speed={speedpar:F1}"); -#endif + cumulus.LogDebugMessage($"DoWind: gust={gustpar:F1}, speed={speedpar:F1} - Current: gust={RecentMaxGust:F1}, speed={WindAverage:F1}"); + // Spike removal is in m/s var windGustMS = ConvertUserWindToMS(gustpar); - var windAvgMS = ConvertUserWindToMS(speedpar); + var windAvgMS = ConvertUserWindToMS(speedpar == -1 ? previousWind : speedpar); if (((Math.Abs(windGustMS - previousGust) > cumulus.Spike.GustDiff) && (previousGust != 999)) || ((Math.Abs(windAvgMS - previousWind) > cumulus.Spike.WindDiff) && (previousWind != 999)) || @@ -5394,7 +6176,8 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim previousWind = windAvgMS; calibratedgust = cumulus.Calib.WindGust.Calibrate(gustpar); - var calibratedspeed = speedpar < 0 ? WindAverage : cumulus.Calib.WindSpeed.Calibrate(speedpar); + var uncalibratedspeed = speedpar < 0 ? WindAverageUncalibrated : speedpar; + var calibratedspeed = cumulus.Calib.WindSpeed.Calibrate(uncalibratedspeed); // use bearing of zero when calm if ((Math.Abs(gustpar) < 0.001) && cumulus.StationOptions.UseZeroBearing) @@ -5403,7 +6186,7 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim } else { - Bearing = (bearingpar + (int)cumulus.Calib.WindDir.Offset) % 360; + Bearing = (bearingpar + (int) cumulus.Calib.WindDir.Offset) % 360; if (Bearing < 0) { Bearing = 360 + Bearing; @@ -5419,6 +6202,7 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim windspeeds[nextwindvalue] = calibratedgust; windbears[nextwindvalue] = Bearing; + nextwindvalue = (nextwindvalue + 1) % maxwindvalues; // Recalculate wind rose data lock (windcounts) @@ -5430,7 +6214,7 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim for (int i = 0; i < numwindvalues; i++) { - int j = (((windbears[i] * 100) + 1125) % 36000) / (int)Math.Floor(cumulus.WindRoseAngle * 100); + int j = (((windbears[i] * 100) + 1125) % 36000) / (int) Math.Floor(cumulus.WindRoseAngle * 100); windcounts[j] += windspeeds[i]; } } @@ -5440,47 +6224,59 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim numwindvalues++; } - nextwindvalue = (nextwindvalue + 1) % maxwindvalues; - CheckHighGust(calibratedgust, Bearing, timestamp); - WindRecent[nextwind].Gust = calibratedgust; - WindRecent[nextwind].Speed = calibratedspeed; + WindRecent[nextwind].Gust = gustpar; // We store uncalibrated gust values, so if we need to calculate the average from them we do not need to uncalibrate + WindRecent[nextwind].Speed = uncalibratedspeed; WindRecent[nextwind].Timestamp = timestamp; nextwind = (nextwind + 1) % MaxWindRecent; +#if DEBUGWIND + cumulus.LogDebugMessage($"Wind calc using speed: {cumulus.StationOptions.UseSpeedForAvgCalc}"); +#endif + if (cumulus.StationOptions.CalcuateAverageWindSpeed) { int numvalues = 0; double totalwind = 0; + double avg = 0; var fromTime = timestamp - cumulus.AvgSpeedTime; for (int i = 0; i < MaxWindRecent; i++) { if (WindRecent[i].Timestamp >= fromTime) { +#if DEBUGWIND +// cumulus.LogDebugMessage($"Wind Time:{WindRecent[i].Timestamp.ToLongTimeString()} Gust:{WindRecent[i].Gust:F1} Speed:{WindRecent[i].Speed:F1}"); +#endif + numvalues++; - if (cumulus.StationOptions.UseSpeedForAvgCalc) - { - totalwind += WindRecent[i].Speed; - } - else - { - totalwind += WindRecent[i].Gust; - } + totalwind += cumulus.StationOptions.UseSpeedForAvgCalc ? WindRecent[i].Speed : WindRecent[i].Gust; } } // average the values, if we have enough samples if (numvalues > 3) - WindAverage = totalwind / numvalues; + { + avg = totalwind / numvalues; + } else - WindAverage = totalwind / 3; + { + avg = totalwind / 3; + } + + WindAverageUncalibrated = avg; + + // we want any calibration to be applied from uncalibrated values + WindAverage = cumulus.Calib.WindSpeed.Calibrate(avg); + //cumulus.LogDebugMessage("next=" + nextwind + " wind=" + uncalibratedgust + " tot=" + totalwind + " numv=" + numvalues + " avg=" + WindAverage); } else { WindAverage = calibratedspeed; + WindAverageUncalibrated = uncalibratedspeed; } + if (CalcRecentMaxGust) { // Find recent max gust @@ -5494,13 +6290,16 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim maxgust = WindRecent[i].Gust; } } - RecentMaxGust = maxgust; + // wind gust is stored uncaligrated, so we need to calibrate now + RecentMaxGust = cumulus.Calib.WindGust.Calibrate(maxgust); } else { RecentMaxGust = calibratedgust; } + cumulus.LogDebugMessage($"DoWind: New: gust={RecentMaxGust:F1}, speed={WindAverage:F1}, latest:{WindLatest:F1}"); + CheckHighAvgSpeed(timestamp); WindVec[nextwindvec].X = calibratedgust * Math.Sin(DegToRad(Bearing)); @@ -5529,7 +6328,7 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim } else { - AvgBearing = (int)Math.Round(RadToDeg(Math.Atan(totalwindY / totalwindX))); + AvgBearing = (int) Math.Round(RadToDeg(Math.Atan(totalwindY / totalwindX))); if (totalwindX < 0) { @@ -5570,13 +6369,13 @@ public void DoWind(double gustpar, int bearingpar, double speedpar, DateTime tim diffTo = difference; BearingRangeTo = WindVec[i].Bearing; // Calculate rounded up value - BearingRangeTo10 = (int)(Math.Ceiling(WindVec[i].Bearing / 10.0) * 10); + 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); + BearingRangeFrom10 = (int) (Math.Floor(WindVec[i].Bearing / 10.0) * 10); } } } @@ -5626,9 +6425,22 @@ public void InitialiseWind() } } } + + cumulus.LogDebugMessage($"InitialiseWind: gust={RecentMaxGust:F1}, speed={WindAverage:F1}"); } + public void AddValuesToRecentWind(double gust, double speed, DateTime start, DateTime end) + { + for (DateTime ts = start; ts <= end; ts = ts.AddSeconds(3)) + { + nextwindvalue = (nextwindvalue + 1) % maxwindvalues; + WindRecent[nextwind].Gust = gust; + WindRecent[nextwind].Speed = speed; + WindRecent[nextwind].Timestamp = ts; + nextwind = (nextwind + 1) % MaxWindRecent; + } + } public void DoUV(double value, DateTime timestamp) { @@ -5677,7 +6489,7 @@ protected void DoSunHours(double hrs) { if (StartOfDaySunHourCounter < -9998) { - cumulus.LogMessage("No start of day sun counter. Start counting from now"); + cumulus.LogWarningMessage("No start of day sun counter. Start counting from now"); StartOfDaySunHourCounter = hrs; } @@ -5703,7 +6515,7 @@ protected void DoWetBulb(double temp, DateTime timestamp) // Supplied in CELSIUS double Es = MeteoLib.SaturationVapourPressure1980(TempDry); double Ew = MeteoLib.SaturationVapourPressure1980(temp); double E = Ew - (0.00066 * (1 + 0.00115 * temp) * (TempDry - temp) * 1013); - int hum = (int)(100 * (E / Es)); + int hum = (int) (100 * (E / Es)); DoOutdoorHumidity(hum, timestamp); // calculate DP // Calculate DewPoint @@ -5867,6 +6679,7 @@ private int getShortestAngle(int bearing1, int bearing2) public int[] DavisTxRssi = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; public string DavisFirmwareVersion = "???"; public string GW1000FirmwareVersion = "???"; + public string EcowittCameraUrl = string.Empty; public static Dictionary SensorReception { get; set; } @@ -6712,7 +7525,7 @@ public void DayReset(DateTime timestamp) } catch (Exception ex) { - cumulus.LogMessage("Error creating NOAA reports: " + ex.Message); + cumulus.LogErrorMessage("Error creating NOAA reports: " + ex.Message); } } @@ -6757,7 +7570,7 @@ public void DayReset(DateTime timestamp) } catch (Exception ex) { - cumulus.LogMessage("Error executing external program: " + ex.Message); + cumulus.LogErrorMessage("Error executing external program: " + ex.Message); } } @@ -6926,7 +7739,7 @@ private async void DoDayfile(DateTime timestamp) strb.Append(DominantWindBearing + cumulus.ListSeparator); strb.Append(HeatingDegreeDays.ToString("F1") + cumulus.ListSeparator); strb.Append(CoolingDegreeDays.ToString("F1") + cumulus.ListSeparator); - strb.Append((int)HiLoToday.HighSolar + cumulus.ListSeparator); + strb.Append((int) HiLoToday.HighSolar + cumulus.ListSeparator); strb.Append(HiLoToday.HighSolarTime.ToString("HH:mm") + cumulus.ListSeparator); strb.Append(HiLoToday.HighUv.ToString(cumulus.UVFormat) + cumulus.ListSeparator); strb.Append(HiLoToday.HighUvTime.ToString("HH:mm") + cumulus.ListSeparator); @@ -6953,8 +7766,8 @@ private async void DoDayfile(DateTime timestamp) if ((HiLoToday.HighTemp < -400) || (HiLoToday.LowTemp > 900)) { - cumulus.LogMessage("***Error: Daily values are still at default at end of day"); - cumulus.LogMessage("Data not logged to dayfile.txt"); + cumulus.LogErrorMessage("***Error: Daily values are still at default at end of day"); + cumulus.LogErrorMessage("Data not logged to dayfile.txt"); return; } else @@ -6976,7 +7789,7 @@ private async void DoDayfile(DateTime timestamp) } catch (Exception ex) { - cumulus.LogMessage("Error writing to dayfile.txt: " + ex.Message); + cumulus.LogErrorMessage("Error writing to dayfile.txt: " + ex.Message); retries--; await System.Threading.Tasks.Task.Delay(250); } @@ -7028,7 +7841,7 @@ private async void DoDayfile(DateTime timestamp) DominantWindBearing = DominantWindBearing, HeatingDegreeDays = HeatingDegreeDays, CoolingDegreeDays = CoolingDegreeDays, - HighSolar = (int)HiLoToday.HighSolar, + HighSolar = (int) HiLoToday.HighSolar, HighSolarTime = HiLoToday.HighSolarTime, HighUv = HiLoToday.HighUv, HighUvTime = HiLoToday.HighUvTime, @@ -7095,7 +7908,7 @@ private async void DoDayfile(DateTime timestamp) queryString.Append(DominantWindBearing + ","); queryString.Append(HeatingDegreeDays.ToString("F1", InvC) + ","); queryString.Append(CoolingDegreeDays.ToString("F1", InvC) + ","); - queryString.Append((int)HiLoToday.HighSolar + ","); + queryString.Append((int) HiLoToday.HighSolar + ","); queryString.Append(HiLoToday.HighSolarTime.ToString("\\'HH:mm\\'") + ","); queryString.Append(HiLoToday.HighUv.ToString(cumulus.UVFormat, InvC) + ","); queryString.Append(HiLoToday.HighUvTime.ToString("\\'HH:mm\\'") + ",'"); @@ -7462,7 +8275,7 @@ public void StartLoop() } catch (Exception ex) { - cumulus.LogMessage("An error occurred during the station start-up: " + ex.Message); + cumulus.LogErrorMessage("An error occurred during the station start-up: " + ex.Message); } } @@ -7500,7 +8313,7 @@ public int CalcAverageBearing() private int calcavgbear(double x, double y) { - var avg = 90 - (int)(RadToDeg(Math.Atan2(y, x))); + var avg = 90 - (int) (RadToDeg(Math.Atan2(y, x))); if (avg < 0) { avg = 360 + avg; @@ -7518,33 +8331,33 @@ public void AddRecentDataWithAq(DateTime timestamp, double windAverage, double r // Check for Air Quality readings switch (cumulus.StationOptions.PrimaryAqSensor) { - case (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor: + case (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor: if (cumulus.airLinkDataOut != null) { pm2p5 = cumulus.airLinkDataOut.pm2p5; pm10 = cumulus.airLinkDataOut.pm10; } break; - case (int)Cumulus.PrimaryAqSensor.AirLinkIndoor: + case (int) Cumulus.PrimaryAqSensor.AirLinkIndoor: if (cumulus.airLinkDataIn != null) { pm2p5 = cumulus.airLinkDataIn.pm2p5; pm10 = cumulus.airLinkDataIn.pm10; } break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt1: + case (int) Cumulus.PrimaryAqSensor.Ecowitt1: pm2p5 = AirQuality1; break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt2: + case (int) Cumulus.PrimaryAqSensor.Ecowitt2: pm2p5 = AirQuality2; break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt3: + case (int) Cumulus.PrimaryAqSensor.Ecowitt3: pm2p5 = AirQuality3; break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt4: + case (int) Cumulus.PrimaryAqSensor.Ecowitt4: pm2p5 = AirQuality3; break; - case (int)Cumulus.PrimaryAqSensor.EcowittCO2: + case (int) Cumulus.PrimaryAqSensor.EcowittCO2: pm2p5 = CO2_pm2p5; pm10 = CO2_pm10; break; @@ -7565,7 +8378,7 @@ public void UpdateRecentDataAqEntry(DateTime ts, double pm2p5, double pm10) } catch (Exception e) { - cumulus.LogMessage($"UpdateGraphDataAqEntry: Exception caught: {e.Message}"); + cumulus.LogErrorMessage($"UpdateGraphDataAqEntry: Exception caught: {e.Message}"); } } @@ -7922,7 +8735,7 @@ public void CalculateDominantWindBearing(int averageBearing, double averageSpeed } catch { - cumulus.LogMessage("Error in dominant wind direction calculation"); + cumulus.LogErrorMessage("Error in dominant wind direction calculation"); } } @@ -8028,7 +8841,7 @@ private void LoadRecentFromDataLogs(DateTime ts) } catch (Exception e) { - cumulus.LogMessage("LoadRecent: Error querying database for latest record - " + e.Message); + cumulus.LogErrorMessage("LoadRecent: Error querying database for latest record - " + e.Message); } @@ -8064,7 +8877,7 @@ private void LoadRecentFromDataLogs(DateTime ts) OutsideTemp = rec.OutdoorTemperature, Pressure = rec.Pressure, RainToday = rec.RainToday, - SolarRad = (int)rec.SolarRad, + SolarRad = (int) rec.SolarRad, UV = rec.UV, WindAvgDir = rec.AvgBearing, WindGust = rec.RecentMaxGust, @@ -8078,7 +8891,7 @@ private void LoadRecentFromDataLogs(DateTime ts) AppTemp = rec.ApparentTemperature, IndoorTemp = rec.IndoorTemperature, IndoorHumidity = rec.IndoorHumidity, - SolarMax = (int)rec.CurrentSolarMax, + SolarMax = (int) rec.CurrentSolarMax, RainRate = rec.RainRate, Pm2p5 = -1, Pm10 = -1 @@ -8088,19 +8901,19 @@ private void LoadRecentFromDataLogs(DateTime ts) } catch (Exception e) { - cumulus.LogMessage($"LoadRecent: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogWarningMessage($"LoadRecent: Error at line {linenum} of {logFile} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); errorCount++; if (errorCount >= 10) { - cumulus.LogMessage($"LoadRecent: Too many errors reading {logFile} - aborting load of graph data"); + cumulus.LogErrorMessage($"LoadRecent: Too many errors reading {logFile} - aborting load of graph data"); } } } } catch (Exception e) { - cumulus.LogMessage($"LoadRecent: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogErrorMessage($"LoadRecent: Error at line {linenum} of {logFile} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); } @@ -8111,7 +8924,7 @@ private void LoadRecentFromDataLogs(DateTime ts) } catch (Exception e) { - cumulus.LogMessage($"LoadRecent: Error inserting recent data into database: {e.Message}"); + cumulus.LogErrorMessage($"LoadRecent: Error inserting recent data into database: {e.Message}"); } } @@ -8151,26 +8964,26 @@ private void LoadRecentAqFromDataLogs(DateTime ts) } catch (Exception e) { - cumulus.LogMessage("LoadRecentAqFromDataLogs: Error querying database for oldest record without AQ data - " + e.Message); + cumulus.LogErrorMessage("LoadRecentAqFromDataLogs: Error querying database for oldest record without AQ data - " + e.Message); } if (cumulus.StationOptions.PrimaryAqSensor < 0) return; cumulus.LogMessage($"LoadRecentAqFromDataLogs: Attempting to load {cumulus.RecentDataDays} days of entries to Air Quality recent data"); - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor - || cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkIndoor) + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor + || cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkIndoor) { logFile = cumulus.GetAirLinkLogFileName(filedate); } - else if ((cumulus.StationOptions.PrimaryAqSensor >= (int)Cumulus.PrimaryAqSensor.Ecowitt1 && cumulus.StationOptions.PrimaryAqSensor <= (int)Cumulus.PrimaryAqSensor.Ecowitt4) || - cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.EcowittCO2) // Ecowitt + else if ((cumulus.StationOptions.PrimaryAqSensor >= (int) Cumulus.PrimaryAqSensor.Ecowitt1 && cumulus.StationOptions.PrimaryAqSensor <= (int) Cumulus.PrimaryAqSensor.Ecowitt4) || + cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.EcowittCO2) // Ecowitt { logFile = cumulus.GetExtraLogFileName(filedate); } else { - cumulus.LogMessage($"LoadRecentAqFromDataLogs: Error - The primary AQ sensor is not set to a valid value, currently={cumulus.StationOptions.PrimaryAqSensor}"); + cumulus.LogErrorMessage($"LoadRecentAqFromDataLogs: Error - The primary AQ sensor is not set to a valid value, currently={cumulus.StationOptions.PrimaryAqSensor}"); return; } @@ -8199,19 +9012,19 @@ private void LoadRecentAqFromDataLogs(DateTime ts) { // entry is from required period double pm2p5, pm10; - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkIndoor) + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkIndoor) { // AirLink Indoor pm2p5 = Convert.ToDouble(st[5]); pm10 = Convert.ToDouble(st[10]); } - else if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor) + else if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor) { // AirLink Outdoor pm2p5 = Convert.ToDouble(st[32]); pm10 = Convert.ToDouble(st[37]); } - else if (cumulus.StationOptions.PrimaryAqSensor >= (int)Cumulus.PrimaryAqSensor.Ecowitt1 && cumulus.StationOptions.PrimaryAqSensor <= (int)Cumulus.PrimaryAqSensor.Ecowitt4) + else if (cumulus.StationOptions.PrimaryAqSensor >= (int) Cumulus.PrimaryAqSensor.Ecowitt1 && cumulus.StationOptions.PrimaryAqSensor <= (int) Cumulus.PrimaryAqSensor.Ecowitt4) { // Ecowitt sensor 1-4 - fields 68 -> 71 pm2p5 = Convert.ToDouble(st[67 + cumulus.StationOptions.PrimaryAqSensor]); @@ -8231,12 +9044,12 @@ private void LoadRecentAqFromDataLogs(DateTime ts) } catch (Exception e) { - cumulus.LogMessage($"LoadRecentAqFromDataLogs: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogWarningMessage($"LoadRecentAqFromDataLogs: Error at line {linenum} of {logFile} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); errorCount++; if (errorCount >= 20) { - cumulus.LogMessage($"LoadRecentAqFromDataLogs: Too many errors reading {logFile} - aborting load of graph data"); + cumulus.LogErrorMessage($"LoadRecentAqFromDataLogs: Too many errors reading {logFile} - aborting load of graph data"); } } } @@ -8245,7 +9058,7 @@ private void LoadRecentAqFromDataLogs(DateTime ts) } catch (Exception e) { - cumulus.LogMessage($"LoadRecentAqFromDataLogs: Error at line {linenum} of {logFile} : {e.Message}"); + cumulus.LogErrorMessage($"LoadRecentAqFromDataLogs: Error at line {linenum} of {logFile} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); RecentDataDb.Rollback(); } @@ -8258,14 +9071,14 @@ private void LoadRecentAqFromDataLogs(DateTime ts) else { filedate = filedate.AddMonths(1); - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor - || cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkIndoor) // AirLink + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor + || cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkIndoor) // AirLink { logFile = cumulus.GetAirLinkLogFileName(filedate); } - else if ((cumulus.StationOptions.PrimaryAqSensor >= (int)Cumulus.PrimaryAqSensor.Ecowitt1 - && cumulus.StationOptions.PrimaryAqSensor <= (int)Cumulus.PrimaryAqSensor.Ecowitt4) - || cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.EcowittCO2) // Ecowitt + else if ((cumulus.StationOptions.PrimaryAqSensor >= (int) Cumulus.PrimaryAqSensor.Ecowitt1 + && cumulus.StationOptions.PrimaryAqSensor <= (int) Cumulus.PrimaryAqSensor.Ecowitt4) + || cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.EcowittCO2) // Ecowitt { logFile = cumulus.GetExtraLogFileName(filedate); } @@ -8333,7 +9146,7 @@ private void LoadLast3HourData(DateTime ts) } catch (Exception e) { - cumulus.LogMessage($"LoadLast3Hour: Error loading data from database : {e.Message}"); + cumulus.LogErrorMessage($"LoadLast3Hour: Error loading data from database : {e.Message}"); } } cumulus.LogMessage($"LoadLast3Hour: Loaded {result.Count} entries to last 3 hour data list"); @@ -8355,7 +9168,7 @@ private void LoadWindData() } catch (Exception e) { - cumulus.LogMessage($"LoadWindData: Error loading data from database : {e.Message}"); + cumulus.LogErrorMessage($"LoadWindData: Error loading data from database : {e.Message}"); } try @@ -8364,7 +9177,7 @@ private void LoadWindData() } catch (Exception e) { - cumulus.LogMessage($"LoadWindData: Error loading pointer from database : {e.Message}"); + cumulus.LogErrorMessage($"LoadWindData: Error loading pointer from database : {e.Message}"); } cumulus.LogMessage($"LoadWindData: Loaded {result.Count} entries to WindRecent data list"); @@ -8398,7 +9211,7 @@ public void SaveWindData() } catch (Exception ex) { - cumulus.LogMessage($"SaveWindData: Error saving RecentWind to the database : {ex.Message}"); + cumulus.LogErrorMessage($"SaveWindData: Error saving RecentWind to the database : {ex.Message}"); RecentDataDb.Rollback(); } } @@ -8442,12 +9255,12 @@ public string LoadDayFile() } catch (Exception e) { - cumulus.LogMessage($"LoadDayFile: Error at line {linenum} of {cumulus.DayFileName} : {e.Message}"); + cumulus.LogWarningMessage($"LoadDayFile: Error at line {linenum} of {cumulus.DayFileName} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); errorCount++; if (errorCount >= 20) { - cumulus.LogMessage($"LoadDayFile: Too many errors reading {cumulus.DayFileName} - aborting load of daily data"); + cumulus.LogErrorMessage($"LoadDayFile: Too many errors reading {cumulus.DayFileName} - aborting load of daily data"); } } } while (!(sr.EndOfStream || errorCount >= 20)); @@ -8460,7 +9273,7 @@ public string LoadDayFile() } catch (Exception e) { - cumulus.LogMessage($"LoadDayFile: Error at line {linenum} of {cumulus.DayFileName} : {e.Message}"); + cumulus.LogErrorMessage($"LoadDayFile: Error at line {linenum} of {cumulus.DayFileName} : {e.Message}"); cumulus.LogMessage("Please edit the file to correct the error"); } @@ -8471,7 +9284,7 @@ public string LoadDayFile() else { var msg = "LoadDayFile: No Dayfile found - No entries added to recent daily data list"; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); return msg; } } @@ -8515,7 +9328,7 @@ public dayfilerec ParseDayFileRec(string data) if (st.Count > idx++ && double.TryParse(st[19], out varDbl)) rec.LowHumidity = Convert.ToInt32(varDbl); else - rec.LowHumidity = (int)Cumulus.DefaultLoVal; + rec.LowHumidity = (int) Cumulus.DefaultLoVal; if (st.Count > idx++ && st[20].Length == 5) rec.LowHumidityTime = GetDateTime(rec.Date, st[20]); @@ -8523,7 +9336,7 @@ public dayfilerec ParseDayFileRec(string data) if (st.Count > idx++ && double.TryParse(st[21], out varDbl)) rec.HighHumidity = Convert.ToInt32(varDbl); else - rec.HighHumidity = (int)Cumulus.DefaultHiVal; + rec.HighHumidity = (int) Cumulus.DefaultHiVal; if (st.Count > idx++ && st[22].Length == 5) rec.HighHumidityTime = GetDateTime(rec.Date, st[22]); @@ -8595,7 +9408,7 @@ public dayfilerec ParseDayFileRec(string data) if (st.Count > idx++ && double.TryParse(st[39], out varDbl)) rec.DominantWindBearing = Convert.ToInt32(varDbl); else - rec.DominantWindBearing = (int)Cumulus.DefaultHiVal; + rec.DominantWindBearing = (int) Cumulus.DefaultHiVal; if (st.Count > idx++ && double.TryParse(st[40], out varDbl)) rec.HeatingDegreeDays = varDbl; @@ -8610,7 +9423,7 @@ public dayfilerec ParseDayFileRec(string data) if (st.Count > idx++ && double.TryParse(st[42], out varDbl)) rec.HighSolar = Convert.ToInt32(varDbl); else - rec.HighSolar = (int)Cumulus.DefaultHiVal; + rec.HighSolar = (int) Cumulus.DefaultHiVal; if (st.Count > idx++ && st[43].Length == 5) rec.HighSolarTime = GetDateTime(rec.Date, st[43]); @@ -8799,7 +9612,7 @@ public logfilerec ParseLogFileRec(string data, bool minMax) } catch (Exception ex) { - cumulus.LogMessage("Error parsing log file record: " + ex.Message); + cumulus.LogErrorMessage("Error parsing log file record: " + ex.Message); cumulus.LogDataMessage("Log record: " + data); throw ex; } @@ -8871,52 +9684,52 @@ public void DoSoilMoisture(double value, int index) switch (index) { case 1: - SoilMoisture1 = (int)value; + SoilMoisture1 = (int) value; break; case 2: - SoilMoisture2 = (int)value; + SoilMoisture2 = (int) value; break; case 3: - SoilMoisture3 = (int)value; + SoilMoisture3 = (int) value; break; case 4: - SoilMoisture4 = (int)value; + SoilMoisture4 = (int) value; break; case 5: - SoilMoisture5 = (int)value; + SoilMoisture5 = (int) value; break; case 6: - SoilMoisture6 = (int)value; + SoilMoisture6 = (int) value; break; case 7: - SoilMoisture7 = (int)value; + SoilMoisture7 = (int) value; break; case 8: - SoilMoisture8 = (int)value; + SoilMoisture8 = (int) value; break; case 9: - SoilMoisture9 = (int)value; + SoilMoisture9 = (int) value; break; case 10: - SoilMoisture10 = (int)value; + SoilMoisture10 = (int) value; break; case 11: - SoilMoisture11 = (int)value; + SoilMoisture11 = (int) value; break; case 12: - SoilMoisture12 = (int)value; + SoilMoisture12 = (int) value; break; case 13: - SoilMoisture13 = (int)value; + SoilMoisture13 = (int) value; break; case 14: - SoilMoisture14 = (int)value; + SoilMoisture14 = (int) value; break; case 15: - SoilMoisture15 = (int)value; + SoilMoisture15 = (int) value; break; case 16: - SoilMoisture16 = (int)value; + SoilMoisture16 = (int) value; break; } } @@ -9204,7 +10017,7 @@ public string BetelCast(double z_hpa, int z_month, string z_wind, int z_trend, b z_hpa = z_baro_top - 1; } - int z_option = (int)Math.Floor((z_hpa - z_baro_bottom) / z_constant); + int z_option = (int) Math.Floor((z_hpa - z_baro_bottom) / z_constant); StringBuilder z_output = new StringBuilder(100); if (z_option < 0) @@ -9443,7 +10256,7 @@ public void WriteAlltimeIniFile() } catch (Exception ex) { - cumulus.LogMessage("Error writing alltime.ini file: " + ex.Message); + cumulus.LogErrorMessage("Error writing alltime.ini file: " + ex.Message); } } @@ -9617,7 +10430,7 @@ public void WriteMonthlyAlltimeIniFile() } catch (Exception ex) { - cumulus.LogMessage("Error writing MonthlyAlltime.ini file: " + ex.Message); + cumulus.LogErrorMessage("Error writing MonthlyAlltime.ini file: " + ex.Message); } } @@ -9850,7 +10663,7 @@ public void WriteMonthIniFile() } catch (Exception ex) { - cumulus.LogMessage("Error writing month.ini file: " + ex.Message); + cumulus.LogErrorMessage("Error writing month.ini file: " + ex.Message); } } cumulus.LogDebugMessage("End writing to Month.ini file"); @@ -10029,7 +10842,7 @@ public void WriteYearIniFile() } catch (Exception ex) { - cumulus.LogMessage("Error writing year.ini file: " + ex.Message); + cumulus.LogErrorMessage("Error writing year.ini file: " + ex.Message); } } } @@ -10101,36 +10914,36 @@ public string GetWCloudURL(out string pwstring, DateTime timestamp) StringBuilder sb = new StringBuilder($"https://api.weathercloud.net/v01/set?wid={cumulus.WCloud.ID}&key={pwstring}"); //Temperature - sb.Append("&tempin=" + (int)Math.Round(ConvertUserTempToC(IndoorTemperature) * 10)); - sb.Append("&temp=" + (int)Math.Round(ConvertUserTempToC(OutdoorTemperature) * 10)); - sb.Append("&chill=" + (int)Math.Round(ConvertUserTempToC(WindChill) * 10)); - sb.Append("&dew=" + (int)Math.Round(ConvertUserTempToC(OutdoorDewpoint) * 10)); - sb.Append("&heat=" + (int)Math.Round(ConvertUserTempToC(HeatIndex) * 10)); + sb.Append("&tempin=" + (int) Math.Round(ConvertUserTempToC(IndoorTemperature) * 10)); + sb.Append("&temp=" + (int) Math.Round(ConvertUserTempToC(OutdoorTemperature) * 10)); + sb.Append("&chill=" + (int) Math.Round(ConvertUserTempToC(WindChill) * 10)); + sb.Append("&dew=" + (int) Math.Round(ConvertUserTempToC(OutdoorDewpoint) * 10)); + sb.Append("&heat=" + (int) Math.Round(ConvertUserTempToC(HeatIndex) * 10)); // Humidity sb.Append("&humin=" + IndoorHumidity); sb.Append("&hum=" + OutdoorHumidity); // Wind - sb.Append("&wspd=" + (int)Math.Round(ConvertUserWindToMS(WindLatest) * 10)); - sb.Append("&wspdhi=" + (int)Math.Round(ConvertUserWindToMS(RecentMaxGust) * 10)); - sb.Append("&wspdavg=" + (int)Math.Round(ConvertUserWindToMS(WindAverage) * 10)); + sb.Append("&wspd=" + (int) Math.Round(ConvertUserWindToMS(WindLatest) * 10)); + sb.Append("&wspdhi=" + (int) Math.Round(ConvertUserWindToMS(RecentMaxGust) * 10)); + sb.Append("&wspdavg=" + (int) Math.Round(ConvertUserWindToMS(WindAverage) * 10)); // Wind Direction sb.Append("&wdir=" + Bearing); sb.Append("&wdiravg=" + AvgBearing); // Pressure - sb.Append("&bar=" + (int)Math.Round(ConvertUserPressToMB(Pressure) * 10)); + sb.Append("&bar=" + (int) Math.Round(ConvertUserPressToMB(Pressure) * 10)); // rain - sb.Append("&rain=" + (int)Math.Round(ConvertUserRainToMM(RainToday) * 10)); - sb.Append("&rainrate=" + (int)Math.Round(ConvertUserRainToMM(RainRate) * 10)); + sb.Append("&rain=" + (int) Math.Round(ConvertUserRainToMM(RainToday) * 10)); + sb.Append("&rainrate=" + (int) Math.Round(ConvertUserRainToMM(RainRate) * 10)); // ET if (cumulus.WCloud.SendSolar && cumulus.Manufacturer == cumulus.DAVIS) { - sb.Append("&et=" + (int)Math.Round(ConvertUserRainToMM(ET) * 10)); + sb.Append("&et=" + (int) Math.Round(ConvertUserRainToMM(ET) * 10)); } // solar @@ -10142,7 +10955,7 @@ public string GetWCloudURL(out string pwstring, DateTime timestamp) // uv if (cumulus.WCloud.SendUV) { - sb.Append("&uvi=" + (int)Math.Round(UV * 10)); + sb.Append("&uvi=" + (int) Math.Round(UV * 10)); } // aq @@ -10150,7 +10963,7 @@ public string GetWCloudURL(out string pwstring, DateTime timestamp) { switch (cumulus.StationOptions.PrimaryAqSensor) { - case (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor: + case (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor: if (cumulus.airLinkDataOut != null) { sb.Append($"&pm25={cumulus.airLinkDataOut.pm2p5:F0}"); @@ -10158,23 +10971,23 @@ public string GetWCloudURL(out string pwstring, DateTime timestamp) sb.Append($"&aqi={AirQualityIndices.US_EPApm2p5(cumulus.airLinkDataOut.pm2p5_24hr)}"); } break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt1: + case (int) Cumulus.PrimaryAqSensor.Ecowitt1: sb.Append($"&pm25={AirQuality1:F0}"); sb.Append($"&aqi={AirQualityIndices.US_EPApm2p5(AirQualityAvg1)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt2: + case (int) Cumulus.PrimaryAqSensor.Ecowitt2: sb.Append($"&pm25={AirQuality2:F0}"); sb.Append($"&aqi={AirQualityIndices.US_EPApm2p5(AirQualityAvg2)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt3: + case (int) Cumulus.PrimaryAqSensor.Ecowitt3: sb.Append($"&pm25={AirQuality3:F0}"); sb.Append($"&aqi={AirQualityIndices.US_EPApm2p5(AirQualityAvg3)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt4: + case (int) Cumulus.PrimaryAqSensor.Ecowitt4: sb.Append($"&pm25={AirQuality4:F0}"); sb.Append($"&aqi={AirQualityIndices.US_EPApm2p5(AirQualityAvg4)}"); break; - case (int)Cumulus.PrimaryAqSensor.EcowittCO2: + case (int) Cumulus.PrimaryAqSensor.EcowittCO2: sb.Append($"&pm25={CO2_pm2p5:F0}"); sb.Append($"&pm10={CO2_pm10:F0}"); sb.Append($"&aqi={AirQualityIndices.US_EPApm2p5(CO2_pm2p5_24h)}"); @@ -10384,7 +11197,7 @@ public string GetAwekasURLv4(out string pwstring, DateTime timestamp) switch (cumulus.StationOptions.PrimaryAqSensor) { - case (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor: + case (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor: if (cumulus.airLinkDataOut != null) { sb.Append($"AqPM1={cumulus.airLinkDataOut.pm1.ToString("F1", InvC)}"); @@ -10394,23 +11207,23 @@ public string GetAwekasURLv4(out string pwstring, DateTime timestamp) sb.Append($"&AqPM10_avg_24h={cumulus.airLinkDataOut.pm10_24hr.ToString("F1", InvC)}"); } break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt1: + case (int) Cumulus.PrimaryAqSensor.Ecowitt1: sb.Append($"AqPM2.5={AirQuality1.ToString("F1", InvC)}"); sb.Append($"&AqPM2.5_avg_24h={AirQualityAvg1.ToString("F1", InvC)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt2: + case (int) Cumulus.PrimaryAqSensor.Ecowitt2: sb.Append($"AqPM2.5={AirQuality2.ToString("F1", InvC)}"); sb.Append($"&AqPM2.5_avg_24h={AirQualityAvg2.ToString("F1", InvC)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt3: + case (int) Cumulus.PrimaryAqSensor.Ecowitt3: sb.Append($"AqPM2.5={AirQuality3.ToString("F1", InvC)}"); sb.Append($"&AqPM2.5_avg_24h={AirQualityAvg3.ToString("F1", InvC)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt4: + case (int) Cumulus.PrimaryAqSensor.Ecowitt4: sb.Append($"AqPM2.5={AirQuality4.ToString("F1", InvC)}"); sb.Append($"&AqPM2.5_avg_24h={AirQualityAvg4.ToString("F1", InvC)}"); break; - case (int)Cumulus.PrimaryAqSensor.EcowittCO2: + case (int) Cumulus.PrimaryAqSensor.EcowittCO2: sb.Append($"AqPM2.5={CO2_pm2p5.ToString("F1", InvC)}"); sb.Append($"&AqPM2.5_avg_24h={CO2_pm2p5_24h.ToString("F1", InvC)}"); sb.Append($"&AqPM10={CO2_pm10.ToString("F1", InvC)}"); @@ -10632,26 +11445,26 @@ public string GetWundergroundURL(out string pwstring, DateTime timestamp, bool c if (cumulus.Wund.SendLeafWetness2) Data.Append($"&leafwetness2={LeafWetness2.ToString(cumulus.LeafWetFormat)}"); - if (cumulus.Wund.SendAirQuality && cumulus.StationOptions.PrimaryAqSensor > (int)Cumulus.PrimaryAqSensor.Undefined) + if (cumulus.Wund.SendAirQuality && cumulus.StationOptions.PrimaryAqSensor > (int) Cumulus.PrimaryAqSensor.Undefined) { switch (cumulus.StationOptions.PrimaryAqSensor) { - case (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor: + case (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor: if (cumulus.airLinkDataOut != null) { Data.Append($"&AqPM2.5={cumulus.airLinkDataOut.pm2p5:F1}&AqPM10={cumulus.airLinkDataOut.pm10.ToString("F1", invC)}"); } break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt1: + case (int) Cumulus.PrimaryAqSensor.Ecowitt1: Data.Append($"&AqPM2.5={AirQuality1.ToString("F1", invC)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt2: + case (int) Cumulus.PrimaryAqSensor.Ecowitt2: Data.Append($"&AqPM2.5={AirQuality2.ToString("F1", invC)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt3: + case (int) Cumulus.PrimaryAqSensor.Ecowitt3: Data.Append($"&AqPM2.5={AirQuality3.ToString("F1", invC)}"); break; - case (int)Cumulus.PrimaryAqSensor.Ecowitt4: + case (int) Cumulus.PrimaryAqSensor.Ecowitt4: Data.Append($"&AqPM2.5={AirQuality4.ToString("F1", invC)}"); break; } @@ -12068,6 +12881,18 @@ public string GetTodayYestSolar() json.Append(" hrs"); json.Append(sepStr); json.Append(" "); + json.Append("\"],"); + + json.Append("[\"High UV-Index\",\""); + json.Append(HiLoToday.HighUv.ToString("F1")); + json.Append(" "); + json.Append(sepStr); + json.Append(HiLoToday.HighUvTime.ToString(cumulus.ProgramOptions.TimeFormat)); + json.Append(sepStr); + json.Append(HiLoYest.HighUv.ToString("F1")); + json.Append(" "); + json.Append(sepStr); + json.Append(HiLoYest.HighUvTime.ToString(cumulus.ProgramOptions.TimeFormat)); json.Append("\"]"); json.Append("]}"); @@ -12104,7 +12929,7 @@ public string GetDayfile(string draw, int start, int length, string search) lineNum++; // if we have a search string and no match, skip to next line - if (!string.IsNullOrEmpty(search) &&!line.Contains(search)) + if (!string.IsNullOrEmpty(search) && !line.Contains(search)) { continue; } @@ -12172,7 +12997,7 @@ public string GetDayfile(string draw, int start, int length, string search) } catch (Exception ex) { - cumulus.LogMessage("GetDayFile: Error - " + ex.ToString()); + cumulus.LogErrorMessage("GetDayFile: Error - " + ex.ToString()); } return ""; @@ -12257,7 +13082,7 @@ public string GetLogfile(string from, string to, string draw, int start, int len if (!File.Exists(logfile)) { - cumulus.LogMessage($"GetLogFile: Error, file does not exist: {logfile}"); + cumulus.LogErrorMessage($"GetLogFile: Error, file does not exist: {logfile}"); return ""; } @@ -12391,7 +13216,7 @@ public string GetLogfile(string from, string to, string draw, int start, int len } catch (Exception ex) { - cumulus.LogMessage(ex.ToString()); + cumulus.LogErrorMessage("GetLogfile: Error - " + ex.ToString()); } return ""; @@ -12460,7 +13285,7 @@ public string GetCachedSqlCommands(string draw, int start, int length, string se } catch (Exception ex) { - cumulus.LogMessage(ex.ToString()); + cumulus.LogErrorMessage("GetCachedSqlCommands: Error - " + ex.ToString()); } return ""; @@ -12757,16 +13582,16 @@ public string GetAvailGraphData(bool local) // air quality // Check if we are to generate AQ data at all. Only if a primary sensor is defined and it isn't the Indoor AirLink - if (cumulus.StationOptions.PrimaryAqSensor > (int)Cumulus.PrimaryAqSensor.Undefined - && cumulus.StationOptions.PrimaryAqSensor != (int)Cumulus.PrimaryAqSensor.AirLinkIndoor) + if (cumulus.StationOptions.PrimaryAqSensor > (int) Cumulus.PrimaryAqSensor.Undefined + && cumulus.StationOptions.PrimaryAqSensor != (int) Cumulus.PrimaryAqSensor.AirLinkIndoor) { json.Append(",\"AirQuality\":["); json.Append("\"PM 2.5\""); // Only the AirLink and Ecowitt CO2 servers provide PM10 values at the moment - if (cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkOutdoor || - cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.AirLinkIndoor || - cumulus.StationOptions.PrimaryAqSensor == (int)Cumulus.PrimaryAqSensor.EcowittCO2) + if (cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkOutdoor || + cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.AirLinkIndoor || + cumulus.StationOptions.PrimaryAqSensor == (int) Cumulus.PrimaryAqSensor.EcowittCO2) { json.Append(",\"PM 10\""); } @@ -12949,6 +13774,11 @@ public string GetSelectaChartOptions() return JsonSerializer.SerializeToString(cumulus.SelectaChartOptions); } + public string GetSelectaPeriodOptions() + { + return JsonSerializer.SerializeToString(cumulus.SelectaPeriodOptions); + } + public string GetDailyRainGraphData() { @@ -13914,7 +14744,7 @@ internal string GetCurrentData() HiLoToday.LowPressTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.HighRainRate, HiLoToday.HighRainRateTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.HighHumidity, HiLoToday.LowHumidity, HiLoToday.HighHumidityTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowHumidityTime.ToString(cumulus.ProgramOptions.TimeFormat), cumulus.Units.PressText, cumulus.Units.TempText, cumulus.Units.RainText, HiLoToday.HighDewPoint, HiLoToday.LowDewPoint, HiLoToday.HighDewPointTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowDewPointTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowWindChill, - HiLoToday.LowWindChillTime.ToString(cumulus.ProgramOptions.TimeFormat), (int)SolarRad, (int)HiLoToday.HighSolar, HiLoToday.HighSolarTime.ToString(cumulus.ProgramOptions.TimeFormat), UV, HiLoToday.HighUv, + HiLoToday.LowWindChillTime.ToString(cumulus.ProgramOptions.TimeFormat), (int) SolarRad, (int) HiLoToday.HighSolar, HiLoToday.HighSolarTime.ToString(cumulus.ProgramOptions.TimeFormat), UV, HiLoToday.HighUv, HiLoToday.HighUvTime.ToString(cumulus.ProgramOptions.TimeFormat), forecaststr, getTimeString(cumulus.SunRiseTime, cumulus.ProgramOptions.TimeFormat), getTimeString(cumulus.SunSetTime, cumulus.ProgramOptions.TimeFormat), getTimeString(cumulus.MoonRiseTime, cumulus.ProgramOptions.TimeFormat), getTimeString(cumulus.MoonSetTime, cumulus.ProgramOptions.TimeFormat), HiLoToday.HighHeatIndex, HiLoToday.HighHeatIndexTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.HighAppTemp, HiLoToday.LowAppTemp, HiLoToday.HighAppTempTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowAppTempTime.ToString(cumulus.ProgramOptions.TimeFormat), CurrentSolarMax, @@ -14116,7 +14946,7 @@ public void UpdateAPRS() } catch (Exception e) { - cumulus.LogMessage("CWOP error: " + e.Message); + cumulus.LogErrorMessage("CWOP error: " + e.Message); } } } @@ -14144,7 +14974,7 @@ private string APRSLat(Cumulus cumulus) } cumulus.DegToDMS(lat, out d, out m, out s); - int hh = (int) Math.Round(s*100/60.0); + int hh = (int) Math.Round(s * 100 / 60.0); return String.Format("{0:D2}{1:D2}.{2:D2}{3}", d, m, hh, dir); } @@ -14172,7 +15002,7 @@ private string APRSLon(Cumulus cumulus) } cumulus.DegToDMS(lon, out d, out m, out s); - int hh = (int) Math.Round(s*100/60.0); + int hh = (int) Math.Round(s * 100 / 60.0); return String.Format("{0:D3}{1:D2}.{2:D2}{3}", d, m, hh, dir); } @@ -14545,7 +15375,7 @@ public AllTimeRec this[string propertyName] public AllTimeRec LowAppTemp { get; set; } = new AllTimeRec(16); public AllTimeRec HighHeatIndex { get; set; } = new AllTimeRec(17); public AllTimeRec HighDewPoint { get; set; } = new AllTimeRec(18); - public AllTimeRec LowDewPoint{ get; set; } = new AllTimeRec(19); + public AllTimeRec LowDewPoint { get; set; } = new AllTimeRec(19); public AllTimeRec HighWindRun { get; set; } = new AllTimeRec(20); public AllTimeRec LongestDryPeriod { get; set; } = new AllTimeRec(21); public AllTimeRec LongestWetPeriod { get; set; } = new AllTimeRec(22); diff --git a/CumulusMX/Wizard.cs b/CumulusMX/Wizard.cs index 3836e17a..cd8646fc 100644 --- a/CumulusMX/Wizard.cs +++ b/CumulusMX/Wizard.cs @@ -1,9 +1,11 @@ using System; using System.IO; using System.Net; + +using EmbedIO; + using ServiceStack; using ServiceStack.Text; -using EmbedIO; namespace CumulusMX { @@ -24,7 +26,7 @@ public string GetAlpacaFormData() description = cumulus.LocationDesc, latitude = cumulus.Latitude, longitude = cumulus.Longitude, - altitude = (int)cumulus.Altitude, + altitude = (int) cumulus.Altitude, altitudeunit = cumulus.AltitudeInFeet ? "feet" : "metres", }; @@ -81,7 +83,7 @@ public string GetAlpacaFormData() }; var weatherflow = new JsonStationSettingsWeatherFlow() - {deviceid = cumulus.WeatherFlowOptions.WFDeviceId, tcpport = cumulus.WeatherFlowOptions.WFTcpPort, token = cumulus.WeatherFlowOptions.WFToken, dayshistory = cumulus.WeatherFlowOptions.WFDaysHist}; + { deviceid = cumulus.WeatherFlowOptions.WFDeviceId, tcpport = cumulus.WeatherFlowOptions.WFTcpPort, token = cumulus.WeatherFlowOptions.WFToken, dayshistory = cumulus.WeatherFlowOptions.WFDaysHist }; var gw1000 = new JsonStationSettingsGw1000Conn() { @@ -113,6 +115,12 @@ public string GetAlpacaFormData() comportname = cumulus.ComportName }; + var ecowittapi = new JsonStationSettingsEcowittApi() + { + applicationkey = cumulus.EcowittApplicationKey, + userkey = cumulus.EcowittUserApiKey, + mac = cumulus.EcowittMacAddress + }; var station = new JsonWizardStation() { @@ -125,7 +133,8 @@ public string GetAlpacaFormData() easyw = easyweather, imet = imet, wmr928 = wmr, - weatherflow = weatherflow + weatherflow = weatherflow, + ecowittapi = ecowittapi }; var copy = new JsonWizardInternetCopy() @@ -139,7 +148,7 @@ public string GetAlpacaFormData() enabled = cumulus.FtpOptions.Enabled, directory = cumulus.FtpOptions.Directory, ftpport = cumulus.FtpOptions.Port, - sslftp = (int)cumulus.FtpOptions.FtpMode, + sslftp = (int) cumulus.FtpOptions.FtpMode, hostname = cumulus.FtpOptions.Hostname, password = cumulus.FtpOptions.Password, username = cumulus.FtpOptions.Username, @@ -206,7 +215,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error de-serializing Set-up Wizard Settings JSON: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Wizard Data: " + json); context.Response.StatusCode = 500; return msg; @@ -223,7 +232,7 @@ public string UpdateConfig(IHttpContext context) cumulus.FtpOptions.Enabled = settings.internet.ftp.enabled; if (cumulus.FtpOptions.Enabled) { - cumulus.FtpOptions.FtpMode = (Cumulus.FtpProtocols)settings.internet.ftp.sslftp; + cumulus.FtpOptions.FtpMode = (Cumulus.FtpProtocols) settings.internet.ftp.sslftp; if (cumulus.FtpOptions.FtpMode == Cumulus.FtpProtocols.FTP || cumulus.FtpOptions.FtpMode == Cumulus.FtpProtocols.FTPS || cumulus.FtpOptions.FtpMode == Cumulus.FtpProtocols.SFTP) { cumulus.FtpOptions.Directory = string.IsNullOrWhiteSpace(settings.internet.ftp.directory) ? string.Empty : settings.internet.ftp.directory.Trim(); @@ -305,7 +314,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing internet settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -340,7 +349,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing web settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -364,7 +373,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Location settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -400,7 +409,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Units settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -417,7 +426,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Logging setting: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -427,7 +436,7 @@ public string UpdateConfig(IHttpContext context) { if (cumulus.StationType != settings.station.stationtype) { - cumulus.LogMessage("Station type changed, restart required"); + cumulus.LogWarningMessage("Station type changed, restart required"); cumulus.LogConsoleMessage("*** Station type changed, restart required ***", ConsoleColor.Yellow); } cumulus.StationType = settings.station.stationtype; @@ -436,7 +445,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Station Type setting: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -469,7 +478,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Davis VP/VP2/Vue settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -498,7 +507,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing WLL settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -516,7 +525,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing GW1000 settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -535,7 +544,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = $"Error processing WeatherFlow settings: {ex.Message}"; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -552,7 +561,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Fine Offset settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -569,7 +578,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing EasyWeather settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -586,7 +595,7 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing Instromet settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); errorMsg += msg + "\n\n"; context.Response.StatusCode = 500; } @@ -602,18 +611,37 @@ public string UpdateConfig(IHttpContext context) catch (Exception ex) { var msg = "Error processing WMR928 settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); + errorMsg += msg + "\n\n"; + context.Response.StatusCode = 500; + } + + // Ecowitt API + try + { + if (settings.station.ecowittapi != null) + { + cumulus.EcowittApplicationKey = string.IsNullOrWhiteSpace(settings.station.ecowittapi.applicationkey) ? null : settings.station.ecowittapi.applicationkey.Trim(); + cumulus.EcowittUserApiKey = string.IsNullOrWhiteSpace(settings.station.ecowittapi.userkey) ? null : settings.station.ecowittapi.userkey.Trim(); + cumulus.EcowittMacAddress = string.IsNullOrWhiteSpace(settings.station.ecowittapi.mac) ? null : settings.station.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; } + // Save the settings cumulus.WriteIniFile(); } catch (Exception ex) { var msg = "Error processing Wizard settings: " + ex.Message; - cumulus.LogMessage(msg); + cumulus.LogErrorMessage(msg); cumulus.LogDebugMessage("Station Data: " + json); errorMsg += msg; context.Response.StatusCode = 500; @@ -624,10 +652,10 @@ public string UpdateConfig(IHttpContext context) private string degToString(decimal degrees, bool lat) { - var degs = (int)Math.Floor(Math.Abs(degrees)); + var degs = (int) Math.Floor(Math.Abs(degrees)); var minsF = (Math.Abs(degrees) - degs) * 60; - var secs = (int)Math.Round((minsF - Math.Floor(minsF)) * 60); - var mins = (int)Math.Floor(minsF); + var secs = (int) Math.Round((minsF - Math.Floor(minsF)) * 60); + var mins = (int) Math.Floor(minsF); string hemi; if (lat) hemi = degrees >= 0 ? "N" : "S"; @@ -685,6 +713,7 @@ internal class JsonWizardStation public JsonWizardImet imet { get; set; } public JsonStationSettingsWMR928 wmr928 { get; set; } public JsonStationSettingsWeatherFlow weatherflow { get; set; } + public JsonStationSettingsEcowittApi ecowittapi { get; set; } } internal class JsonWizardDavisVp diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 8890a7ca..ff42c1f8 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -5,13 +5,12 @@ using System.Linq; using System.Text; using System.Web; -using FluentFTP.Helpers; -using Org.BouncyCastle.Ocsp; + using Swan; namespace CumulusMX { - public delegate string WebTagFunction(Dictionary tagParams); + public delegate string WebTagFunction(Dictionary tagParams); internal class WebTags { @@ -258,7 +257,7 @@ private double GetSnowDepth(DateTime day) } catch (Exception e) { - cumulus.LogMessage("Error reading diary database: " + e.Message); + cumulus.LogErrorMessage("Error reading diary database: " + e.Message); depth = 0; } return depth; @@ -275,7 +274,7 @@ private int GetSnowLying(DateTime day) } catch (Exception e) { - cumulus.LogMessage("Error reading diary database: " + e.Message); + cumulus.LogErrorMessage("Error reading diary database: " + e.Message); lying = 0; } return lying; @@ -292,7 +291,7 @@ private int GetSnowFalling(DateTime day) } catch (Exception e) { - cumulus.LogMessage("Error reading diary database: " + e.Message); + cumulus.LogErrorMessage("Error reading diary database: " + e.Message); falling = 0; } return falling; @@ -324,7 +323,7 @@ private DateTime GetRecentTs(Dictionary tagParams) } catch { - cumulus.LogMessage($"Error: Webtag <#{tagParams.Get("webtag")}>, expecting an integer value for parameter 'm=n', found 'm={minsagostr}'"); + cumulus.LogWarningMessage($"Error: Webtag <#{tagParams.Get("webtag")}>, expecting an integer value for parameter 'm=n', found 'm={minsagostr}'"); } } if (hoursagostr != null) @@ -335,7 +334,7 @@ private DateTime GetRecentTs(Dictionary tagParams) } catch { - cumulus.LogMessage($"Error: Webtag <#{tagParams.Get("webtag")}>, expecting an integer value for parameter 'h=n', found 'h={hoursagostr}'"); + cumulus.LogWarningMessage($"Error: Webtag <#{tagParams.Get("webtag")}>, expecting an integer value for parameter 'h=n', found 'h={hoursagostr}'"); } } if (daysagostr != null) @@ -346,7 +345,7 @@ private DateTime GetRecentTs(Dictionary tagParams) } catch { - cumulus.LogMessage($"Error: Webtag <#{tagParams.Get("webtag")}>, expecting an integer value for parameter 'd=n', found 'd={daysagostr}'"); + cumulus.LogWarningMessage($"Error: Webtag <#{tagParams.Get("webtag")}>, expecting an integer value for parameter 'd=n', found 'd={daysagostr}'"); } } if (minsago < 0) @@ -371,7 +370,7 @@ private int GetMonthParam(Dictionary tagParams) { // problem converting parameter, use current month instead month = DateTime.Now.Month; - cumulus.LogMessage($"Error: Webtag <#{tagParams.Get("webtag")}> expecting an integer value for parameter 'mon=n', found 'mon={monthstr}'"); + cumulus.LogWarningMessage($"Error: Webtag <#{tagParams.Get("webtag")}> expecting an integer value for parameter 'mon=n', found 'mon={monthstr}'"); } } else @@ -543,8 +542,10 @@ private string TagwindAvg(Dictionary tagParams) // we add on the meteo day start hour to 00:00 today var startOfDay = DateTime.Today.AddHours(-cumulus.GetHourInc()); // then if that is later than now we are still in the previous day, so subtract a day - if (startOfDay > DateTime.Now) + // Allow a 20 second grace into the following day so we still have the previous days total just after rollover + if (startOfDay > DateTime.Now.AddSeconds(-20)) startOfDay = startOfDay.AddDays(-1); + var hours = (DateTime.Now - startOfDay).TotalHours; var timeToday = station.WindRunHourMult[cumulus.Units.Wind] * hours; // just after rollover the numbers will be silly, so return zero for the first 15 minutes @@ -3246,6 +3247,11 @@ private string Tagwebcamurl(Dictionary tagParams) return cumulus.WebcamURL; } + private string TagEcowittCameraUrl(Dictionary tagParams) + { + return string.IsNullOrEmpty(station.EcowittCameraUrl) ? string.Empty : station.EcowittCameraUrl; + } + private string Tagtempunit(Dictionary tagParams) { @@ -4347,7 +4353,6 @@ private string TagHttpUploadAlarm(Dictionary tagParams) return "0"; } - // Monthly highs and lows - values private string TagMonthTempH(Dictionary tagParams) { @@ -5185,7 +5190,7 @@ private string TagSystemUpTime(Dictionary tagParams) } catch (Exception ex) { - cumulus.LogMessage("Error processing SystemUpTime web tag"); + cumulus.LogErrorMessage("Error processing SystemUpTime web tag"); cumulus.LogMessage(ex.Message); return "Error"; } @@ -5490,7 +5495,7 @@ private string TagRecentRain(Dictionary tagParams) return CheckRcDp(CheckPressUnit(result.HasValue ? result.Value : station.RainToday, tagParams), tagParams, cumulus.RainDPlaces); } - private string TagRecentRainToday(Dictionary tagParams) + private string TagRecentRainToday(Dictionary tagParams) { var recentTs = GetRecentTs(tagParams); @@ -5499,7 +5504,7 @@ private string TagRecentRainToday(Dictionary tagParams) return CheckRcDp(CheckRainUnit(result.Count == 0 ? station.RainToday : result[0].RainToday, tagParams), tagParams, cumulus.RainDPlaces); } - private string TagRecentSolarRad(Dictionary tagParams) + private string TagRecentSolarRad(Dictionary tagParams) { var recentTs = GetRecentTs(tagParams); @@ -5508,7 +5513,7 @@ private string TagRecentSolarRad(Dictionary tagParams) return result.Count == 0 ? station.SolarRad.ToString("F0") : result[0].SolarRad.ToString("F0"); } - private string TagRecentUv(Dictionary tagParams) + private string TagRecentUv(Dictionary tagParams) { var recentTs = GetRecentTs(tagParams); @@ -5517,7 +5522,7 @@ private string TagRecentUv(Dictionary tagParams) return CheckRcDp(result.Count == 0 ? station.UV : result[0].UV, tagParams, cumulus.UVDPlaces); } - private string TagRecentTs(Dictionary tagParams) + private string TagRecentTs(Dictionary tagParams) { var recentTs = GetRecentTs(tagParams); @@ -5527,52 +5532,52 @@ private string TagRecentTs(Dictionary tagParams) } // Recent history with commas replaced - private string TagRcRecentOutsideTemp(Dictionary tagParams) + private string TagRcRecentOutsideTemp(Dictionary tagParams) { return ReplaceCommas(TagRecentOutsideTemp(tagParams)); } - private string TagRcRecentWindSpeed(Dictionary tagParams) + private string TagRcRecentWindSpeed(Dictionary tagParams) { return ReplaceCommas(TagRecentWindSpeed(tagParams)); } - private string TagRcRecentWindGust(Dictionary tagParams) + private string TagRcRecentWindGust(Dictionary tagParams) { return ReplaceCommas(TagRecentWindGust(tagParams)); } - private string TagRcRecentWindLatest(Dictionary tagParams) + private string TagRcRecentWindLatest(Dictionary tagParams) { return ReplaceCommas(TagRecentWindLatest(tagParams)); } - private string TagRcRecentWindChill(Dictionary tagParams) + private string TagRcRecentWindChill(Dictionary tagParams) { return ReplaceCommas(TagRecentWindChill(tagParams)); } - private string TagRcRecentDewPoint(Dictionary tagParams) + private string TagRcRecentDewPoint(Dictionary tagParams) { return ReplaceCommas(TagRecentDewPoint(tagParams)); } - private string TagRcRecentHeatIndex(Dictionary tagParams) + private string TagRcRecentHeatIndex(Dictionary tagParams) { return ReplaceCommas(TagRecentHeatIndex(tagParams)); } - private string TagRcRecentPressure(Dictionary tagParams) + private string TagRcRecentPressure(Dictionary tagParams) { return ReplaceCommas(TagRecentPressure(tagParams)); } - private string TagRcRecentRainToday(Dictionary tagParams) + private string TagRcRecentRainToday(Dictionary tagParams) { return ReplaceCommas(TagRecentRainToday(tagParams)); } - private string TagRcRecentUv(Dictionary tagParams) + private string TagRcRecentUv(Dictionary tagParams) { return ReplaceCommas(TagRecentUv(tagParams)); } @@ -5964,6 +5969,7 @@ public void InitialiseWebtags() { "forumurl", Tagforumurl }, { "webcam", Tagwebcam }, { "webcamurl", Tagwebcamurl }, + { "EcowittCameraUrl", TagEcowittCameraUrl }, { "tempunit", Tagtempunit }, { "tempunitnodeg", Tagtempunitnodeg }, { "tempunitnoenc", Tagtempunitnoenc }, @@ -6111,6 +6117,13 @@ public void InitialiseWebtags() { "CO2-pm10-24h", TagCO2_pm10_24h }, { "CO2-temp", TagC02_temp }, { "CO2-hum", TagC02_hum }, + { "CO2_24h", TagCO2_24h }, + { "CO2_pm2p5", TagCO2_pm2p5 }, + { "CO2_pm2p5_24h", TagCO2_pm2p5_24h }, + { "CO2_pm10", TagCO2_pm10 }, + { "CO2_pm10_24h", TagCO2_pm10_24h }, + { "CO2_temp", TagC02_temp }, + { "CO2_hum", TagC02_hum }, { "LeakSensor1", TagLeakSensor1 }, { "LeakSensor2", TagLeakSensor2 }, { "LeakSensor3", TagLeakSensor3 }, @@ -6519,7 +6532,7 @@ public void InitialiseWebtags() } } - public string GetWebTagText(string tagString, Dictionary tagParams) + public string GetWebTagText(string tagString, Dictionary tagParams) { return webTagDictionary.ContainsKey(tagString) ? webTagDictionary[tagString](tagParams) : string.Copy(string.Empty); } diff --git a/Updates.txt b/Updates.txt index 68e16f53..5e860d73 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,4 +1,77 @@ -3.26.0 - b3247 +3.27.0 - b3257 +—————————————— +New +- Adds a new station type of Ecowitt Cloud. This is intended to be used with those Ecowitt console that no not have a local API, cannot use the HTTP protocol, or are located remotely. + - It grabs the data from the Ecowitt cloud servers. This data is updated once a minute and is slightly less rich than the locally available API data. + - This station can also be used as a source of Extra Sensor data for your primary station + - It also makes available the URL of the latest camera on the Ecowitt cloud server via the new web tag <#EcowittCameraUrl> + - You can use the tag in the URL field of HTTP Files to download the latest image +- Adds a new station type of Davis Cloud + - It grabs the data from the Davis WeatherLink cloud servers. The data is updated according to your subscription model + - Pro+ = 1 minute updates + - Pro = 5 minute updates + - Free = 15 minute updates +- Adds new Select-a-Period graphs to the dashboard interface. + - Similar to the existing Select-a-Graph which only show recent 1 minute data, the new charts show data at your logging resolution, but you can select any start and end dates from your historic data. + - Note: selecting a large date range can be quite slow as the data has to be read from the log files +- You can now regenerate ALL missing NOAA reports from either the Month or Year NOAA screens + - Note: Unlike the (Re)Generate button, the new option will not overwrite existing report files, it will only create missing ones +- There is a new Latest Errors display in the dashboard - Utils | Latest Errors + - By default it displays the last 50 Warnings and Errors encountered by CMX, if you are seeing too many warnings that are expected, you can opt to log only errors in Settings | Program Settings | Logging Options +- The Cumulus settings can now be 'secured' with a username/password. NOTE: Because Cumulus does not use HTTPS these credentials can be snooped on the network, the security provided is limited. + - This feature has required some minor changes to the Cumulus API: wsport.json, version.json, dateformat.txt, and csvseparator.txt have moved to a new path /api/info/ +- Add forwarders to Ecowitt Extra Sensors station type +- Add an integrity check to the SQLite database on start-up + + +Changed +- Internal changes to update the Visual Studio project files from the previous legacy version +- Additional keep-alive headers added to PHP uploads +- PHP upload now uses HTTP GET for data sizes < 7000 bytes + - IMPORTANT: You must update the copy of upload.php on your web site with the latest version in this release +- Dashboard gauges now follow the general dark theme +- Adds UV-I to dashboard today/yesterday +- PHP Upload now closes the connection before it errors - if the host server sends a maximum number of allowed requests per session +- The following web tags have been deprecated and will be removed in some future release: + CO2-24h + CO2-pm2p5 + CO2-pm2p5-24h + CO2-pm10 + CO2-temp + CO2-hum + Please switch to the new names: + CO2_24h + CO2_pm2p5 + CO2_pm2p5_24h + CO2_pm10 + CO2_temp + CO2_hum + + +Fixed +- Fix for Davis average wind spike on restarts with catch-up +- Bug in Fine Offset console logger interval setting +- Error saving Locale Strings page - new all-time record alarm was missing +- Default Web Site: + - Fix Select-a-Charts for Davis soil moisture sensors limiting the graph to a maximum value of 100 cb + - webfiles\js\selectachart.js + - Fixed some rounding errors when converting windspeeds to non-native units + - webfiles\lib\steelseries\scripts\gauges.js +- Ecowitt HTTP Station - fixed piezo rain rate not being decoded +- All stations, fixed rain rate being recorded as zero in the recent graphs when MX was calculating the rate rather than the station supplying it +- Wind average calibration is once again applied to live data when the option for MX to calculate the average is enabled +- Davis WLL 10 minute Gust speeds when no broadcasts are being received +- Error 500 on Locale Strings settings page +- Fix CO₂ sensor being visible on graphs by default +- Many Alarms were reverting the Latch Hours value to an integer on restart +- If the graph data display options are changed whilst PHP incremental uploads are in use, then the uploads may start failing +- Web tag #rmidnight was being reset before the EOD processing (midnight rollover) +- Fix bug setting HTTP Ecowitt station custom server +- AirLink Health error messages on start-up when Airlink is associated with the main station +- Davis VP2 applying average wind speed calibration to the gust value in LOOP2 packets + + +3.26.0 - b3248 —————————————— New - MQTT has a new behaviour - Only update a topic if the data has changed