diff --git a/CumulusMX/ConvertUnits.cs b/CumulusMX/ConvertUnits.cs
index 4c799b8c..9089d4fd 100644
--- a/CumulusMX/ConvertUnits.cs
+++ b/CumulusMX/ConvertUnits.cs
@@ -114,6 +114,23 @@ public static double WindMPHToUser(double value)
};
}
+ ///
+ /// Converts wind supplied in knots to user units
+ ///
+ /// Wind in knots
+ /// Wind in configured units
+ public static double WindKnotsToUser(double value)
+ {
+ return Program.cumulus.Units.Wind switch
+ {
+ 0 => value * 0.5144444,
+ 1 => value * 1.150779,
+ 2 => value * 1.852,
+ 3 => value,
+ _ => 0,
+ };
+ }
+
///
/// Converts wind supplied in kph to user units
///
diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs
index 88624a29..64bdd82d 100644
--- a/CumulusMX/Cumulus.cs
+++ b/CumulusMX/Cumulus.cs
@@ -46,8 +46,10 @@
using Swan;
+using static System.Net.WebRequestMethods;
using static CumulusMX.EmailSender;
+using File = System.IO.File;
using Timer = System.Timers.Timer;
namespace CumulusMX
@@ -3520,6 +3522,7 @@ private void ReadIniFile()
// check for Cumulus 1 [FTP Site] and correct it
if (ini.ValueExists("FTP Site", "Port"))
{
+ LogMessage("Cumulus.ini: Changing old [FTP Site] to [FTP site]");
var contents = File.ReadAllText("Cumulus.ini");
contents = contents.Replace("[FTP Site]", "[FTP site]");
File.WriteAllText("Cumulus.ini", contents);
@@ -3609,6 +3612,7 @@ private void ReadIniFile()
DavisOptions.IncrementPressureDP = ini.GetValue("Station", "DavisIncrementPressureDP", false);
if (StationType == StationTypes.VantagePro && DavisOptions.UseLoop2)
{
+ LogMessage("Cumulus.ini: Disabling LOOP2 for old VP station");
DavisOptions.UseLoop2 = false;
rewriteRequired = true;
}
@@ -3617,7 +3621,7 @@ private void ReadIniFile()
if (!DavisBaudRates.Contains(DavisOptions.BaudRate))
{
// nope, that isn't allowed, set the default
- LogMessage("Error, the value for DavisBaudRate in the ini file " + DavisOptions.BaudRate + " is not valid, using default 19200.");
+ LogMessage("Cumulus.ini: Error, the value for DavisBaudRate in the ini file " + DavisOptions.BaudRate + " is not valid, using default 19200.");
DavisOptions.BaudRate = 19200;
rewriteRequired = true;
}
@@ -3625,10 +3629,15 @@ private void ReadIniFile()
DavisOptions.RainGaugeType = ini.GetValue("Station", "VPrainGaugeType", -1);
if (DavisOptions.RainGaugeType > 3)
{
+ LogMessage("Cumulus.ini: Invalid Davis rain gauge type, defaulting to -1");
DavisOptions.RainGaugeType = -1;
rewriteRequired = true;
}
- DavisOptions.ConnectionType = ini.GetValue("Station", "VP2ConnectionType", 0, 0, 1);
+ DavisOptions.ConnectionType = ini.GetValue("Station", "VP2ConnectionType", 0, 0, 2);
+ if (DavisOptions.ConnectionType == 1)
+ {
+ DavisOptions.ConnectionType = 2;
+ }
DavisOptions.TCPPort = ini.GetValue("Station", "VP2TCPPort", 22222, 1, 65535);
DavisOptions.IPAddr = ini.GetValue("Station", "VP2IPAddr", "0.0.0.0");
@@ -3643,14 +3652,14 @@ private void ReadIniFile()
if (Latitude > 90 || Latitude < -90)
{
Latitude = 0;
- LogErrorMessage($"Error, invalid latitude value in Cumulus.ini [{Latitude}], defaulting to zero.");
+ LogErrorMessage($"Cumulus.ini: Error, invalid latitude value [{Latitude}], defaulting to zero.");
rewriteRequired = true;
}
Longitude = ini.GetValue("Station", "Longitude", (decimal) 0.0);
if (Longitude > 180 || Longitude < -180)
{
Longitude = 0;
- LogErrorMessage($"Error, invalid longitude value in Cumulus.ini [{Longitude}], defaulting to zero.");
+ LogErrorMessage($"Cumulus.ini: Error, invalid longitude value [{Longitude}], defaulting to zero.");
rewriteRequired = true;
}
@@ -3811,10 +3820,11 @@ private void ReadIniFile()
{
RecordsBeganDateTime = DateTime.Parse(RecordsBeganDate, CultureInfo.CurrentCulture);
recreateRequired = true;
+ LogMessage($"Cumulus.ini: Changing old StartDate [{RecordsBeganDate}] to StartDateIso [{RecordsBeganDateTime:yyyy-MM-dd}]");
}
catch (Exception ex)
{
- LogErrorMessage($"Error parsing the RecordsBegan date {RecordsBeganDate}: {ex.Message}");
+ LogErrorMessage($"Cumulus.ini: Error parsing the RecordsBegan date {RecordsBeganDate}: {ex.Message}");
}
}
else
@@ -3823,7 +3833,7 @@ private void ReadIniFile()
RecordsBeganDateTime = DateTime.ParseExact(RecordsBeganDate, "yyyy-MM-dd", CultureInfo.InvariantCulture);
}
- LogMessage($"Cumulus start date Parsed: {RecordsBeganDateTime:yyyy-MM-dd}");
+ LogMessage($"Cumulus.ini: Start date Parsed: {RecordsBeganDateTime:yyyy-MM-dd}");
ImetOptions.WaitTime = ini.GetValue("Station", "ImetWaitTime", 500, 0);
ImetOptions.ReadDelay = ini.GetValue("Station", "ImetReadDelay", 500, 0);
@@ -3833,7 +3843,7 @@ private void ReadIniFile()
if (!ImetOptions.BaudRates.Contains(ImetOptions.BaudRate))
{
// nope, that isn't allowed, set the default
- LogMessage("Error, the value for ImetOptions.ImetBaudRate in the ini file " + ImetOptions.BaudRate + " is not valid, using default 19200.");
+ LogMessage("Cumulus.ini: Error, the value for ImetOptions.ImetBaudRate " + ImetOptions.BaudRate + " is not valid, using default 19200.");
ImetOptions.BaudRate = 19200;
rewriteRequired = true;
}
@@ -3862,6 +3872,7 @@ private void ReadIniFile()
if (ChillHourThreshold < -998)
{
ChillHourThreshold = Units.Temp == 0 ? 7 : 45;
+ LogMessage("Cumulus.ini: Defaulting ChillHourThreshold to " + ChillHourThreshold);
rewriteRequired = true;
}
@@ -4036,6 +4047,7 @@ private void ReadIniFile()
if (AirLinkInStationId == -1 && AirLinkIsNode)
{
AirLinkInStationId = WllStationId;
+ LogMessage("Cumulus.ini: No AirLinkInStationId supplied, but AirlinkIsNode, so using main station id");
rewriteRequired = true;
}
AirLinkInHostName = ini.GetValue("AirLink", "In-Hostname", string.Empty);
@@ -4046,6 +4058,7 @@ private void ReadIniFile()
if (AirLinkOutStationId == -1 && AirLinkIsNode)
{
AirLinkOutStationId = WllStationId;
+ LogMessage("Cumulus.ini: No AirLinkOutStationId supplied, but AirlinkIsNode, so using main station id");
rewriteRequired = true;
}
AirLinkOutHostName = ini.GetValue("AirLink", "Out-Hostname", string.Empty);
@@ -4061,6 +4074,7 @@ private void ReadIniFile()
FtpOptions.FtpMode = (FtpProtocols) ini.GetValue("FTP site", "Sslftp", 0, 0, 3);
if (FtpOptions.Enabled && FtpOptions.Hostname == string.Empty && FtpOptions.FtpMode != FtpProtocols.PHP)
{
+ LogMessage("Cumulus.ini: FTP enabled, but no hostname supplied, disabling FTP");
FtpOptions.Enabled = false;
rewriteRequired = true;
}
@@ -4073,14 +4087,14 @@ private void ReadIniFile()
if (!sshAuthenticationVals.Contains(FtpOptions.SshAuthen))
{
FtpOptions.SshAuthen = "password";
- LogWarningMessage($"Error, invalid SshFtpAuthentication value in Cumulus.ini [{FtpOptions.SshAuthen}], defaulting to Password.");
+ LogWarningMessage($"Cumulus.ini: Error, invalid SshFtpAuthentication value [{FtpOptions.SshAuthen}], defaulting to Password.");
rewriteRequired = true;
}
FtpOptions.SshPskFile = ini.GetValue("FTP site", "SshFtpPskFile", string.Empty);
if (FtpOptions.SshPskFile.Length > 0 && (FtpOptions.SshAuthen == "psk" || FtpOptions.SshAuthen == "password_psk") && !File.Exists(FtpOptions.SshPskFile))
{
FtpOptions.SshPskFile = string.Empty;
- LogErrorMessage($"Error, file name specified by SshFtpPskFile value in Cumulus.ini does not exist [{FtpOptions.SshPskFile}].");
+ LogErrorMessage($"Cumulus.ini: Error, file name specified by SshFtpPskFile does not exist [{FtpOptions.SshPskFile}], removing it.");
rewriteRequired = true;
}
FtpOptions.DisableEPSV = ini.GetValue("FTP site", "DisableEPSV", false);
@@ -4099,6 +4113,7 @@ private void ReadIniFile()
!(FtpOptions.LocalCopyFolder.EndsWith(sep1) || FtpOptions.LocalCopyFolder.EndsWith(sep2))
)
{
+ LogMessage("Cumulus.ini: Local copy folder does not end with a directory separator, adding it");
FtpOptions.LocalCopyFolder += sep1;
rewriteRequired = true;
}
@@ -4114,6 +4129,7 @@ private void ReadIniFile()
if (FtpOptions.Enabled && FtpOptions.PhpUrl == string.Empty && FtpOptions.FtpMode == FtpProtocols.PHP)
{
+ LogMessage("Cumulus.ini: PHP upload enabled but the target URL is missing, disabling uploads");
FtpOptions.Enabled = false;
rewriteRequired = true;
}
@@ -4139,6 +4155,7 @@ private void ReadIniFile()
UpdateInterval = ini.GetValue("FTP site", "UpdateInterval", DefaultWebUpdateInterval);
if (UpdateInterval < 1)
{
+ LogMessage("Cumulus.ini: Update interval invalid, resetting to 1");
UpdateInterval = 1;
rewriteRequired = true;
}
@@ -4396,6 +4413,7 @@ private void ReadIniFile()
Windy.Interval = ini.GetValue("Windy", "Interval", Windy.DefaultInterval);
if (Windy.Interval < 5)
{
+ LogMessage("Cumulus.ini: Windy upload interval set to less than 5 mins, resetting to 5");
Windy.Interval = 5;
rewriteRequired = true;
}
@@ -4425,6 +4443,7 @@ private void ReadIniFile()
WindGuru.Interval = ini.GetValue("WindGuru", "Interval", WindGuru.DefaultInterval);
if (WindGuru.Interval < 1)
{
+ LogMessage("Cumulus.ini: WindGuru update interval invalid, resetting to 1");
WindGuru.Interval = 1;
rewriteRequired = true;
}
@@ -4481,6 +4500,7 @@ private void ReadIniFile()
MQTT.IpVersion = ini.GetValue("MQTT", "IPversion", 0, 0, 6); // 0 = unspecified, 4 = force IPv4, 6 = force IPv6
if (MQTT.IpVersion != 0 && MQTT.IpVersion != 4 && MQTT.IpVersion != 6)
{
+ LogMessage("Cumulus.ini: MQTT IP Version invalid, restting to unspecified");
MQTT.IpVersion = 0;
rewriteRequired = true;
}
@@ -4943,54 +4963,63 @@ private void ReadIniFile()
NOAAconf.HeatThreshold = ini.GetValue("NOAA", "HeatingThreshold", -1000.0);
if (NOAAconf.HeatThreshold < -99 || NOAAconf.HeatThreshold > 150)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.HeatThreshold, resetting it");
NOAAconf.HeatThreshold = Units.Temp == 0 ? 18.3 : 65;
rewriteRequired = true;
}
NOAAconf.CoolThreshold = ini.GetValue("NOAA", "CoolingThreshold", -1000.0);
if (NOAAconf.CoolThreshold < -99 || NOAAconf.CoolThreshold > 150)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.CoolThreshold, resetting it");
NOAAconf.CoolThreshold = Units.Temp == 0 ? 18.3 : 65;
rewriteRequired = true;
}
NOAAconf.MaxTempComp1 = ini.GetValue("NOAA", "MaxTempComp1", -1000.0);
if (NOAAconf.MaxTempComp1 < -99 || NOAAconf.MaxTempComp1 > 150)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.MaxTempComp1, resetting it");
NOAAconf.MaxTempComp1 = Units.Temp == 0 ? 27 : 80;
rewriteRequired = true;
}
NOAAconf.MaxTempComp2 = ini.GetValue("NOAA", "MaxTempComp2", -1000.0);
if (NOAAconf.MaxTempComp2 < -99 || NOAAconf.MaxTempComp2 > 99)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.MaxTempComp2, resetting it");
NOAAconf.MaxTempComp2 = Units.Temp == 0 ? 0 : 32;
rewriteRequired = true;
}
NOAAconf.MinTempComp1 = ini.GetValue("NOAA", "MinTempComp1", -1000.0);
if (NOAAconf.MinTempComp1 < -99 || NOAAconf.MinTempComp1 > 99)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.MinTempComp1, resetting it");
NOAAconf.MinTempComp1 = Units.Temp == 0 ? 0 : 32;
rewriteRequired = true;
}
NOAAconf.MinTempComp2 = ini.GetValue("NOAA", "MinTempComp2", -1000.0);
if (NOAAconf.MinTempComp2 < -99 || NOAAconf.MinTempComp2 > 99)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.MinTempComp2, resetting it");
NOAAconf.MinTempComp2 = Units.Temp == 0 ? -18 : 0;
rewriteRequired = true;
}
NOAAconf.RainComp1 = ini.GetValue("NOAA", "RainComp1", -1000.0);
if (NOAAconf.RainComp1 < 0 || NOAAconf.RainComp1 > 99)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.RainComp1, resetting it");
NOAAconf.RainComp1 = Units.Rain == 0 ? 0.2 : 0.01;
rewriteRequired = true;
}
NOAAconf.RainComp2 = ini.GetValue("NOAA", "RainComp2", -1000.0);
if (NOAAconf.RainComp2 < 0 || NOAAconf.RainComp2 > 99)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.RainComp2, resetting it");
NOAAconf.RainComp2 = Units.Rain == 0 ? 2 : 0.1;
rewriteRequired = true;
}
NOAAconf.RainComp3 = ini.GetValue("NOAA", "RainComp3", -1000.0);
if (NOAAconf.RainComp3 < 0 || NOAAconf.RainComp3 > 99)
{
+ LogMessage("Cumulus.ini: Invalid NOAAconf.RainComp3, resetting it");
NOAAconf.RainComp3 = Units.Rain == 0 ? 20 : 1;
rewriteRequired = true;
}
@@ -5004,6 +5033,7 @@ private void ReadIniFile()
// Check for Cumulus 1 default format - and update
if (NOAAconf.MonthFile == "'NOAAMO'mmyy'.txt'" || NOAAconf.MonthFile == "\"NOAAMO\"mmyy\".txt\"")
{
+ LogMessage("Cumulus.ini: Updating old Cumulus 1 NOAA monthly file name");
NOAAconf.MonthFile = "'NOAAMO'MMyy'.txt'";
rewriteRequired = true;
}
@@ -8201,165 +8231,179 @@ public void BackupData(bool daily, DateTime timestamp)
if (!File.Exists(dirpath + DirectorySeparator + filename))
{
// create a zip archive file for the backup
- using (FileStream zipFile = new FileStream(dirpath + DirectorySeparator + filename, FileMode.Create))
+ try
{
- using ZipArchive archive = new ZipArchive(zipFile, ZipArchiveMode.Create);
- try
- {
- if (File.Exists(AlltimeIniFile))
- archive.CreateEntryFromFile(AlltimeIniFile, alltimebackup);
- if (File.Exists(MonthlyAlltimeIniFile))
- archive.CreateEntryFromFile(MonthlyAlltimeIniFile, monthlyAlltimebackup);
- if (File.Exists(DayFileName))
- archive.CreateEntryFromFile(DayFileName, daybackup);
- if (File.Exists(TodayIniFile))
- archive.CreateEntryFromFile(TodayIniFile, todaybackup);
- if (File.Exists(YesterdayFile))
- archive.CreateEntryFromFile(YesterdayFile, yesterdaybackup);
- if (File.Exists(LogFile))
- archive.CreateEntryFromFile(LogFile, logbackup);
- if (File.Exists(MonthIniFile))
- archive.CreateEntryFromFile(MonthIniFile, monthbackup);
- if (File.Exists(YearIniFile))
- archive.CreateEntryFromFile(YearIniFile, yearbackup);
- if (File.Exists("Cumulus.ini"))
- archive.CreateEntryFromFile("Cumulus.ini", configbackup);
- if (File.Exists("UniqueId.txt"))
- archive.CreateEntryFromFile("UniqueId.txt", uniquebackup);
- if (File.Exists("strings.ini"))
- archive.CreateEntryFromFile("strings.ini", stringsbackup);
- }
- catch (Exception ex)
- {
- LogExceptionMessage(ex, "Backup: Error backing up the data files");
- }
-
- if (daily)
- {
- // for daily backup the db is in use, so use an online backup
- try
- {
- var backUpDest = dirpath + "cumulusmx.db";
- var zipLocation = datafolder + "cumulusmx.db";
- LogDebugMessage("Making backup copy of the database");
- station.RecentDataDb.Backup(backUpDest);
- LogDebugMessage("Completed backup copy of the database");
-
- LogDebugMessage("Archiving backup copy of the database");
- archive.CreateEntryFromFile(backUpDest, zipLocation);
- LogDebugMessage("Completed backup copy of the database");
-
- LogDebugMessage("Deleting backup copy of the database");
- File.Delete(backUpDest);
-
- backUpDest = dirpath + "diary.db";
- zipLocation = datafolder + "diary.db";
- LogDebugMessage("Making backup copy of the diary");
- DiaryDB.Backup(backUpDest);
- LogDebugMessage("Completed backup copy of the diary");
-
- LogDebugMessage("Archiving backup copy of the diary");
- archive.CreateEntryFromFile(backUpDest, zipLocation);
- LogDebugMessage("Completed backup copy of the diary");
-
- LogDebugMessage("Deleting backup copy of the diary");
- File.Delete(backUpDest);
- }
- catch (Exception ex)
- {
- LogExceptionMessage(ex, "Error making db backup");
- }
- }
- else
+ using (FileStream zipFile = new FileStream(dirpath + DirectorySeparator + filename, FileMode.Create))
{
- // start-up backup - the db is not yet in use, do a file copy including any recovery files
+ using ZipArchive archive = new ZipArchive(zipFile, ZipArchiveMode.Create);
try
{
- LogDebugMessage("Archiving the database");
- if (File.Exists(dbfile))
- archive.CreateEntryFromFile(dbfile, dbBackup);
-
- if (File.Exists(dbfile + "-journal"))
- archive.CreateEntryFromFile(dbfile + "-journal", dbBackup + "-journal");
-
- if (File.Exists(diaryfile))
- archive.CreateEntryFromFile(diaryfile, diarybackup);
-
- if (File.Exists(diaryfile + "-journal"))
- archive.CreateEntryFromFile(diaryfile + "-journal", diarybackup + "-journal");
-
- LogDebugMessage("Completed archive of the database");
+ if (File.Exists(AlltimeIniFile))
+ archive.CreateEntryFromFile(AlltimeIniFile, alltimebackup);
+ if (File.Exists(MonthlyAlltimeIniFile))
+ archive.CreateEntryFromFile(MonthlyAlltimeIniFile, monthlyAlltimebackup);
+ if (File.Exists(DayFileName))
+ archive.CreateEntryFromFile(DayFileName, daybackup);
+ if (File.Exists(TodayIniFile))
+ archive.CreateEntryFromFile(TodayIniFile, todaybackup);
+ if (File.Exists(YesterdayFile))
+ archive.CreateEntryFromFile(YesterdayFile, yesterdaybackup);
+ if (File.Exists(LogFile))
+ archive.CreateEntryFromFile(LogFile, logbackup);
+ if (File.Exists(MonthIniFile))
+ archive.CreateEntryFromFile(MonthIniFile, monthbackup);
+ if (File.Exists(YearIniFile))
+ archive.CreateEntryFromFile(YearIniFile, yearbackup);
+ if (File.Exists("Cumulus.ini"))
+ archive.CreateEntryFromFile("Cumulus.ini", configbackup);
+ if (File.Exists("UniqueId.txt"))
+ archive.CreateEntryFromFile("UniqueId.txt", uniquebackup);
+ if (File.Exists("strings.ini"))
+ archive.CreateEntryFromFile("strings.ini", stringsbackup);
}
catch (Exception ex)
{
- LogExceptionMessage(ex, "Backup: Error backing up the database files");
+ LogExceptionMessage(ex, "Backup: Error backing up the data files");
}
- }
-
- try
- {
- if (File.Exists(extraFile))
- archive.CreateEntryFromFile(extraFile, extraBackup);
- if (File.Exists(AirLinkFile))
- archive.CreateEntryFromFile(AirLinkFile, AirLinkBackup);
- // custom logs
- for (var i = 0; i < 10; i++)
+ if (daily)
{
- if (CustomIntvlLogSettings[i].Enabled)
+ // for daily backup the db is in use, so use an online backup
+ try
{
- var custfilename = GetCustomIntvlLogFileName(i, timestamp);
- if (File.Exists(custfilename))
- archive.CreateEntryFromFile(custfilename, datafolder + Path.GetFileName(custfilename));
+ var backUpDest = dirpath + "cumulusmx.db";
+ var zipLocation = datafolder + "cumulusmx.db";
+ LogDebugMessage("Making backup copy of the database");
+ station.RecentDataDb.Backup(backUpDest);
+ LogDebugMessage("Completed backup copy of the database");
+
+ LogDebugMessage("Archiving backup copy of the database");
+ archive.CreateEntryFromFile(backUpDest, zipLocation);
+ LogDebugMessage("Completed backup copy of the database");
+
+ LogDebugMessage("Deleting backup copy of the database");
+ File.Delete(backUpDest);
+
+ backUpDest = dirpath + "diary.db";
+ zipLocation = datafolder + "diary.db";
+ LogDebugMessage("Making backup copy of the diary");
+ DiaryDB.Backup(backUpDest);
+ LogDebugMessage("Completed backup copy of the diary");
+
+ LogDebugMessage("Archiving backup copy of the diary");
+ archive.CreateEntryFromFile(backUpDest, zipLocation);
+ LogDebugMessage("Completed backup copy of the diary");
+
+ LogDebugMessage("Deleting backup copy of the diary");
+ File.Delete(backUpDest);
}
-
- if (CustomDailyLogSettings[i].Enabled)
+ catch (Exception ex)
{
- var custfilename = GetCustomDailyLogFileName(i);
- if (File.Exists(custfilename))
- archive.CreateEntryFromFile(custfilename, datafolder + Path.GetFileName(custfilename));
+ LogExceptionMessage(ex, "Error making db backup");
}
}
-
- // Do not do this extra backup between 00:00 & Roll-over hour on the first of the month
- // as the month has not yet rolled over - only applies for start-up backups
- if (timestamp.Day == 1 && timestamp.Hour >= RolloverHour)
+ else
{
- var newTime = timestamp.AddDays(-1);
- // on the first of month, we also need to backup last months files as well
- var LogFile2 = GetLogFileName(newTime);
- var logbackup2 = datafolder + Path.GetFileName(LogFile2);
+ // start-up backup - the db is not yet in use, do a file copy including any recovery files
+ try
+ {
+ LogDebugMessage("Archiving the database");
+ if (File.Exists(dbfile))
+ archive.CreateEntryFromFile(dbfile, dbBackup);
+
+ if (File.Exists(dbfile + "-journal"))
+ archive.CreateEntryFromFile(dbfile + "-journal", dbBackup + "-journal");
+
+ if (File.Exists(diaryfile))
+ archive.CreateEntryFromFile(diaryfile, diarybackup);
- var extraFile2 = GetExtraLogFileName(newTime);
- var extraBackup2 = datafolder + Path.GetFileName(extraFile2);
+ if (File.Exists(diaryfile + "-journal"))
+ archive.CreateEntryFromFile(diaryfile + "-journal", diarybackup + "-journal");
- var AirLinkFile2 = GetAirLinkLogFileName(timestamp.AddDays(-1));
- var AirLinkBackup2 = datafolder + Path.GetFileName(AirLinkFile2);
+ LogDebugMessage("Completed archive of the database");
+ }
+ catch (Exception ex)
+ {
+ LogExceptionMessage(ex, "Backup: Error backing up the database files");
+ }
+ }
- if (File.Exists(LogFile2))
- archive.CreateEntryFromFile(LogFile2, logbackup2);
- if (File.Exists(extraFile2))
- archive.CreateEntryFromFile(extraFile2, extraBackup2);
- if (File.Exists(AirLinkFile2))
- archive.CreateEntryFromFile(AirLinkFile2, AirLinkBackup2);
+ try
+ {
+ if (File.Exists(extraFile))
+ archive.CreateEntryFromFile(extraFile, extraBackup);
+ if (File.Exists(AirLinkFile))
+ archive.CreateEntryFromFile(AirLinkFile, AirLinkBackup);
+ // custom logs
for (var i = 0; i < 10; i++)
{
if (CustomIntvlLogSettings[i].Enabled)
{
- var custfilename = GetCustomIntvlLogFileName(i, newTime);
+ var custfilename = GetCustomIntvlLogFileName(i, timestamp);
+ if (File.Exists(custfilename))
+ archive.CreateEntryFromFile(custfilename, datafolder + Path.GetFileName(custfilename));
+ }
+
+ if (CustomDailyLogSettings[i].Enabled)
+ {
+ var custfilename = GetCustomDailyLogFileName(i);
if (File.Exists(custfilename))
archive.CreateEntryFromFile(custfilename, datafolder + Path.GetFileName(custfilename));
}
}
+
+ // Do not do this extra backup between 00:00 & Roll-over hour on the first of the month
+ // as the month has not yet rolled over - only applies for start-up backups
+ if (timestamp.Day == 1 && timestamp.Hour >= RolloverHour)
+ {
+ var newTime = timestamp.AddDays(-1);
+ // on the first of month, we also need to backup last months files as well
+ var LogFile2 = GetLogFileName(newTime);
+ var logbackup2 = datafolder + Path.GetFileName(LogFile2);
+
+ var extraFile2 = GetExtraLogFileName(newTime);
+ var extraBackup2 = datafolder + Path.GetFileName(extraFile2);
+
+ var AirLinkFile2 = GetAirLinkLogFileName(timestamp.AddDays(-1));
+ var AirLinkBackup2 = datafolder + Path.GetFileName(AirLinkFile2);
+
+ if (File.Exists(LogFile2))
+ archive.CreateEntryFromFile(LogFile2, logbackup2);
+ if (File.Exists(extraFile2))
+ archive.CreateEntryFromFile(extraFile2, extraBackup2);
+ if (File.Exists(AirLinkFile2))
+ archive.CreateEntryFromFile(AirLinkFile2, AirLinkBackup2);
+
+ for (var i = 0; i < 10; i++)
+ {
+ if (CustomIntvlLogSettings[i].Enabled)
+ {
+ var custfilename = GetCustomIntvlLogFileName(i, newTime);
+ if (File.Exists(custfilename))
+ archive.CreateEntryFromFile(custfilename, datafolder + Path.GetFileName(custfilename));
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogExceptionMessage(ex, "Backup: Error backing up extra log files");
}
}
- catch (Exception ex)
- {
- LogExceptionMessage(ex, "Backup: Error backing up extra log files");
- }
+
+ LogMessage("Created backup file " + filename);
+ }
+ catch (UnauthorizedAccessException)
+ {
+ LogErrorMessage("BackupData: Error, no permission to create/write file: " + dirpath + DirectorySeparator + filename);
+ LogConsoleMessage("Error, no permission to create/write file: " + dirpath + DirectorySeparator + filename, ConsoleColor.Yellow);
+ }
+ catch (Exception ex)
+ {
+ LogErrorMessage($"BackupData: Error while attempting to create/write file: {dirpath + DirectorySeparator + filename}, error message: {ex.Message}");
+ LogConsoleMessage($"Error while attempting to create/write file: {dirpath + DirectorySeparator + filename}, error message: {ex.Message}", ConsoleColor.Yellow);
}
- LogMessage("Created backup file " + filename);
}
else
{
@@ -11096,17 +11140,17 @@ private async Task UploadString(HttpClient httpclient, bool incremental, s
if (FtpOptions.PhpCompression == "gzip")
{
using var zipped = new System.IO.Compression.GZipStream(ms, System.IO.Compression.CompressionMode.Compress, true);
- await zipped.WriteAsync(byteData, 0, byteData.Length);
+ await zipped.WriteAsync(byteData, 0, byteData.Length, cancellationToken);
}
else if (FtpOptions.PhpCompression == "deflate")
{
using var zipped = new System.IO.Compression.DeflateStream(ms, System.IO.Compression.CompressionMode.Compress, true);
- await zipped.WriteAsync(byteData, 0, byteData.Length);
+ await zipped.WriteAsync(byteData, 0, byteData.Length, cancellationToken);
}
ms.Position = 0;
byte[] compressed = new byte[ms.Length];
- await ms.ReadAsync(compressed, 0, compressed.Length);
+ await ms.ReadAsync(compressed, 0, compressed.Length, cancellationToken);
outStream = new MemoryStream(compressed);
streamContent = new StreamContent(outStream);
@@ -11572,7 +11616,9 @@ public void LogFtpMessage(string message)
if (FtpOptions.Logging)
{
+#pragma warning disable CA2254 // Template should be a static expression
FtpLoggerMX.LogInformation(message);
+#pragma warning restore CA2254 // Template should be a static expression
}
}
@@ -11582,7 +11628,9 @@ public void LogFtpDebugMessage(string message)
{
if (!string.IsNullOrEmpty(message))
LogDebugMessage(message);
+#pragma warning disable CA2254 // Template should be a static expression
FtpLoggerMX.LogInformation(message);
+#pragma warning restore CA2254 // Template should be a static expression
}
}
diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj
index bfeb029d..e9943ece 100644
--- a/CumulusMX/CumulusMX.csproj
+++ b/CumulusMX/CumulusMX.csproj
@@ -36,7 +36,7 @@
- 4.001.1.4025
+ 4.1.2.4026
Copyright © 2015-$([System.DateTime]::Now.ToString('yyyy')) Cumulus MX
diff --git a/CumulusMX/DataStruct.cs b/CumulusMX/DataStruct.cs
index db0d7ef1..93acdf02 100644
--- a/CumulusMX/DataStruct.cs
+++ b/CumulusMX/DataStruct.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
@@ -9,7 +10,7 @@ public class DataStruct(Cumulus cumulus, double outdoorTemp, int outdoorHum, dou
int indoorHum, double pressure, double windLatest, double windAverage, double recentmaxgust, double windRunToday, int bearing, int avgbearing,
double rainToday, double rainYesterday, double rainMonth, double rainYear, double rainRate, double rainLastHour, double heatIndex, double humidex,
double appTemp, double tempTrend, double pressTrend, double highGustToday, string highGustTodayTime, double highWindToday, int highGustBearingToday,
- string windUnit, int bearingRangeFrom10, int bearingRangeTo10, string windRoseData, double highTempToday, double lowTempToday, string highTempTodayToday,
+ string windUnit, string windRunUnit, int bearingRangeFrom10, int bearingRangeTo10, string windRoseData, double highTempToday, double lowTempToday, string highTempTodayToday,
string lowTempTodayTime, double highPressToday, double lowPressToday, string highPressTodayTime, string lowPressTodayTime, double highRainRateToday,
string highRainRateTodayTime, int highHumToday, int lowHumToday, string highHumTodayTime, string lowHumTodayTime, string pressUnit, string tempUnit,
string rainUnit, double highDewpointToday, double lowDewpointToday, string highDewpointTodayTime, string lowDewpointTodayTime, double lowWindChillToday,
@@ -17,7 +18,7 @@ public class DataStruct(Cumulus cumulus, double outdoorTemp, int outdoorHum, dou
string highUVindexTodayTime, string forecast, string sunrise, string sunset, string moonrise, string moonset, double highHeatIndexToday,
string highHeatIndexTodayTime, double highAppTempToday, double lowAppTempToday, string highAppTempTodayTime, string lowAppTempTodayTime,
int currentSolarMax, double alltimeHighPressure, double alltimeLowPressure, double sunshineHours, string domWindDir, string lastRainTipISO,
- double highHourlyRainToday, string highHourlyRainTodayTime, string highBeaufortToday, string beaufort, string beaufortDesc, string lastDataRead,
+ double highHourlyRainToday, string highHourlyRainTodayTime, string highBeaufortToday, string beaufort, string beaufortDesc, DateTime lastDataRead,
bool dataStopped, double stormRain, string stormRainStart, int cloudbase, string cloudbaseUnit, double last24hourRain,
double feelsLike, double highFeelsLikeToday, string highFeelsLikeTodayTime, double lowFeelsLikeToday, string lowFeelsLikeTodayTime,
double highHumidexToday, string highHumidexTodayTime, List alarms)
@@ -162,6 +163,9 @@ public string LowWindChillTodayRounded
[DataMember]
public string WindUnit { get; } = windUnit;
+ [DataMember]
+ public string WindRunUnit { get; } = windRunUnit;
+
[DataMember]
public string RainUnit { get; } = rainUnit;
@@ -600,7 +604,14 @@ public string Build
public string BeaufortDesc { get; } = beaufortDesc;
[DataMember]
- public string LastDataRead { get; } = lastDataRead;
+ public string LastDataRead { get; } = lastDataRead.ToLocalTime().ToString(cumulus.ProgramOptions.TimeFormatLong);
+
+ [DataMember]
+ public string LastDataReadDate
+ {
+ get => lastDataRead.ToLocalTime().ToString("d");
+ }
+
[DataMember]
public bool DataStopped { get; } = dataStopped;
diff --git a/CumulusMX/DavisCloudStation.cs b/CumulusMX/DavisCloudStation.cs
index d377c86d..49ed35b3 100644
--- a/CumulusMX/DavisCloudStation.cs
+++ b/CumulusMX/DavisCloudStation.cs
@@ -696,6 +696,13 @@ private void GetHistoricData(BackgroundWorker worker)
DoHumidex(timestamp);
DoCloudBaseHeatIndex(timestamp);
+ if (cumulus.StationOptions.CalculateSLP && StationPressure > 0)
+ {
+ var abs = cumulus.Calib.Press.Calibrate(StationPressure);
+ var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToHpa(abs), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
+ DoPressure(ConvertUnits.PressMBToUser(slp), timestamp);
+ }
+
// Log all the data
_ = cumulus.DoLogFile(timestamp, false);
cumulus.MySqlRealtimeFile(999, false, timestamp);
@@ -1006,21 +1013,7 @@ private void DecodeCurrent(List sensors)
lsidLastUpdate[sensor.lsid] = rec.ts;
var ts = Utils.FromUnixTime(rec.ts);
- if (cumulus.StationOptions.CalculateSLP)
- {
- if (rec.bar_absolute.HasValue)
- {
- var abs = ConvertUnits.PressINHGToHpa(rec.bar_absolute.Value);
- abs = cumulus.Calib.Press.Calibrate(abs);
- var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), abs, ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
- DoPressure(ConvertUnits.PressMBToUser(slp), ts);
- }
- else
- {
- cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Baro data (absolute)");
- }
- }
- else
+ if (!cumulus.StationOptions.CalculateSLP)
{
if (rec.bar_sea_level.HasValue)
{
@@ -1258,7 +1251,7 @@ private void DecodeCurrent(List sensors)
}
else
{
- cumulus.LogErrorMessage("DecodeCurrent: Warning, no valid Humidity data");
+ cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Humidity data");
}
}
catch (Exception ex)
@@ -1271,7 +1264,7 @@ private void DecodeCurrent(List sensors)
{
if (data.temp_out.HasValue && data.temp_out < -98)
{
- cumulus.LogErrorMessage("DecodeCurrent: Warning, no valid Primary temperature value found [-99]");
+ cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Primary temperature value found [-99]");
}
else
{
@@ -1284,7 +1277,7 @@ private void DecodeCurrent(List sensors)
}
else
{
- cumulus.LogErrorMessage("DecodeCurrent: Warning, no valid Temperature data");
+ cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Temperature data");
}
}
}
@@ -1303,7 +1296,7 @@ private void DecodeCurrent(List sensors)
}
else
{
- cumulus.LogErrorMessage("DecodeCurrent: Warning, no valid Dew Point data");
+ cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Dew Point data");
}
}
catch (Exception ex)
@@ -1385,7 +1378,7 @@ private void DecodeCurrent(List sensors)
}
else
{
- cumulus.LogDebugMessage("DecodeCurrent: Warning, no valid Wind data");
+ cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Wind data");
}
}
catch (Exception ex)
@@ -1478,10 +1471,6 @@ private void DecodeCurrent(List sensors)
DoUV(data.uv.Value, lastRecordTime);
}
- else
- {
- cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid UV data");
- }
}
catch (Exception ex)
{
@@ -1502,15 +1491,11 @@ private void DecodeCurrent(List sensors)
{
cumulus.LogDebugMessage("DecodeCurrent: using solar data");
DoSolarRad(data.solar_rad.Value, lastRecordTime);
- }
- else
- {
- cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Solar data");
- }
- if (data.et_year.HasValue && !cumulus.StationOptions.CalculatedET && (data.et_year.Value >= 0) && (data.et_year.Value < 32000))
- {
- DoET(ConvertUnits.RainINToUser(data.et_year.Value), lastRecordTime);
+ if (data.et_year.HasValue && !cumulus.StationOptions.CalculatedET && (data.et_year.Value >= 0) && (data.et_year.Value < 32000))
+ {
+ DoET(ConvertUnits.RainINToUser(data.et_year.Value), lastRecordTime);
+ }
}
}
catch (Exception ex)
@@ -2080,6 +2065,20 @@ private void DecodeCurrent(List sensors)
DoHumidex(dateTime);
DoCloudBaseHeatIndex(dateTime);
+ if (cumulus.StationOptions.CalculateSLP)
+ {
+ if (StationPressure > 0)
+ {
+ var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToHpa(StationPressure), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
+ DoPressure(ConvertUnits.PressMBToUser(slp), dateTime);
+ }
+ else
+ {
+ cumulus.LogWarningMessage("DecodeCurrent: Warning, no valid Baro data (absolute)");
+ }
+ }
+
+
DoForecast(string.Empty, false);
UpdateStatusPanel(DateTime.Now);
@@ -3282,14 +3281,6 @@ private void DecodeHistoric(int dataType, int sensorType, string json, bool curr
cumulus.LogWarningMessage("DecodeHistoric: Warning, no valid Baro data (sea level)");
}
}
- else if (data13baro.bar_absolute != null)
- {
- ts = Utils.FromUnixTime(data13baro.ts);
- var abs = ConvertUnits.PressINHGToHpa((double) data13baro.bar_absolute);
- abs = cumulus.Calib.Press.Calibrate(abs);
- var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), abs, ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
- DoPressure(ConvertUnits.PressMBToUser(slp), ts);
- }
else
{
cumulus.LogWarningMessage("DecodeHistoric: Warning, no valid Baro data (absolute)");
diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs
index 75ad06a1..85d74320 100644
--- a/CumulusMX/DavisStation.cs
+++ b/CumulusMX/DavisStation.cs
@@ -2548,14 +2548,81 @@ private void GetArchiveData()
rolloverdone = false;
}
+ int interval;
+ if (starting && timestamp > nextLoggerTime)
+ {
+ interval = loggerInterval;
+ starting = false;
+ }
+ else
+ {
+ interval = (int) (timestamp - lastDataReadTime).TotalMinutes;
+ }
+
// ..and then process it
// 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
+ // Windrun (done)
+ // Dominant wind bearing (done)
// ET - if MX calculated
- // Degree days
- // Rainfall
+ // Degree days (done)
+ // Rainfall (done)
+
+ if (h == rollHour && !rolloverdone)
+ {
+ double lastAvg = ConvertUnits.WindMPHToUser(archiveData.AvgWindSpeed);
+ if (archiveData.HiWindSpeed < 250 && archiveData.AvgWindSpeed < 250)
+ {
+ int bearing = archiveData.WindDirection;
+ bearing = bearing == 255 ? 0 : (int) (bearing * 22.5);
+
+ // update dominant wind bearing
+ CalculateDominantWindBearing(bearing, WindAverage, interval);
+ }
+
+ // add in 'archivePeriod' minutes worth of wind speed to windrun
+ WindRunToday += ((lastAvg * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0);
+
+ var preDayTS = timestamp.AddDays(-1).Date.AddHours(23).AddMinutes(59);
+
+ CheckForWindrunHighLow(preDayTS);
+
+
+ if (!cumulus.StationOptions.CalculatedET && archiveData.ET >= 0 && archiveData.ET < 32000)
+ {
+ DoET(ConvertUnits.RainINToUser(archiveData.ET) + AnnualETTotal, preDayTS);
+ }
+
+
+ // Now process the "average" interval temperature - use this as our
+ if ((archiveData.OutsideTemperature > -200) && (archiveData.OutsideTemperature < 300))
+ {
+ var temp = ConvertUnits.TempFToUser(archiveData.OutsideTemperature);
+ // add in 'archivePeriod' minutes worth of temperature to the temp samples
+ tempsamplestoday += interval;
+ TempTotalToday += (temp * interval);
+
+ // update chill hours
+ if (temp < cumulus.ChillHourThreshold)
+ {
+ // add 1 minute to chill hours
+ ChillHours += (interval / 60.0);
+ }
+
+ // update heating/cooling degree days
+ UpdateDegreeDays(interval);
+ }
+
+ var lastRain = ConvertRainClicksToUser(archiveData.Rainfall) + RainCounter;
+ double lastRainrate = ConvertRainClicksToUser(archiveData.HiRainRate);
+
+ if (lastRainrate < 0)
+ {
+ lastRainrate = 0;
+ }
+
+ DoRain(lastRain, lastRainrate, preDayTS);
+ }
// In roll-over hour and roll-over not yet done
@@ -2593,17 +2660,6 @@ private void GetArchiveData()
midnightraindone = true;
}
- int interval;
- if (starting && timestamp > nextLoggerTime)
- {
- interval = loggerInterval;
- starting = false;
- }
- else
- {
- interval = (int) (timestamp - lastDataReadTime).TotalMinutes;
- }
-
if ((archiveData.InsideTemperature > -200) && (archiveData.InsideTemperature < 300))
{
DoIndoorTemp(ConvertUnits.TempFToUser(archiveData.InsideTemperature));
@@ -2635,19 +2691,24 @@ private void GetArchiveData()
if ((archiveData.OutsideTemperature > -200) && (archiveData.OutsideTemperature < 300))
{
DoOutdoorTemp(ConvertUnits.TempFToUser(archiveData.OutsideTemperature), timestamp);
- // add in 'archivePeriod' minutes worth of temperature to the temp samples
- tempsamplestoday += interval;
- TempTotalToday += (OutdoorTemperature * interval);
- // update chill hours
- if (OutdoorTemperature < cumulus.ChillHourThreshold)
+ // we don't want to do the totals for the first instant of the day
+ if (h != rollHour || timestamp.Minute != 0)
{
- // add 1 minute to chill hours
- ChillHours += (interval / 60.0);
- }
+ // add in 'archivePeriod' minutes worth of temperature to the temp samples
+ tempsamplestoday += interval;
+ TempTotalToday += (OutdoorTemperature * interval);
+
+ // update chill hours
+ if (OutdoorTemperature < cumulus.ChillHourThreshold)
+ {
+ // add 1 minute to chill hours
+ ChillHours += (interval / 60.0);
+ }
- // update heating/cooling degree days
- UpdateDegreeDays(interval);
+ // update heating/cooling degree days
+ UpdateDegreeDays(interval);
+ }
}
double wind = ConvertUnits.WindMPHToUser(archiveData.HiWindSpeed);
@@ -2671,21 +2732,13 @@ private void GetArchiveData()
DoCloudBaseHeatIndex(timestamp);
// add in 'archivePeriod' minutes worth of wind speed to windrun
- WindRunToday += ((WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0);
-
- DateTime windruncheckTS;
- if ((h == rollHour) && (timestamp.Minute == 0))
- {
- // 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);
- }
- else
+ // we don't want to do the this for the first instant of the day
+ if (h != rollHour || timestamp.Minute != 0)
{
- windruncheckTS = timestamp;
+ WindRunToday += ((WindAverage * WindRunHourMult[cumulus.Units.Wind] * interval) / 60.0);
+ CheckForWindrunHighLow(timestamp);
}
- CheckForWindrunHighLow(windruncheckTS);
double rain = ConvertRainClicksToUser(archiveData.Rainfall) + RainCounter;
double rainrate = ConvertRainClicksToUser(archiveData.HiRainRate);
@@ -2716,7 +2769,8 @@ private void GetArchiveData()
SunshineHours += (interval / 60.0);
}
- if (!cumulus.StationOptions.CalculatedET && archiveData.ET >= 0 && archiveData.ET < 32000)
+ // we don't want to do the this for the first instant of the day
+ if ((h != rollHour || timestamp.Minute != 0) && !cumulus.StationOptions.CalculatedET && archiveData.ET >= 0 && archiveData.ET < 32000)
{
DoET(ConvertUnits.RainINToUser(archiveData.ET) + AnnualETTotal, timestamp);
}
@@ -2831,7 +2885,8 @@ private void GetArchiveData()
OutdoorHumidity, Pressure, RainToday, SolarRad, UV, RainCounter, FeelsLike, Humidex, ApparentTemperature, IndoorTemperature, IndoorHumidity, CurrentSolarMax, RainRate, -1, -1);
DoTrendValues(timestamp);
- if (cumulus.StationOptions.CalculatedET && timestamp.Minute == 0)
+ // we don't want to do the this for the first instant of the day
+ if (cumulus.StationOptions.CalculatedET && h != rollHour && timestamp.Minute != 0)
{
// Start of a new hour, and we want to calculate ET in Cumulus
CalculateEvapotranspiration(timestamp);
diff --git a/CumulusMX/DavisWllStation.cs b/CumulusMX/DavisWllStation.cs
index f9354b51..0589b150 100644
--- a/CumulusMX/DavisWllStation.cs
+++ b/CumulusMX/DavisWllStation.cs
@@ -2047,9 +2047,9 @@ private void DecodeHistoric(int dataType, int sensorType, string json)
try
{
- if (data11.temp_last == -99 || data11.temp_last == null)
+ if (data11.temp_last < -98 || data11.temp_last == null)
{
- cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Extra temperature value on TxId {data11.tx_id}");
+ cumulus.LogDebugMessage($"WL.com historic: Warning, no valid Extra temperature value [-99] on TxId {data11.tx_id}");
}
else
{
diff --git a/CumulusMX/EcowittApi.cs b/CumulusMX/EcowittApi.cs
index 0d76453a..073cfb91 100644
--- a/CumulusMX/EcowittApi.cs
+++ b/CumulusMX/EcowittApi.cs
@@ -1351,6 +1351,15 @@ private void ProcessHistoryData(EcowittHistoricData data, CancellationToken toke
// finally apply this data
ApplyHistoricData(rec);
+ // Do the CMX calculate SLP now as it depends on temperature
+ if (cumulus.StationOptions.CalculateSLP)
+ {
+ var abs = cumulus.Calib.Press.Calibrate(station.StationPressure);
+ var slp = MeteoLib.GetSeaLevelPressure(station.AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(abs), ConvertUnits.UserTempToC(station.OutdoorTemperature), cumulus.Latitude);
+
+ station.DoPressure(ConvertUnits.PressMBToUser(slp), rec.Key);
+ }
+
// add in archive period worth of sunshine, if sunny
if (station.CurrentSolarMax > 0 &&
station.SolarRad > station.CurrentSolarMax * cumulus.SolarOptions.SunThreshold / 100 &&
@@ -1488,16 +1497,8 @@ private void ApplyHistoricData(KeyValuePair rec)
if (rec.Value.StationPressure.HasValue)
{
station.StationPressure = (double) rec.Value.StationPressure;
-
- if (cumulus.StationOptions.CalculateSLP)
- {
- station.StationPressure = cumulus.Calib.Press.Calibrate(station.StationPressure);
- var slp = MeteoLib.GetSeaLevelPressure(station.AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(station.StationPressure), ConvertUnits.UserTempToC(station.OutdoorTemperature), cumulus.Latitude);
-
- station.DoPressure(ConvertUnits.PressMBToUser(slp), rec.Key);
- }
-
station.AltimeterPressure = ConvertUnits.PressMBToUser(MeteoLib.StationToAltimeter(station.StationPressure, station.AltitudeM(cumulus.Altitude)));
+ // Leave CMX calculated SLP until the end as it uses Temperature
}
else
{
diff --git a/CumulusMX/EcowittCloudStation.cs b/CumulusMX/EcowittCloudStation.cs
index 5681fc19..10feee70 100644
--- a/CumulusMX/EcowittCloudStation.cs
+++ b/CumulusMX/EcowittCloudStation.cs
@@ -372,13 +372,9 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok
{
StationPressure = data.pressure.absolute.value;
- if (cumulus.StationOptions.CalculateSLP)
- {
- StationPressure = cumulus.Calib.Press.Calibrate(StationPressure);
- var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), StationPressure, ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
- DoPressure(slp, Utils.FromUnixTime(data.pressure.absolute.time));
- }
- else
+ // leave cmx calculated SLP until the end as it depends on temperature
+
+ if (!cumulus.StationOptions.CalculateSLP)
{
DoPressure(data.pressure.relative.value, Utils.FromUnixTime(data.pressure.relative.time));
}
@@ -592,6 +588,12 @@ private void ProcessCurrentData(EcowittApi.CurrentDataData data, CancellationTok
cumulus.LogErrorMessage($"ProcessCurrentData: Error in Camera data - {ex.Message}");
}
+ if (cumulus.StationOptions.CalculateSLP)
+ {
+ var abs = cumulus.Calib.Press.Calibrate(StationPressure);
+ var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToHpa(abs), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
+ DoPressure(ConvertUnits.PressMBToUser(slp), Utils.FromUnixTime(data.pressure.absolute.time));
+ }
thisStation.DoForecast("", false);
@@ -960,23 +962,23 @@ private static void ProcessAirQuality(EcowittApi.CurrentDataData data, WeatherSt
if (data.pm25_ch1 != null)
{
station.DoAirQuality(data.pm25_ch1.pm25.value, 1);
- //station.DoAirQualityAvg(data.pm25_ch1.AqiAvg24h.value, 1);
+ //station.DoAirQualityAvg(data.pm25_ch1.AqiAvg24h.value, 1)
}
if (data.pm25_ch2 != null)
{
station.DoAirQuality(data.pm25_ch2.pm25.value, 2);
- //station.DoAirQualityAvg(data.pm25_ch2.AqiAvg24h.value, 2);
+ //station.DoAirQualityAvg(data.pm25_ch2.AqiAvg24h.value, 2)
}
if (data.pm25_ch3 != null)
{
station.DoAirQuality(data.pm25_ch3.pm25.value, 3);
- //station.DoAirQualityAvg(data.pm25_ch3.AqiAvg24h.value, 3);
+ //station.DoAirQualityAvg(data.pm25_ch3.AqiAvg24h.value, 3)
}
if (data.pm25_ch4 != null)
{
station.DoAirQuality(data.pm25_ch4.pm25.value, 4);
- //station.DoAirQualityAvg(data.pm25_ch1.AqiAvg24h.value, 4);
+ //station.DoAirQualityAvg(data.pm25_ch1.AqiAvg24h.value, 4)
}
}
@@ -991,14 +993,14 @@ private static void ProcessCo2(EcowittApi.CurrentDataData data, WeatherStation s
if (data.pm25_aqi_combo != null)
{
station.CO2_pm2p5 = data.pm25_aqi_combo.pm25.value;
- //station.CO2_pm2p5_24h = data.pm25_aqi_combo.AqiAvg24h.value;
+ //station.CO2_pm2p5_24h = data.pm25_aqi_combo.AqiAvg24h.value
station.CO2_pm2p5_aqi = station.GetAqi(WeatherStation.AqMeasure.pm2p5, station.CO2_pm2p5);
}
if (data.pm10_aqi_combo != null)
{
station.CO2_pm10 = data.pm10_aqi_combo.pm10.value;
- //station.CO2_pm10_24h = data.pm10_aqi_combo.AqiAvg24h.value;
+ //station.CO2_pm10_24h = data.pm10_aqi_combo.AqiAvg24h.value
station.CO2_pm10_aqi = station.GetAqi(WeatherStation.AqMeasure.pm10, station.CO2_pm10);
}
diff --git a/CumulusMX/FOStation.cs b/CumulusMX/FOStation.cs
index bab73b17..1f398cf4 100644
--- a/CumulusMX/FOStation.cs
+++ b/CumulusMX/FOStation.cs
@@ -861,15 +861,15 @@ private void GetAndProcessData()
if (cumulus.FineOffsetOptions.SyncReads && !synchronising)
{
- var doSensorSync = DateTime.Now.Subtract(FOSensorClockTime).TotalDays > 1;
- var doStationSync = DateTime.Now.Subtract(FOStationClockTime).TotalDays > 1;
- doSolarSync = hasSolar && DateTime.Now.Subtract(FOSolarClockTime).TotalDays > 1;
+ var doSensorSync = DateTime.UtcNow.Subtract(FOSensorClockTime.ToUniversalTime()).TotalDays > 1;
+ var doStationSync = DateTime.UtcNow.Subtract(FOStationClockTime.ToUniversalTime()).TotalDays > 1;
+ doSolarSync = hasSolar && DateTime.UtcNow.Subtract(FOSolarClockTime.ToUniversalTime()).TotalDays > 1;
if (doSensorSync || doStationSync || doSolarSync)
{
doSolarSync = hasSolar;
- if (hasSolar && DateTime.Now.Subtract(FOSolarClockTime).TotalDays > 1)
+ if (hasSolar && DateTime.UtcNow.Subtract(FOSolarClockTime.ToUniversalTime()).TotalDays > 1)
{
if (DateTime.Now.CompareTo(cumulus.SunRiseTime.AddMinutes(30)) > 0)
{
@@ -1111,7 +1111,7 @@ private void GetAndProcessData()
StopSynchronising();
FinaliseSync();
}
- else if (DateTime.Now.Subtract(syncStart).TotalMinutes > 1)
+ else if (DateTime.UtcNow.Subtract(syncStart.ToUniversalTime()).TotalMinutes > 1)
{
StopSynchronising();
diff --git a/CumulusMX/GW1000Station.cs b/CumulusMX/GW1000Station.cs
index b7bb5468..4b51fd08 100644
--- a/CumulusMX/GW1000Station.cs
+++ b/CumulusMX/GW1000Station.cs
@@ -982,13 +982,7 @@ private void GetLiveData()
tempUint16 = GW1000Api.ConvertBigEndianUInt16(data, idx);
StationPressure = ConvertUnits.PressMBToUser(tempUint16 / 10.0);
AltimeterPressure = ConvertUnits.PressMBToUser(MeteoLib.StationToAltimeter(tempUint16 / 10.0, AltitudeM(cumulus.Altitude)));
-
- if (cumulus.StationOptions.CalculateSLP)
- {
- StationPressure = cumulus.Calib.Press.Calibrate(StationPressure);
- var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(StationPressure), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
- DoPressure(ConvertUnits.PressMBToUser(slp), dateTime);
- }
+ // Leave calculate SLP until the end as it depends on temperature
idx += 2;
break;
case 0x09: //Relative Barometric (hPa)
@@ -1354,7 +1348,7 @@ private void GetLiveData()
if (newLightningTime > LightningTime)
{
LightningTime = newLightningTime;
- if (newLightningDistance != 999)
+ if (newLightningDistance < 999)
LightningDistance = newLightningDistance;
}
@@ -1389,6 +1383,13 @@ private void GetLiveData()
DoFeelsLike(dateTime);
DoHumidex(dateTime);
DoCloudBaseHeatIndex(dateTime);
+
+ if (cumulus.StationOptions.CalculateSLP)
+ {
+ var abs = cumulus.Calib.Press.Calibrate(StationPressure);
+ var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(abs), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
+ DoPressure(ConvertUnits.PressMBToUser(slp), dateTime);
+ }
}
DoForecast("", false);
diff --git a/CumulusMX/HttpStationAmbient.cs b/CumulusMX/HttpStationAmbient.cs
index f10a5f23..fb4a50d1 100644
--- a/CumulusMX/HttpStationAmbient.cs
+++ b/CumulusMX/HttpStationAmbient.cs
@@ -194,53 +194,6 @@ public string ProcessData(IHttpContext context, bool main)
}
- // === Pressure ===
- try
- {
- // baromabsin
- // baromrelin
-
- var press = data["baromrelin"];
- var stnPress = data["baromabsin"];
-
- if (press == null)
- {
- cumulus.LogWarningMessage($"{procName}: Error, missing baro pressure");
- }
- else
- {
- if (!cumulus.StationOptions.CalculateSLP)
- {
- var pressVal = ConvertUnits.PressINHGToUser(Convert.ToDouble(press, CultureInfo.InvariantCulture));
- DoPressure(pressVal, recDate);
- }
- }
-
- if (stnPress == null)
- {
- cumulus.LogDebugMessage($"{procName}: Error, missing absolute baro pressure");
- }
- else
- {
- StationPressure = ConvertUnits.PressINHGToUser(Convert.ToDouble(stnPress, CultureInfo.InvariantCulture));
-
- if (cumulus.StationOptions.CalculateSLP)
- {
- StationPressure = cumulus.Calib.Press.Calibrate(StationPressure);
- var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(StationPressure), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
-
- DoPressure(ConvertUnits.PressMBToUser(slp), recDate);
- }
- }
- }
- catch (Exception ex)
- {
- cumulus.LogErrorMessage("ProcessData: Error in Pressure data - " + ex.Message);
- context.Response.StatusCode = 500;
- return "Failed: Error in baro pressure data - " + ex.Message;
- }
-
-
// === Indoor temp ===
try
{
@@ -290,6 +243,53 @@ public string ProcessData(IHttpContext context, bool main)
return "Failed: Error in outdoor temp data - " + ex.Message;
}
+ // === Pressure ===
+ try
+ {
+ // baromabsin
+ // baromrelin
+
+ var press = data["baromrelin"];
+ var stnPress = data["baromabsin"];
+
+ if (press == null)
+ {
+ cumulus.LogWarningMessage($"{procName}: Error, missing baro pressure");
+ }
+ else
+ {
+ if (!cumulus.StationOptions.CalculateSLP)
+ {
+ var pressVal = ConvertUnits.PressINHGToUser(Convert.ToDouble(press, CultureInfo.InvariantCulture));
+ DoPressure(pressVal, recDate);
+ }
+ }
+
+ if (stnPress == null)
+ {
+ cumulus.LogDebugMessage($"{procName}: Error, missing absolute baro pressure");
+ }
+ else
+ {
+ StationPressure = ConvertUnits.PressINHGToUser(Convert.ToDouble(stnPress, CultureInfo.InvariantCulture));
+
+ if (cumulus.StationOptions.CalculateSLP)
+ {
+ StationPressure = cumulus.Calib.Press.Calibrate(StationPressure);
+ var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(StationPressure), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
+
+ DoPressure(ConvertUnits.PressMBToUser(slp), recDate);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ cumulus.LogErrorMessage("ProcessData: Error in Pressure data - " + ex.Message);
+ context.Response.StatusCode = 500;
+ return "Failed: Error in baro pressure data - " + ex.Message;
+ }
+
+
// === Rain ===
try
diff --git a/CumulusMX/HttpStationEcowitt.cs b/CumulusMX/HttpStationEcowitt.cs
index 9d737ee3..9128148b 100644
--- a/CumulusMX/HttpStationEcowitt.cs
+++ b/CumulusMX/HttpStationEcowitt.cs
@@ -494,55 +494,6 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
}
- // === Pressure ===
- try
- {
- // baromabsin
- // baromrelin
-
- var press = data["baromrelin"];
- var stnPress = data["baromabsin"];
-
- if (press == null)
- {
- cumulus.LogWarningMessage($"{procName}: Error, missing baro pressure");
- }
- else
- {
- if (!cumulus.StationOptions.CalculateSLP)
- {
- var pressVal = ConvertUnits.PressINHGToUser(Convert.ToDouble(press, invNum));
- DoPressure(pressVal, recDate);
- }
- }
-
- if (stnPress == null)
- {
- cumulus.LogDebugMessage($"{procName}: Error, missing absolute baro pressure");
- }
- else
- {
- StationPressure = ConvertUnits.PressINHGToUser(Convert.ToDouble(stnPress, invNum));
-
- if (cumulus.StationOptions.CalculateSLP)
- {
- StationPressure = cumulus.Calib.Press.Calibrate(StationPressure);
-
- var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(StationPressure), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
-
- DoPressure(ConvertUnits.PressMBToUser(slp), recDate);
- }
-
- AltimeterPressure = ConvertUnits.PressMBToUser(MeteoLib.StationToAltimeter(ConvertUnits.UserPressToHpa(StationPressure), AltitudeM(cumulus.Altitude)));
- }
- }
- catch (Exception ex)
- {
- cumulus.LogErrorMessage($"{procName}: Error in Pressure data - {ex.Message}");
- return "Failed: Error in baro pressure data - " + ex.Message;
- }
-
-
// === Indoor temp ===
try
{
@@ -596,6 +547,55 @@ public string ApplyData(string dataString, bool main, DateTime? ts = null)
}
+ // === Pressure ===
+ try
+ {
+ // baromabsin
+ // baromrelin
+
+ var press = data["baromrelin"];
+ var stnPress = data["baromabsin"];
+
+ if (press == null)
+ {
+ cumulus.LogWarningMessage($"{procName}: Error, missing baro pressure");
+ }
+ else
+ {
+ if (!cumulus.StationOptions.CalculateSLP)
+ {
+ var pressVal = ConvertUnits.PressINHGToUser(Convert.ToDouble(press, invNum));
+ DoPressure(pressVal, recDate);
+ }
+ }
+
+ if (stnPress == null)
+ {
+ cumulus.LogDebugMessage($"{procName}: Error, missing absolute baro pressure");
+ }
+ else
+ {
+ StationPressure = ConvertUnits.PressINHGToUser(Convert.ToDouble(stnPress, invNum));
+
+ if (cumulus.StationOptions.CalculateSLP)
+ {
+ StationPressure = cumulus.Calib.Press.Calibrate(StationPressure);
+
+ var slp = MeteoLib.GetSeaLevelPressure(AltitudeM(cumulus.Altitude), ConvertUnits.UserPressToMB(StationPressure), ConvertUnits.UserTempToC(OutdoorTemperature), cumulus.Latitude);
+
+ DoPressure(ConvertUnits.PressMBToUser(slp), recDate);
+ }
+
+ AltimeterPressure = ConvertUnits.PressMBToUser(MeteoLib.StationToAltimeter(ConvertUnits.UserPressToHpa(StationPressure), AltitudeM(cumulus.Altitude)));
+ }
+ }
+ catch (Exception ex)
+ {
+ cumulus.LogErrorMessage($"{procName}: Error in Pressure data - {ex.Message}");
+ return "Failed: Error in baro pressure data - " + ex.Message;
+ }
+
+
// === Rain ===
try
{
diff --git a/CumulusMX/JsonStation.cs b/CumulusMX/JsonStation.cs
index 283a7e1d..a312eb55 100644
--- a/CumulusMX/JsonStation.cs
+++ b/CumulusMX/JsonStation.cs
@@ -1,14 +1,9 @@
using System;
-using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Text;
-using System.Threading.Tasks;
using EmbedIO;
-using FluentFTP.Helpers;
-
using MQTTnet;
using ServiceStack;
@@ -331,6 +326,10 @@ private string ApplyData(string dataString)
avg = ConvertUnits.WindKPHToUser(avg);
gust = ConvertUnits.WindKPHToUser(gust);
break;
+ case "knots":
+ avg = ConvertUnits.WindKnotsToUser(avg);
+ gust = ConvertUnits.WindKnotsToUser(gust);
+ break;
default:
cumulus.LogErrorMessage("ApplyData: Invalid windspeed units supplied: " + data.units.windspeed);
retStr.AppendLine("Invalid windspeed units");
diff --git a/CumulusMX/MeteoLib.cs b/CumulusMX/MeteoLib.cs
index 5eb3de08..4f643f42 100644
--- a/CumulusMX/MeteoLib.cs
+++ b/CumulusMX/MeteoLib.cs
@@ -356,8 +356,8 @@ public static double GetSeaLevelPressure(double altitudeM, double pressureHpa, d
}
///
- /// Calculates the sea leavel pressure
- /// https://www.wind101.net/sea-level-pressure-advanced/LAPLACE%20GENERAL%20EQUATION%20THE%20REDUCTION%20OF%20BAROMETRIC%20PRESSURE%20DrKFS_net.pdf
+ /// Calculates the sea leavel pressure.
+ /// See: https://www.wind101.net/sea-level-pressure-advanced/LAPLACE%20GENERAL%20EQUATION%20THE%20REDUCTION%20OF%20BAROMETRIC%20PRESSURE%20DrKFS_net.pdf
///
/// Station altitude in metres
/// Station pressure in inHg
diff --git a/CumulusMX/Program.cs b/CumulusMX/Program.cs
index 16dcc0a0..cc5fea54 100644
--- a/CumulusMX/Program.cs
+++ b/CumulusMX/Program.cs
@@ -129,9 +129,10 @@ private static async Task Main(string[] args)
var lang = string.Empty;
var servicename = string.Empty;
- for (int i = 0; i < args.Length; i++)
+ try
{
- try
+ int i = 0;
+ while (i < args.Length)
{
switch (args[i])
{
@@ -184,11 +185,13 @@ private static async Task Main(string[] args)
Usage();
break;
}
+
+ i++;
}
- catch
- {
- Usage();
- }
+ }
+ catch
+ {
+ Usage();
}
diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs
index 30394c3c..5ef11788 100644
--- a/CumulusMX/WeatherStation.cs
+++ b/CumulusMX/WeatherStation.cs
@@ -2057,9 +2057,6 @@ private void TenMinuteChanged()
cumulus.RotateLogFiles();
ClearAlarms();
-
- System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
- GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, false);
}
private void HourChanged(DateTime now)
@@ -2078,7 +2075,7 @@ private void HourChanged(DateTime now)
if (now.Hour == rollHour)
{
DayReset(now);
- cumulus.BackupData(true, now);
+ Task.Run(() => cumulus.BackupData(true, now));
}
if (now.Hour == 0)
@@ -2089,6 +2086,9 @@ private void HourChanged(DateTime now)
}
RemoveOldRecentData(now);
+
+ System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
+ //GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, false)
}
private void CheckForDataStopped()
@@ -2112,7 +2112,7 @@ private void CheckForDataStopped()
{
cumulus.LogErrorMessage("*** Data input appears to have stopped");
}
- }
+ } // Calculates evapotranspiration based on the data for the last hour and updates the running annual total.
else
{
DataStopped = false;
@@ -2146,6 +2146,8 @@ private void ReadBlakeLarsenData()
}
}
+
+ // Calculates evapotranspiration based on the data for the last hour and updates the running annual total.
public void CalculateEvapotranspiration(DateTime date)
{
cumulus.LogDebugMessage("Calculating ET from data");
@@ -5887,6 +5889,8 @@ public void DoRain(double total, double rate, DateTime timestamp)
// Calculate today"s rainfall
RainToday = (RainCounter - RainCounterDayStart) * cumulus.Calib.Rain.Mult;
+ // Allow for rounding errors
+ if (RainToday < 0) RainToday = 0;
// Calculate rain since midnight for Wunderground etc
double trendval = RainCounter - MidnightRainCount;
@@ -11867,12 +11871,12 @@ public string GetTodayYestSolar()
json.Append("[\"High Solar Radiation\",\"");
json.Append(HiLoToday.HighSolar.ToString("F0"));
- json.Append(" W/m2");
+ json.Append(" W/m2");
json.Append(sepStr);
json.Append(HiLoToday.HighSolarTime.ToString(cumulus.ProgramOptions.TimeFormat));
json.Append(sepStr);
json.Append(HiLoYest.HighSolar.ToString("F0"));
- json.Append(" W/m2");
+ json.Append(" W/m2");
json.Append(sepStr);
json.Append(HiLoYest.HighSolarTime.ToString(cumulus.ProgramOptions.TimeFormat));
json.Append("\"],");
@@ -13784,7 +13788,7 @@ internal string GetCurrentData()
var data = new DataStruct(cumulus, OutdoorTemperature, OutdoorHumidity, TempTotalToday / tempsamplestoday, IndoorTemperature, OutdoorDewpoint, WindChill, IndoorHumidity,
Pressure, WindLatest, WindAverage, RecentMaxGust, WindRunToday, Bearing, AvgBearing, RainToday, RainYesterday, RainMonth, RainYear, RainRate,
RainLastHour, HeatIndex, Humidex, ApparentTemperature, temptrendval, presstrendval, HiLoToday.HighGust, HiLoToday.HighGustTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.HighWind,
- HiLoToday.HighGustBearing, cumulus.Units.WindText, BearingRangeFrom10, BearingRangeTo10, windRoseData.ToString(), HiLoToday.HighTemp, HiLoToday.LowTemp,
+ HiLoToday.HighGustBearing, cumulus.Units.WindText, cumulus.Units.WindRunText, BearingRangeFrom10, BearingRangeTo10, windRoseData.ToString(), HiLoToday.HighTemp, HiLoToday.LowTemp,
HiLoToday.HighTempTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowTempTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.HighPress, HiLoToday.LowPress, HiLoToday.HighPressTime.ToString(cumulus.ProgramOptions.TimeFormat),
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,
@@ -13795,7 +13799,7 @@ internal string GetCurrentData()
HiLoToday.LowAppTemp, HiLoToday.HighAppTempTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowAppTempTime.ToString(cumulus.ProgramOptions.TimeFormat), CurrentSolarMax,
AllTime.HighPress.Val, AllTime.LowPress.Val, SunshineHours, CompassPoint(DominantWindBearing), LastRainTip,
HiLoToday.HighHourlyRain, HiLoToday.HighHourlyRainTime.ToString(cumulus.ProgramOptions.TimeFormat), "F" + Cumulus.Beaufort(HiLoToday.HighWind), "F" + Cumulus.Beaufort(WindAverage),
- cumulus.BeaufortDesc(WindAverage), LastDataReadTimestamp.ToLocalTime().ToString(cumulus.ProgramOptions.TimeFormatLong), DataStopped, StormRain, stormRainStart, CloudBase, cumulus.CloudBaseInFeet ? "ft" : "m", RainLast24Hour,
+ cumulus.BeaufortDesc(WindAverage), LastDataReadTimestamp, DataStopped, StormRain, stormRainStart, CloudBase, cumulus.CloudBaseInFeet ? "ft" : "m", RainLast24Hour,
FeelsLike, HiLoToday.HighFeelsLike, HiLoToday.HighFeelsLikeTime.ToString(cumulus.ProgramOptions.TimeFormat), HiLoToday.LowFeelsLike, HiLoToday.LowFeelsLikeTime.ToString(cumulus.ProgramOptions.TimeFormat),
HiLoToday.HighHumidex, HiLoToday.HighHumidexTime.ToString(cumulus.ProgramOptions.TimeFormat), alarms);
diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs
index 03643b24..2beab54c 100644
--- a/CumulusMX/webtags.cs
+++ b/CumulusMX/webtags.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -414,6 +415,12 @@ private string GetFormattedDateTime(TimeSpan ts, string defaultFormat, Dictionar
return GetFormattedDateTime(dt, defaultFormat, tagParams);
}
+ private static string GetFormattedTimeSpan(TimeSpan ts, string defaultFormat, Dictionary tagParams)
+ {
+ string dtformat = tagParams.Get("format") ?? defaultFormat;
+ return String.Format(dtformat, ts);
+ }
+
private string GetMonthlyAlltimeValueStr(AllTimeRec rec, Dictionary tagParams, int decimals)
{
if (rec.Ts <= cumulus.defaultRecordTS)
@@ -3592,7 +3599,7 @@ private string TagMonthTempAvg(Dictionary tagParams)
if (start.Date == DateTime.Now.AddHours(cumulus.GetHourInc()).Date)
{
- // first day of the currebt month, there are no dayfile entries
+ // first day of the current month, there are no dayfile entries
// so return the average temp so far today
return Tagavgtemp(tagParams);
}
@@ -3632,6 +3639,34 @@ private string TagYearTempAvg(Dictionary tagParams)
return CheckRcDp(CheckTempUnit(avg, tagParams), tagParams, cumulus.TempDPlaces);
}
+ private string TagAnnualRainfall(Dictionary tagParams)
+ {
+ var year = tagParams.Get("y");
+ DateTime start;
+ DateTime end;
+ double total;
+
+ if (year != null)
+ {
+ if (int.Parse(year) == DateTime.Now.Year)
+ {
+ total = station.RainYear;
+ }
+ else
+ {
+ start = new DateTime(int.Parse(year), 1, 1, 0, 0, 0, DateTimeKind.Local);
+ end = start.AddYears(1);
+ total = station.DayFile.Where(x => x.Date >= start && x.Date < end).Sum(x => x.TotalRain);
+ }
+ }
+ else
+ {
+ total = station.RainYear;
+ }
+
+ return CheckRcDp(CheckRainUnit(total, tagParams), tagParams, cumulus.RainDPlaces);
+ }
+
private string TagThwIndex(Dictionary tagParams)
{
return CheckRcDp(CheckTempUnit(station.THWIndex, tagParams), tagParams, 1);
@@ -5350,7 +5385,7 @@ private string TagSystemUpTime(Dictionary tagParams)
TimeSpan ts = TimeSpan.FromSeconds(upTime);
- return string.Format($"{ts.Days} days {ts.Hours} hours");
+ return GetFormattedTimeSpan(ts, "{0:%d} days {0:%h} hours", tagParams);
}
catch (Exception ex)
{
@@ -5363,7 +5398,7 @@ private string TagSystemUpTime(Dictionary tagParams)
private static string TagProgramUpTime(Dictionary tagParams)
{
TimeSpan ts = DateTime.Now.ToUniversalTime() - Program.StartTime.ToUniversalTime();
- return string.Format($"{ts.Days} days {ts.Hours} hours");
+ return GetFormattedTimeSpan(ts, "{0:%d} days {0:%h} hours", tagParams);
}
private static string TagProgramUpTimeMs(Dictionary tagParams)
@@ -6746,6 +6781,7 @@ public void InitialiseWebtags()
// Specifc Month/Year values
{ "MonthTempAvg", TagMonthTempAvg },
{ "YearTempAvg", TagYearTempAvg },
+ { "AnnualRainfall", TagAnnualRainfall },
// Options
{ "Option_useApparent", TagOption_useApparent },
{ "Option_showSolar", TagOption_showSolar },
diff --git a/Updates.txt b/Updates.txt
index b1e91b69..80648899 100644
--- a/Updates.txt
+++ b/Updates.txt
@@ -1,3 +1,32 @@
+4.1.2 - b4026
+—————————————
+New
+- Adds wind run to the dashboard "now" page
+- Adds support for the format parameter to the <#ProgramUpTime> and <#SystemUpTime> web tags
+ - The format syntax is different from date/time web tags as these two tags use a elapsed time.
+ - For Custom format specifiers see: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings
+ - For Standard format specifiers see: https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-timespan-format-strings
+ - The default output is generated using the format string "{0:%d} days {0:%h} hours"
+ - You can customise this like this example: <#SystemUpTime format="{0:%d}d {0:%h}h {0:%m}m"> --> "12d 9h 46m"
+- New web tag <#AnnualRainfall>
+ - Defaults to the current year if no year is specified, so = <#ryear>
+ - Accepts a tag parameter of y=nnnn, which will return the total rainfall for the specified year. Eg. <#AnnualRainfall y=2021>
+
+Changed
+- Daily backup now runs asynchronously to prevent it stopping MX continue to run
+
+Fixed
+- Davis VP2 connection type being decoded from Cumulus.ini incorrectly
+- Add missing knots input to JSON station
+- Solar W/m2 should use superscript in dashboard
+- Cumulus Calculates SLP giving a spike on start-up with the following stations: Ecowitt Local API, Davis Cloud Station, Ecowitt Cloud Station, HTTP Ambient, HTTP Ecowitt
+- Add missing wind run units and extra space in humidity % in ai2 dashboard
+- Remove UV/Solar missing data messages from Davis Cloud (VP2)
+- A new version of MigrateData3to4 (1.0.3) to fix issues migrating the day file
+- Negative 0.0 appearing when no rainfall has occurred
+
+
+
4.1.1 - b4025
—————————————
Fixed