diff --git a/CumulusMX/Alarm.cs b/CumulusMX/Alarm.cs index 6b61e5f3..8ac7a461 100644 --- a/CumulusMX/Alarm.cs +++ b/CumulusMX/Alarm.cs @@ -17,7 +17,7 @@ public bool Triggered get => triggered; set { - if (Program.cumulus.NormalRunning) + if (cumulus.NormalRunning) { doTriggered(value); } @@ -49,7 +49,7 @@ public Alarm(string AlarmName, AlarmTypes AlarmType) public void CheckAlarm(double value) { - if (Program.cumulus.NormalRunning) + if (cumulus.NormalRunning) { doTriggered((type == AlarmTypes.Above && value > Value) || (type == AlarmTypes.Below && value < Value)); } @@ -70,7 +70,7 @@ private void doTriggered(bool value) { cumulus.LogMessage($"Alarm ({Name}): Triggered"); - if (Email && cumulus.SmtpOptions.Enabled) + if (Email && cumulus.SmtpOptions.Enabled && cumulus.emailer != null) { cumulus.LogMessage($"Alarm ({Name}): Sending email"); @@ -147,7 +147,7 @@ public bool UpTriggered get => upTriggered; set { - if (Program.cumulus.NormalRunning) + if (cumulus.NormalRunning) { doUpTriggered(value); } @@ -162,7 +162,7 @@ public bool DownTriggered get => downTriggered; set { - if (Program.cumulus.NormalRunning) + if (cumulus.NormalRunning) { doDownTriggered(value); } @@ -174,7 +174,7 @@ public bool DownTriggered public new void CheckAlarm(double value) { - if (Program.cumulus.NormalRunning) + if (cumulus.NormalRunning) { if (value > Value) @@ -204,12 +204,12 @@ private void doUpTriggered(bool value) { cumulus.LogMessage($"Alarm ({Name}): Up triggered"); - if (Email && cumulus.SmtpOptions.Enabled) + if (Email && cumulus.SmtpOptions.Enabled && cumulus.emailer != null) { cumulus.LogMessage($"Alarm ({Name}): Sending email"); // Construct the message - preamble, plus values - var msg = Program.cumulus.AlarmEmailPreamble + "\r\n" + string.Format(EmailMsgUp, Value, Units); + var msg = cumulus.AlarmEmailPreamble + "\r\n" + string.Format(EmailMsgUp, Value, Units); cumulus.emailer.SendEmail(cumulus.AlarmDestEmail, cumulus.AlarmFromEmail, cumulus.AlarmEmailSubject, msg, cumulus.AlarmEmailHtml); } @@ -270,11 +270,11 @@ private void doDownTriggered(bool value) { cumulus.LogMessage($"Alarm ({Name}): Down triggered"); - if (Email && cumulus.SmtpOptions.Enabled) + if (Email && cumulus.SmtpOptions.Enabled && cumulus.emailer != null) { cumulus.LogMessage($"Alarm ({Name}): Sending email"); // Construct the message - preamble, plus values - var msg = Program.cumulus.AlarmEmailPreamble + "\n" + string.Format(EmailMsgDn, Value, Units); + var msg = cumulus.AlarmEmailPreamble + "\n" + string.Format(EmailMsgDn, Value, Units); cumulus.emailer.SendEmail(cumulus.AlarmDestEmail, cumulus.AlarmFromEmail, cumulus.AlarmEmailSubject, msg, cumulus.AlarmEmailHtml); } diff --git a/CumulusMX/Api.cs b/CumulusMX/Api.cs index a3cc3cec..46da1875 100644 --- a/CumulusMX/Api.cs +++ b/CumulusMX/Api.cs @@ -1443,7 +1443,5 @@ public async Task PostUtilsData(string req) } } } - - } } diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index fe071580..2d4e99b6 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -26,6 +26,7 @@ using SQLite; using Renci.SshNet; using System.Collections.Concurrent; +using System.Security.Authentication; namespace CumulusMX { @@ -447,8 +448,8 @@ public struct TExtraFiles public int RolloverHour; public bool Use10amInSummer; - public double Latitude; - public double Longitude; + public decimal Latitude; + public decimal Longitude; public double Altitude; internal int wsPort; @@ -1032,8 +1033,8 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; - LogMessage("Directory separator=[" + DirectorySeparator + "] Decimal separator=[" + DecimalSeparator + "] List separator=[" + ListSeparator + "]"); - LogMessage("Date separator=[" + CultureInfo.CurrentCulture.DateTimeFormat.DateSeparator + "] Time separator=[" + CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator + "]"); + LogMessage($"Directory separator=[{DirectorySeparator}] Decimal separator=[{DecimalSeparator}] List separator=[{ListSeparator}]"); + LogMessage($"Date separator=[{CultureInfo.CurrentCulture.DateTimeFormat.DateSeparator}] Time separator=[{CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator}]"); TimeZone localZone = TimeZone.CurrentTimeZone; DateTime now = DateTime.Now; @@ -1670,7 +1671,7 @@ public Cumulus(int HTTPport, bool DebugEnabled, string startParms) station.CreateEodGraphDataFiles(); } - //LogDebugMessage("Lock: Cumulus releasing the lock"); + LogDebugMessage("Lock: Cumulus releasing the lock"); syncInit.Release(); } @@ -3315,7 +3316,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, Latitude, 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]); @@ -3379,7 +3380,7 @@ internal void DoMoonImage() if (MoonImage.Enabled) { LogDebugMessage("Generating new Moon image"); - var ret = MoonriseMoonset.CreateMoonImage(MoonPhaseAngle, Latitude, MoonImage.Size); + var ret = MoonriseMoonset.CreateMoonImage(MoonPhaseAngle, (double)Latitude, MoonImage.Size); if (ret == "OK") { @@ -3457,8 +3458,8 @@ private string GetMoonStage(double fAge) private void GetSunriseSunset(DateTime time, out DateTime sunrise, out DateTime sunset, out bool alwaysUp, out bool alwaysDown) { - string rise = SunriseSunset.SunRise(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, Longitude, Latitude); - string set = SunriseSunset.SunSet(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, Longitude, Latitude); + string rise = SunriseSunset.SunRise(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, (double) Longitude, (double) Latitude); + string set = SunriseSunset.SunSet(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, (double) Longitude, (double) Latitude); if (rise.Equals("Always Down") || set.Equals("Always Down")) { @@ -3530,8 +3531,8 @@ private DateTime getSunsetTime(DateTime time) private void GetDawnDusk(DateTime time, out DateTime dawn, out DateTime dusk, out bool alwaysUp, out bool alwaysDown) { - string dawnStr = SunriseSunset.CivilTwilightEnds(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, Longitude, Latitude); - string duskStr = SunriseSunset.CivilTwilightStarts(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, Longitude, Latitude); + string dawnStr = SunriseSunset.CivilTwilightEnds(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, (double) Longitude, (double) Latitude); + string duskStr = SunriseSunset.CivilTwilightStarts(time, TimeZone.CurrentTimeZone.GetUtcOffset(time).TotalHours, (double) Longitude, (double) Latitude); if (dawnStr.Equals("Always Down") || duskStr.Equals("Always Down")) { @@ -3950,14 +3951,14 @@ private void ReadIniFile() //VP2SleepInterval = ini.GetValue("Station", "VP2SleepInterval", 0); DavisOptions.PeriodicDisconnectInterval = ini.GetValue("Station", "VP2PeriodicDisconnectInterval", 0); - Latitude = ini.GetValue("Station", "Latitude", 0.0); + Latitude = ini.GetValue("Station", "Latitude", (decimal) 0.0); if (Latitude > 90 || Latitude < -90) { Latitude = 0; LogMessage($"Error, invalid latitude value in Cumulus.ini [{Latitude}], defaulting to zero."); rewriteRequired = true; } - Longitude = ini.GetValue("Station", "Longitude", 0.0); + Longitude = ini.GetValue("Station", "Longitude", (decimal) 0.0); if (Longitude > 180 || Longitude < -180) { Longitude = 0; @@ -5255,6 +5256,7 @@ private void ReadIniFile() SmtpOptions.RequiresAuthentication = ini.GetValue("SMTP", "RequiresAuthentication", false); SmtpOptions.User = ini.GetValue("SMTP", "User", ""); SmtpOptions.Password = ini.GetValue("SMTP", "Password", ""); + SmtpOptions.IgnoreCertErrors = ini.GetValue("SMTP", "IgnoreCertErrors", false); // Growing Degree Days GrowingBase1 = ini.GetValue("GrowingDD", "BaseTemperature1", (Units.Temp == 0 ? 5.0 : 40.0)); @@ -6112,7 +6114,7 @@ internal void WriteIniFile() ini.SetValue("MySQL", "Host", MySqlConnSettings.Server); - ini.SetValue("MySQL", "Port", MySqlConnSettings.Port); + ini.SetValue("MySQL", "Port", (int) MySqlConnSettings.Port); ini.SetValue("MySQL", "User", MySqlConnSettings.UserID); ini.SetValue("MySQL", "Pass", MySqlConnSettings.Password); ini.SetValue("MySQL", "Database", MySqlConnSettings.Database); @@ -6221,6 +6223,7 @@ internal void WriteIniFile() ini.SetValue("SMTP", "User", SmtpOptions.User); ini.SetValue("SMTP", "Password", SmtpOptions.Password); ini.SetValue("SMTP", "Logging", SmtpOptions.Logging); + ini.SetValue("SMTP", "IgnoreCertErrors", SmtpOptions.IgnoreCertErrors); // Growing Degree Days ini.SetValue("GrowingDD", "BaseTemperature1", GrowingBase1); @@ -7026,7 +7029,7 @@ public async void DoLogFile(DateTime timestamp, bool live) // make sure solar max is calculated for those stations without a solar sensor LogMessage("DoLogFile: Writing log entry for " + timestamp); LogDebugMessage("DoLogFile: max gust: " + station.RecentMaxGust.ToString(WindFormat)); - station.CurrentSolarMax = AstroLib.SolarMax(timestamp, Longitude, Latitude, station.AltitudeM(Altitude), out station.SolarElevation, SolarOptions); + station.CurrentSolarMax = AstroLib.SolarMax(timestamp, (double) Longitude, (double) Latitude, station.AltitudeM(Altitude), out station.SolarElevation, SolarOptions); var filename = GetLogFileName(timestamp); var sb = new StringBuilder(256); @@ -8158,17 +8161,29 @@ public void DoHTMLFiles() public void DoLocalCopy() { var remotePath = ""; - var folderSep1 = Path.DirectorySeparatorChar.ToString(); - var folderSep2 = Path.AltDirectorySeparatorChar.ToString(); - if (!FtpOptions.LocalCopyEnabled) - return; + try + { + var folderSep2 = Path.AltDirectorySeparatorChar; - if (FtpOptions.LocalCopyFolder.Length > 0) + if (!FtpOptions.LocalCopyEnabled) + return; + + if (FtpOptions.LocalCopyFolder.Length > 0) + { + remotePath = (FtpOptions.LocalCopyFolder[-1] == DirectorySeparator || FtpOptions.LocalCopyFolder[-1] == folderSep2) ? FtpOptions.LocalCopyFolder : FtpOptions.LocalCopyFolder + DirectorySeparator; + } + else + { + return; + } + } + catch (Exception ex) { - remotePath = (FtpOptions.LocalCopyFolder.EndsWith(folderSep1) || FtpOptions.LocalCopyFolder.EndsWith(folderSep2) ? FtpOptions.LocalCopyFolder : FtpOptions.LocalCopyFolder + folderSep1); + LogMessage("LocalCopy: Error with paths - " + ex.Message); } + var srcfile = ""; string dstfile; @@ -8179,21 +8194,28 @@ public void DoLocalCopy() // upload NOAA reports LogDebugMessage("LocalCopy: Copying NOAA reports"); - var dstPath = string.IsNullOrEmpty(NOAAconf.CopyFolder) ? remotePath : NOAAconf.CopyFolder; + try + { + var dstPath = string.IsNullOrEmpty(NOAAconf.CopyFolder) ? remotePath : NOAAconf.CopyFolder; - srcfile = ReportPath + NOAAconf.LatestMonthReport; - dstfile = dstPath + folderSep1 + NOAAconf.LatestMonthReport; + srcfile = ReportPath + NOAAconf.LatestMonthReport; + dstfile = dstPath + DirectorySeparator + NOAAconf.LatestMonthReport; - File.Copy(srcfile, dstfile, true); + File.Copy(srcfile, dstfile, true); - srcfile = ReportPath + NOAAconf.LatestYearReport; - dstfile = dstPath + folderSep1 + NOAAconf.LatestYearReport; + srcfile = ReportPath + NOAAconf.LatestYearReport; + dstfile = dstPath + DirectorySeparator + NOAAconf.LatestYearReport; - File.Copy(srcfile, dstfile, true); + File.Copy(srcfile, dstfile, true); - NOAAconf.NeedCopy = false; + NOAAconf.NeedCopy = false; - LogDebugMessage("LocalCopy: Done copying NOAA reports"); + LogDebugMessage("LocalCopy: Done copying NOAA reports"); + } + catch (Exception ex) + { + LogMessage("LocalCopy: Error copy NOAA reports - " + ex.Message); + } } catch (Exception e) { @@ -8202,7 +8224,7 @@ public void DoLocalCopy() } // standard files - LogDebugMessage("LocalCopy: Uploading standard web files"); + LogDebugMessage("LocalCopy: Copying standard web files"); for (var i = 0; i < StdWebFiles.Length; i++) { if (StdWebFiles[i].Copy && StdWebFiles[i].CopyRequired) @@ -8216,7 +8238,7 @@ public void DoLocalCopy() catch (Exception e) { LogMessage($"LocalCopy: Error copying standard data file [{StdWebFiles[i].LocalFileName}]"); - LogMessage($"LocalCopy: Error = {e}"); + LogMessage($"LocalCopy: Error = {e.Message}"); } } } @@ -8227,11 +8249,11 @@ public void DoLocalCopy() { if (GraphDataFiles[i].Copy && GraphDataFiles[i].CopyRequired) { - srcfile = GraphDataFiles[i].LocalPath + GraphDataFiles[i].LocalFileName; - dstfile = remotePath + GraphDataFiles[i].RemoteFileName; - try { + srcfile = GraphDataFiles[i].LocalPath + GraphDataFiles[i].LocalFileName; + dstfile = remotePath + GraphDataFiles[i].RemoteFileName; + File.Copy(srcfile, dstfile, true); // The config files only need uploading once per change if (GraphDataFiles[i].LocalFileName == "availabledata.json" || @@ -8243,7 +8265,7 @@ public void DoLocalCopy() catch (Exception e) { LogMessage($"LocalCopy: Error copying graph data file [{srcfile}]"); - LogMessage($"LocalCopy: Error = {e}"); + LogMessage($"LocalCopy: Error = {e.Message}"); } } } @@ -8254,10 +8276,11 @@ public void DoLocalCopy() { if (GraphDataEodFiles[i].Copy && GraphDataEodFiles[i].CopyRequired) { - srcfile = GraphDataEodFiles[i].LocalPath + GraphDataEodFiles[i].LocalFileName; - dstfile = remotePath + GraphDataEodFiles[i].RemoteFileName; try { + srcfile = GraphDataEodFiles[i].LocalPath + GraphDataEodFiles[i].LocalFileName; + dstfile = remotePath + GraphDataEodFiles[i].RemoteFileName; + File.Copy(srcfile, dstfile, true); // Uploaded OK, reset the upload required flag GraphDataEodFiles[i].CopyRequired = false; @@ -8265,7 +8288,7 @@ public void DoLocalCopy() catch (Exception e) { LogMessage($"LocalCopy: Error copying daily graph data file [{srcfile}]"); - LogMessage($"LocalCopy: Error = {e}"); + LogMessage($"LocalCopy: Error = {e.Message}"); } } } @@ -10312,17 +10335,18 @@ public void MySqlCommandSync(ConcurrentQueue Cmds, string CallingFunctio internal void MySqlCommandErrorHandler(string CallingFunction, int ErrorCode, ConcurrentQueue Cmds) { - var ignore = ErrorCode == (int) MySqlConnector.MySqlErrorCode.ParseError || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.EmptyQuery || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.TooBigSelect || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.InvalidUseOfNull || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.MixOfGroupFunctionAndFields || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.SyntaxError || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.TooLongString || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.WrongColumnName || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.DuplicateUnique || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.PrimaryCannotHaveNull || - ErrorCode == (int) MySqlConnector.MySqlErrorCode.DivisionByZero; + var ignore = ErrorCode == (int) MySqlErrorCode.ParseError || + ErrorCode == (int) MySqlErrorCode.EmptyQuery || + ErrorCode == (int) MySqlErrorCode.TooBigSelect || + ErrorCode == (int) MySqlErrorCode.InvalidUseOfNull || + ErrorCode == (int) MySqlErrorCode.MixOfGroupFunctionAndFields || + ErrorCode == (int) MySqlErrorCode.SyntaxError || + ErrorCode == (int) MySqlErrorCode.TooLongString || + ErrorCode == (int) MySqlErrorCode.WrongColumnName || + ErrorCode == (int) MySqlErrorCode.DuplicateUnique || + ErrorCode == (int) MySqlErrorCode.PrimaryCannotHaveNull || + ErrorCode == (int) MySqlErrorCode.DivisionByZero || + ErrorCode == (int) MySqlErrorCode.DuplicateKeyEntry; if (ignore) { @@ -10492,7 +10516,7 @@ public async void CustomHttpRolloverUpdate() } } - public void DegToDMS(double degrees, out int d, out int m, out int s) + public void DegToDMS(decimal degrees, out int d, out int m, out int s) { int secs = (int)(degrees * 60 * 60); diff --git a/CumulusMX/DataEditor.cs b/CumulusMX/DataEditor.cs index 57988e09..b5e8e241 100644 --- a/CumulusMX/DataEditor.cs +++ b/CumulusMX/DataEditor.cs @@ -3343,26 +3343,26 @@ internal string EditDayFile(IHttpContext context) updt.Append($"TotWindRun={station.DayFile[lineNum].WindRun.ToString("F1", InvC)},"); updt.Append($"HighAvgWSpeed={station.DayFile[lineNum].HighAvgWind.ToString(cumulus.WindAvgFormat, InvC)},"); updt.Append($"THAvgWSpeed={station.DayFile[lineNum].HighAvgWindTime:\\'HH:mm\\'},"); - updt.Append($"LowHum={station.DayFile[lineNum].LowHumidity},"); - updt.Append($"TLowHum={station.DayFile[lineNum].LowHumidityTime:\\'HH:mm\\'},"); - updt.Append($"HighHum={station.DayFile[lineNum].HighHumidity},"); - updt.Append($"THighHum={station.DayFile[lineNum].HighHumidityTime:\\'HH:mm\\'},"); + updt.Append($"LowHum={(station.DayFile[lineNum].LowHumidity < 9999 ? station.DayFile[lineNum].LowHumidity.ToString() : "NULL")},"); + updt.Append($"TLowHum={(station.DayFile[lineNum].LowHumidity < 9999 ? $"'{station.DayFile[lineNum].LowHumidityTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"HighHum={(station.DayFile[lineNum].HighHumidity > -9999 ? station.DayFile[lineNum].HighHumidity.ToString() : "NULL")},"); + updt.Append($"THighHum={(station.DayFile[lineNum].HighHumidity > -9999 ? $"'{station.DayFile[lineNum].HighHumidityTime.ToString("HH:mm")}'" : "NULL")},"); updt.Append($"TotalEvap={station.DayFile[lineNum].ET.ToString(cumulus.ETFormat, InvC)},"); updt.Append($"HoursSun={station.DayFile[lineNum].SunShineHours.ToString(cumulus.SunFormat, InvC)},"); - updt.Append($"HighHeatInd={station.DayFile[lineNum].HighHeatIndex.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"THighHeatInd={station.DayFile[lineNum].HighHeatIndexTime:\\'HH:mm\\'},"); - updt.Append($"HighAppTemp={station.DayFile[lineNum].HighAppTemp.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"THighAppTemp={station.DayFile[lineNum].HighAppTempTime:\\'HH:mm\\'},"); - updt.Append($"LowAppTemp={station.DayFile[lineNum].LowAppTemp.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"TLowAppTemp={station.DayFile[lineNum].LowAppTempTime:\\'HH:mm\\'},"); + updt.Append($"HighHeatInd={(station.DayFile[lineNum].HighHeatIndex > -9999 ? station.DayFile[lineNum].HighHeatIndex.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"THighHeatInd={(station.DayFile[lineNum].HighHeatIndex > -9999 ? $"'{station.DayFile[lineNum].HighHeatIndexTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"HighAppTemp={(station.DayFile[lineNum].HighAppTemp > -9999 ? station.DayFile[lineNum].HighAppTemp.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"THighAppTemp={(station.DayFile[lineNum].HighAppTemp > -9999 ? $"'{station.DayFile[lineNum].HighAppTempTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"LowAppTemp={(station.DayFile[lineNum].LowAppTemp < 9999 ? station.DayFile[lineNum].LowAppTemp.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"TLowAppTemp={(station.DayFile[lineNum].LowAppTemp < 9999 ? $"'{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\\'},"); - updt.Append($"LowWindChill={station.DayFile[lineNum].LowWindChill.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"TLowWindChill={station.DayFile[lineNum].LowWindChillTime:\\'HH:mm\\'},"); - updt.Append($"HighDewPoint={station.DayFile[lineNum].HighDewPoint.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"THighDewPoint={station.DayFile[lineNum].HighDewPointTime:\\'HH:mm\\'},"); - updt.Append($"LowDewPoint={station.DayFile[lineNum].LowDewPoint.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"TLowDewPoint={station.DayFile[lineNum].LowDewPointTime:\\'HH:mm\\'},"); + updt.Append($"LowWindChill={(station.DayFile[lineNum].LowWindChill < 9999 ? station.DayFile[lineNum].LowWindChill.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"TLowWindChill={(station.DayFile[lineNum].LowWindChill < 9999 ? $"'{station.DayFile[lineNum].LowWindChillTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"HighDewPoint={(station.DayFile[lineNum].HighDewPoint > -9999 ? station.DayFile[lineNum].HighDewPoint.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"THighDewPoint={(station.DayFile[lineNum].HighDewPoint > -9999 ? $"'{station.DayFile[lineNum].HighDewPointTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"LowDewPoint={(station.DayFile[lineNum].LowDewPoint < 9999 ? station.DayFile[lineNum].LowDewPoint.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"TLowDewPoint={(station.DayFile[lineNum].LowDewPoint < 9999 ? $"'{station.DayFile[lineNum].LowDewPointTime.ToString("HH:mm")}'" : "NULL")},"); updt.Append($"DomWindDir={station.DayFile[lineNum].DominantWindBearing},"); updt.Append($"HeatDegDays={station.DayFile[lineNum].HeatingDegreeDays.ToString("F1", InvC)},"); updt.Append($"CoolDegDays={station.DayFile[lineNum].CoolingDegreeDays.ToString("F1", InvC)},"); @@ -3372,12 +3372,15 @@ internal string EditDayFile(IHttpContext context) updt.Append($"THighUV={station.DayFile[lineNum].HighUvTime:\\'HH:mm\\'},"); updt.Append($"HWindGBearSym='{station.CompassPoint(station.DayFile[lineNum].HighGustBearing)}',"); updt.Append($"DomWindDirSym='{station.CompassPoint(station.DayFile[lineNum].DominantWindBearing)}',"); - updt.Append($"MaxFeelsLike={station.DayFile[lineNum].HighFeelsLike.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"TMaxFeelsLike={station.DayFile[lineNum].HighFeelsLikeTime:\\'HH:mm\\'},"); - updt.Append($"MinFeelsLike={station.DayFile[lineNum].LowFeelsLike.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"TMinFeelsLike={station.DayFile[lineNum].LowFeelsLikeTime:\\'HH:mm\\'},"); - updt.Append($"MaxHumidex={station.DayFile[lineNum].HighHumidex.ToString(cumulus.TempFormat, InvC)},"); - updt.Append($"TMaxHumidex={station.DayFile[lineNum].HighFeelsLikeTime:\\'HH:mm\\'} "); + updt.Append($"MaxFeelsLike={(station.DayFile[lineNum].HighFeelsLike > -9999 ? station.DayFile[lineNum].HighFeelsLike.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"TMaxFeelsLike={(station.DayFile[lineNum].HighFeelsLike > -9999 ? $"'{station.DayFile[lineNum].HighFeelsLikeTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"MinFeelsLike={(station.DayFile[lineNum].LowFeelsLike < 9999 ? station.DayFile[lineNum].LowFeelsLike.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"TMinFeelsLike={(station.DayFile[lineNum].LowFeelsLike < 9999 ? $"'{station.DayFile[lineNum].LowFeelsLikeTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"MaxHumidex={(station.DayFile[lineNum].HighHumidex > -9999 ? station.DayFile[lineNum].HighHumidex.ToString(cumulus.TempFormat, InvC) : "NULL")},"); + updt.Append($"TMaxHumidex={(station.DayFile[lineNum].HighHumidex > -9999 ? $"'{station.DayFile[lineNum].HighHumidexTime.ToString("HH:mm")}'" : "NULL")},"); + updt.Append($"ChillHours={(station.DayFile[lineNum].ChillHours > -9999 ? station.DayFile[lineNum].ChillHours.ToString("F1", InvC) : "NULL")},"); + updt.Append($"HighRain24h={(station.DayFile[lineNum].HighRain24h > -9999 ? station.DayFile[lineNum].HighRain24h.ToString(cumulus.RainFormat, InvC) : "NULL")},"); + updt.Append($"THighRain24h={(station.DayFile[lineNum].HighRain24h > -9999 ? $"'{station.DayFile[lineNum].HighRain24hTime.ToString("HH:mm")}'" : "NULL")} "); updt.Append($"WHERE LogDate='{station.DayFile[lineNum].Date:yyyy-MM-dd}';"); updateStr = updt.ToString(); diff --git a/CumulusMX/EmailSender.cs b/CumulusMX/EmailSender.cs index eb2f4a72..4a6d5497 100644 --- a/CumulusMX/EmailSender.cs +++ b/CumulusMX/EmailSender.cs @@ -57,6 +57,11 @@ public async void SendEmail(string[] to, string from, string subject, string mes using (SmtpClient client = cumulus.SmtpOptions.Logging ? new SmtpClient(new ProtocolLogger("MXdiags/smtp.log")) : new SmtpClient()) { + if (cumulus.SmtpOptions.IgnoreCertErrors) + { + client.ServerCertificateValidationCallback = (s, c, h, e) => true; + } + 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 @@ -192,6 +197,7 @@ public class SmtpOptions public int SslOption; public bool RequiresAuthentication; public bool Logging; + public bool IgnoreCertErrors; } } } diff --git a/CumulusMX/IniFile.cs b/CumulusMX/IniFile.cs index c6974cba..ad65221d 100644 --- a/CumulusMX/IniFile.cs +++ b/CumulusMX/IniFile.cs @@ -393,6 +393,14 @@ internal double GetValue(string SectionName, string Key, double DefaultValue) return DefaultValue; } + internal decimal GetValue(string SectionName, string Key, decimal DefaultValue) + { + string StringValue = GetValue(SectionName, Key, DefaultValue.ToString(CultureInfo.InvariantCulture)); + decimal Value; + if (decimal.TryParse(StringValue, NumberStyles.Any, CultureInfo.InvariantCulture, out Value)) return Value; + return DefaultValue; + } + internal byte[] GetValue(string SectionName, string Key, byte[] DefaultValue) { string StringValue = GetValue(SectionName, Key, EncodeByteArray(DefaultValue)); @@ -430,6 +438,11 @@ internal void SetValue(string SectionName, string Key, double Value) SetValue(SectionName, Key, Value.ToString("G17", CultureInfo.InvariantCulture)); } + internal void SetValue(string SectionName, string Key, decimal Value) + { + SetValue(SectionName, Key, Value.ToString(CultureInfo.InvariantCulture)); + } + internal void SetValue(string SectionName, string Key, byte[] Value) { SetValue(SectionName, Key, EncodeByteArray(Value)); diff --git a/CumulusMX/InternetSettings.cs b/CumulusMX/InternetSettings.cs index 65e804f9..0864aeb3 100644 --- a/CumulusMX/InternetSettings.cs +++ b/CumulusMX/InternetSettings.cs @@ -6,6 +6,7 @@ using System.Web; using ServiceStack; using EmbedIO; +using static ServiceStack.Diagnostics.Events; namespace CumulusMX { @@ -269,6 +270,7 @@ public string UpdateConfig(IHttpContext context) cumulus.SmtpOptions.RequiresAuthentication = settings.emailsettings.authenticate; cumulus.SmtpOptions.User = settings.emailsettings.user; cumulus.SmtpOptions.Password = settings.emailsettings.password; + cumulus.SmtpOptions.IgnoreCertErrors = settings.emailsettings.ignorecerterrors; if (cumulus.emailer == null) { @@ -523,7 +525,8 @@ public string GetAlpacaFormData() ssloption = cumulus.SmtpOptions.SslOption, authenticate = cumulus.SmtpOptions.RequiresAuthentication, user = cumulus.SmtpOptions.User, - password = cumulus.SmtpOptions.Password + password = cumulus.SmtpOptions.Password, + ignorecerterrors = cumulus.SmtpOptions.IgnoreCertErrors }; var misc = new JsonInternetSettingsMisc() @@ -800,6 +803,7 @@ public class JsonEmailSettings public bool authenticate { get; set; } public string user { get; set; } public string password { get; set; } + public bool ignorecerterrors { get; set; } } public class JsonInternetSettingsMisc diff --git a/CumulusMX/MysqlSettings.cs b/CumulusMX/MysqlSettings.cs index 8be1e11c..78308cd9 100644 --- a/CumulusMX/MysqlSettings.cs +++ b/CumulusMX/MysqlSettings.cs @@ -5,6 +5,8 @@ using ServiceStack; using EmbedIO; using System.Text; +using MySqlConnector.Logging; +using System.Collections.Generic; namespace CumulusMX { @@ -340,36 +342,47 @@ private string UpdateMySQLTable(MySqlTable table) { mySqlConn.Open(); - // first get a count of the columns the table currenty has - using (MySqlCommand cmd = new MySqlCommand($"SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='{table.Name}' AND TABLE_SCHEMA='{cumulus.MySqlConnSettings.Database}'", mySqlConn)) + // first get a list of the columns the table currenty has + var currCols = new List(); + using (MySqlCommand cmd = new MySqlCommand($"SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='{table.Name}' AND TABLE_SCHEMA='{cumulus.MySqlConnSettings.Database}'", mySqlConn)) + using (MySqlDataReader reader = cmd.ExecuteReader()) { - colsNow = int.Parse(cmd.ExecuteScalar().ToString()); + if (reader.HasRows) + { + while (reader.Read()) + { + var col = reader.GetString(0); + currCols.Add(col); + } + } } - if (colsNow < table.Columns.Count) + var update = new StringBuilder("ALTER TABLE " + table.Name, 1024); + foreach (var newCol in table.Columns) { - var update = new StringBuilder("ALTER TABLE " + table.Name, 1024); - while (colsNow < table.Columns.Count) + if (!currCols.Contains(newCol.Name)) { - update.Append($" ADD COLUMN {table.Columns[colsNow].Name} {table.Columns[colsNow].Attributes},"); - colsNow++; + update.Append($" ADD COLUMN {newCol.Name} {newCol.Attributes},"); cnt++; } + } + if (cnt > 0) + { // strip trailing comma update.Length--; - using(MySqlCommand cmd = new MySqlCommand(update.ToString(), mySqlConn)) + using (MySqlCommand cmd = new MySqlCommand(update.ToString(), mySqlConn)) { int aff = cmd.ExecuteNonQuery(); - cumulus.LogMessage($"MySQL Update Table: {aff} items were affected."); res = $"Added {cnt} columns to {table.Name} table"; - + cumulus.LogMessage($"MySQL Update Table: " + res); } } else { - res = $"The {table.Name} already has the correct number of columns = {table.Columns.Count}"; + res = $"The {table.Name} table already has all the required columns = {table.Columns.Count}"; + cumulus.LogMessage("MySQL Update Table: " + res); } } } diff --git a/CumulusMX/NOAA.cs b/CumulusMX/NOAA.cs index 89d833ff..6b61b775 100644 --- a/CumulusMX/NOAA.cs +++ b/CumulusMX/NOAA.cs @@ -103,12 +103,12 @@ private string CompassPoint(int bearing) return cumulus.compassp[(((bearing * 100) + 1125) % 36000) / 2250]; } - private static double Frac(double num) + private static decimal Frac(decimal num) { return num - Math.Floor(num); } - private void DecodeLatLong(double latLong, out int deg, out int min, out int sec) + private void DecodeLatLong(decimal latLong, out int deg, out int min, out int sec) { deg = (int) Math.Floor(latLong); latLong = Frac(latLong) * 60; diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index 422fef68..de4bd52f 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Cumulus MX")] -[assembly: AssemblyDescription("Version 3.20.0 - Build 3202")] +[assembly: AssemblyDescription("Version 3.20.1 - Build 3203")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Cumulus MX")] @@ -32,5 +32,5 @@ // 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.20.0.3202")] -[assembly: AssemblyFileVersion("3.20.0.3202")] +[assembly: AssemblyVersion("3.20.1.3203")] +[assembly: AssemblyFileVersion("3.20.1.3203")] diff --git a/CumulusMX/Simulator.cs b/CumulusMX/Simulator.cs index d49f0ef7..32a4bb7e 100644 --- a/CumulusMX/Simulator.cs +++ b/CumulusMX/Simulator.cs @@ -144,7 +144,7 @@ private void doSolar(DateTime recDate) // if we are starting up, set the intial solar rad value to 90% of theoretical if (!solarIntialised) { - CurrentSolarMax = AstroLib.SolarMax(recDate, cumulus.Longitude, cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions); + CurrentSolarMax = AstroLib.SolarMax(recDate, (double) cumulus.Longitude, (double) cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions); solar = CurrentSolarMax * 0.9; solarIntialised = true; } diff --git a/CumulusMX/StationSettings.cs b/CumulusMX/StationSettings.cs index 4e9316f9..877fac6a 100644 --- a/CumulusMX/StationSettings.cs +++ b/CumulusMX/StationSettings.cs @@ -489,9 +489,9 @@ internal string GetAlpacaFormData() return JsonSerializer.SerializeToString(data); } - private void LongToDMS(double longitude, out int d, out int m, out int s, out string hem) + private void LongToDMS(decimal longitude, out int d, out int m, out int s, out string hem) { - double coordinate; + decimal coordinate; if (longitude < 0) { coordinate = -longitude; @@ -512,9 +512,9 @@ private void LongToDMS(double longitude, out int d, out int m, out int s, out st d = secs / 60; } - private void LatToDMS(double latitude, out int d, out int m, out int s, out string hem) + private void LatToDMS(decimal latitude, out int d, out int m, out int s, out string hem) { - double coordinate; + decimal coordinate; if (latitude < 0) { coordinate = -latitude; @@ -720,7 +720,7 @@ internal string UpdateConfig(IHttpContext context) cumulus.LocationName = settings.general.Location.sitename ?? string.Empty; cumulus.LocationDesc = settings.general.Location.description ?? string.Empty; - cumulus.Latitude = settings.general.Location.Latitude.degrees + (settings.general.Location.Latitude.minutes / 60.0) + (settings.general.Location.Latitude.seconds / 3600.0); + cumulus.Latitude = (decimal) (settings.general.Location.Latitude.degrees + (settings.general.Location.Latitude.minutes / 60.0) + (settings.general.Location.Latitude.seconds / 3600.0)); if (settings.general.Location.Latitude.hemisphere == "South") { cumulus.Latitude = -cumulus.Latitude; @@ -729,7 +729,7 @@ internal string UpdateConfig(IHttpContext context) cumulus.LatTxt = string.Format("{0} {1:D2}° {2:D2}' {3:D2}"", settings.general.Location.Latitude.hemisphere[0], settings.general.Location.Latitude.degrees, settings.general.Location.Latitude.minutes, settings.general.Location.Latitude.seconds); - cumulus.Longitude = settings.general.Location.Longitude.degrees + (settings.general.Location.Longitude.minutes / 60.0) + (settings.general.Location.Longitude.seconds / 3600.0); + cumulus.Longitude = (decimal) (settings.general.Location.Longitude.degrees + (settings.general.Location.Longitude.minutes / 60.0) + (settings.general.Location.Longitude.seconds / 3600.0)); if (settings.general.Location.Longitude.hemisphere == "West") { cumulus.Longitude = -cumulus.Longitude; diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 7aab7744..28b40ac5 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -1674,7 +1674,7 @@ private void MinuteChanged(DateTime now) { CheckForDataStopped(); - CurrentSolarMax = AstroLib.SolarMax(now, cumulus.Longitude, cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions); + CurrentSolarMax = AstroLib.SolarMax(now, (double) cumulus.Longitude, (double) cumulus.Latitude, AltitudeM(cumulus.Altitude), out SolarElevation, cumulus.SolarOptions); if (!DataStopped) { @@ -12214,7 +12214,7 @@ public void UpdateAPRS() private string APRSLat(Cumulus cumulus) { string dir; - double lat; + decimal lat; int d, m, s; if (cumulus.Latitude < 0) { @@ -12242,7 +12242,7 @@ private string APRSLat(Cumulus cumulus) private string APRSLon(Cumulus cumulus) { string dir; - double lon; + decimal lon; int d, m, s; if (cumulus.Longitude < 0) { diff --git a/CumulusMX/Wizard.cs b/CumulusMX/Wizard.cs index 28621e38..e7125398 100644 --- a/CumulusMX/Wizard.cs +++ b/CumulusMX/Wizard.cs @@ -611,11 +611,11 @@ public string UpdateConfig(IHttpContext context) return context.Response.StatusCode == 200 ? "success" : errorMsg; } - private string degToString(double degrees, bool lat) + private string degToString(decimal degrees, bool lat) { var degs = (int)Math.Floor(Math.Abs(degrees)); - var minsF = (Math.Abs(degrees) - degs) * 60.0; - var secs = (int)Math.Round((minsF - Math.Floor(minsF)) * 60.0); + var minsF = (Math.Abs(degrees) - degs) * 60; + var secs = (int)Math.Round((minsF - Math.Floor(minsF)) * 60); var mins = (int)Math.Floor(minsF); string hemi; if (lat) @@ -639,8 +639,8 @@ internal class JsonWizard internal class JsonWizardLocation { - public double latitude { get; set; } - public double longitude { get; set; } + public decimal latitude { get; set; } + public decimal longitude { get; set; } public int altitude { get; set; } public string altitudeunit { get; set; } public string sitename { get; set; } diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index 5df517b9..ff2e2311 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -129,6 +129,30 @@ private static string CheckRcDp(double val, Dictionary tagParams } } + private static string CheckRcDp(decimal val, Dictionary tagParams, int decimals) + { + string ret; + try + { + if (tagParams.Get("tc") == "y") + return Math.Truncate(val).ToString(); + + int dp = int.TryParse(tagParams.Get("dp"), out dp) ? dp : decimals; + + ret = val.ToString("F" + dp); + + if (tagParams.Get("rc") == "y") + { + ret = ReplaceCommas(ret); + } + return ret; + } + catch + { + return "error"; + } + } + private double GetSnowDepth(DateTime day) { double depth; @@ -1027,7 +1051,7 @@ private string TagAirLinkTempIn(Dictionary tagParams) } private string TagAirLinkHumIn(Dictionary tagParams) { - return cumulus.airLinkDataIn == null ? "--" : CheckRcDp(cumulus.airLinkDataIn.humidity, tagParams, cumulus.HumDPlaces); + return cumulus.airLinkDataIn == null ? "--" : CheckRcDp((decimal) cumulus.airLinkDataIn.humidity, tagParams, cumulus.HumDPlaces); } private string TagAirLinkPm1In(Dictionary tagParams) { @@ -1090,7 +1114,7 @@ private string TagAirLinkTempOut(Dictionary tagParams) } private string TagAirLinkHumOut(Dictionary tagParams) { - return cumulus.airLinkDataOut == null ? "--" : CheckRcDp(cumulus.airLinkDataOut.humidity, tagParams, cumulus.HumDPlaces); + return cumulus.airLinkDataOut == null ? "--" : CheckRcDp((decimal) cumulus.airLinkDataOut.humidity, tagParams, cumulus.HumDPlaces); } private string TagAirLinkPm1Out(Dictionary tagParams) { diff --git a/Updates.txt b/Updates.txt index a010a698..3275903c 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,3 +1,20 @@ +3.20.1 - b3203 +—————————————— +Fixed +- Fix crash in Alarms with some station types +- Change the MySQL table updates to compare column names rather than simple counts +- Fix the All Time, Monthly, This Year records editors not allowing 24 hour rain values to be changed +- Fix Dayfile editor not updating ChillHours and 24 hour rainfall values in the MySQL database +- Fix Dayfile editor inserting 9999/-9999 values into the MySQL database when values are missing + +New +- Adds a new option to the email server settings to ignore certificate errors + +Changed +- The station latitude and longitude are now stored internally as decimal values. This means there will be no loss of precision when storing locations entered as decimal values via the + configuration wizard, or directly in the Cumulus.ini file + + 3.20.0 - b3202 —————————————— Fixed