/// <summary> /// Kontrollerar om klippning är nödvändigt, dvs. om antalet timmar per dag redan är uppfyllt för de senaste dagarna. /// </summary> /// <returns>true om klippning är nödvändig, annars false.</returns> private bool MowingNecessary() { double mowingTimeToday; double averageWorkingHours = LogAnalyzer.GetAverageMowingTime(IterationTime, out mowingTimeToday); // If not enough hours to reach average in the last few days, return necessary. if (averageWorkingHours < Config.AverageWorkPerDayHours) { return(true); } // If not mowed the average time today, then return necessary if (mowingTimeToday < Config.AverageWorkPerDayHours) { return(true); } return(false); }
/// <summary> /// Kollar om strömmen ska slås på eller av och gör det i sådana fall. /// </summary> public void CheckAndAct() { if (_isActing) { Logger.Write(SystemTime.Now, LogType.Debug, LogLevel.Error, "Last CheckAndAct was not finished."); return; } _isActing = true; bool mowerLeftThisTurn = false; if (!HomeFromStart.HasValue) { HomeFromStart = HomeSensor.IsHome; } IterationTime = SystemTime.Now; if (IterationTime.Hour == 0 && IterationTime.Minute == 0) { Logger.Write(IterationTime, LogType.NewDay, LogLevel.Debug, "A new day has begun."); } LogAnalyzer = new LogAnalyzer(Logger, HomeFromStart.Value); if (Config.TimeIntervals == null) { throw new InvalidOperationException(); } // Calculate forecast hours ForecastHours = NextOrCurrentInterval.EndHour - IterationTime.Hour + 2; if (Config.UsingContactHomeSensor) { ForecastHours = Config.MaxMowingHoursWithoutCharge + 1; } try { // Write the hourly report log item if (IterationTime.Minute == 0) { var hourlyReportItem = Logger.LogItems.OrderByDescending(r => r.Time).FirstOrDefault(x => x.Type == LogType.HourlyReport); if (hourlyReportItem == null || hourlyReportItem.Time.ToString("yyyy-MM-dd HH:mm") != IterationTime.ToString("yyyy-MM-dd HH:mm")) { int wetness = RainSensor is SmhiRainSensor ? ((SmhiRainSensor)RainSensor).Wetness : 0; string weatherAheadDescription; WeatherForecast.CheckIfWeatherWillBeGood(12, out weatherAheadDescription); Logger.Write(IterationTime, LogType.HourlyReport, LogLevel.InfoLessInteresting, "Hourly report: " + weatherAheadDescription + " Current wetness: " + wetness); } } // If a report was not made for yesterday, and if mowing started yesterday or before, create a report. var startLogItem = Logger.LogItems.FirstOrDefault(x => x.Type == LogType.MowControllerStarted); var todayStartTime = new DateTime(IterationTime.Year, IterationTime.Month, IterationTime.Day, 0, 0, 0); if (startLogItem.Time < todayStartTime) { var yesterdayStartTime = new DateTime(IterationTime.Year, IterationTime.Month, IterationTime.Day, 0, 0, 0).AddDays(-1); var reportLogItem = Logger.LogItems.FirstOrDefault(x => x.Type == LogType.DailyReport && x.Time >= todayStartTime && x.Time < todayStartTime.AddDays(1)); if (reportLogItem == null) { var mowingLogItems = Logger.LogItems.Where(x => (x.Type == LogType.MowingStarted || x.Type == LogType.MowingEnded) && x.Time >= yesterdayStartTime && x.Time < todayStartTime); var sb = new StringBuilder(); sb.AppendLine("Mowing Summary " + yesterdayStartTime.ToString("yyyy-MM-dd")); sb.AppendLine(); DateTime startTime = DateTime.MinValue; var mowingTime = new TimeSpan(); foreach (LogItem mowingLogItem in mowingLogItems) { sb.Append(mowingLogItem.Time.ToString("HH:mm")); sb.Append(" "); sb.AppendLine(mowingLogItem.Message); if (mowingLogItem.Type == LogType.MowingStarted) { startTime = mowingLogItem.Time; } else { mowingTime += (mowingLogItem.Time - startTime); } } TimeSpan mowingTimeSpan = LogAnalyzer.GetMowingTimeForDay(yesterdayStartTime); sb.AppendLine(); sb.Append("Total mowed: "); sb.Append(mowingTimeSpan.Hours); sb.Append(":"); sb.Append(mowingTimeSpan.Minutes.ToString("00")); sb.AppendLine(" hours."); // If contact sensor, also add detailed mowing summary if (Config.UsingContactHomeSensor) { TimeSpan actualMowingTimeSpan = LogAnalyzer.GetActuallyMowingTimeForDay(yesterdayStartTime); sb.Append("Actual out mowing time: "); sb.Append(actualMowingTimeSpan.Hours); sb.Append(":"); sb.Append(actualMowingTimeSpan.Minutes.ToString("00")); sb.AppendLine(" hours."); TimeSpan exactMowerAwayTimeSpan = LogAnalyzer.GetMowerAwayTimeForDay(yesterdayStartTime); sb.Append("Exact mower away time: "); sb.Append(exactMowerAwayTimeSpan.Hours); sb.Append(":"); sb.Append(exactMowerAwayTimeSpan.Minutes.ToString("00")); sb.AppendLine(" hours."); } Logger.Write(IterationTime, LogType.DailyReport, LogLevel.InfoMoreInteresting, sb.ToString()); } } // Check if mower has entered or exited its home since last time if (_mowerIsHome != HomeSensor.IsHome) { _mowerIsHome = HomeSensor.IsHome; if (_mowerIsHome) { Logger.Write(IterationTime, LogType.MowerCame, LogLevel.Info, "Mower came."); SetMowingStarted(); } else { Logger.Write(IterationTime, LogType.MowerLeft, LogLevel.Info, "Mower left."); mowerLeftThisTurn = true; } } // Check if mower is lost or stuck, but only if contact sensor is used. if (Config.UsingContactHomeSensor && !LogAnalyzer.IsLost) { //int forecastHours = Config.MaxMowingHoursWithoutCharge + 1; if (_mowerIsHome && PowerSwitch.Status == PowerStatus.On && !RainSensor.IsWet && WeatherForecast.CheckIfWeatherWillBeGood(ForecastHours) && LogAnalyzer.IsMowing && !LogAnalyzer.IsStuck) { var lastEssentialLogItem = Logger.LogItems .OrderByDescending(x => x.Time) .FirstOrDefault(x => x.Type == LogType.MowerCame || x.Type == LogType.PowerOn || x.Type == LogType.PowerOff || x.Type == LogType.MowingStarted || x.Type == LogType.MowingEnded); bool mowerHasHadEnoughChargingTime = (lastEssentialLogItem != null && lastEssentialLogItem.Time.AddHours(Config.MaxChargingHours) <= IterationTime); if (mowerHasHadEnoughChargingTime && !BetweenIntervals) { Logger.Write(IterationTime, LogType.MowerStuckInHome, LogLevel.InfoMoreInteresting, $"Mower seems to be stuck at home. It did not leave after {Config.MaxMowingHoursWithoutCharge} hours of charging time."); SetMowingEnded(); } } if (!_mowerIsHome) { var lastMowerLeftLogItem = Logger.LogItems .OrderByDescending(x => x.Time) .FirstOrDefault(x => x.Type == LogType.MowerLeft); var lastLogItem = Logger.LogItems .OrderByDescending(x => x.Time) .FirstOrDefault(x => x.Type == LogType.MowerLost || x.Type == LogType.MowerCame); if (lastMowerLeftLogItem?.Time.AddHours(Config.MaxMowingHoursWithoutCharge) <= IterationTime && lastLogItem?.Type != LogType.MowerLost) { Logger.Write(IterationTime, LogType.MowerLost, LogLevel.InfoMoreInteresting, $"Mower seems to be lost. It did not return home after {Config.MaxMowingHoursWithoutCharge} hours as expected."); if (!BetweenIntervals) { SetMowingEnded(); } } } } // Check if there has ocurred an interval start, and in case it has, write a log message bool atLastMinuteOfInterval = NextOrCurrentInterval.EndHour == IterationTime.Hour && NextOrCurrentInterval.EndMin == IterationTime.Minute; if (!BetweenIntervals && PowerSwitch.Status == PowerStatus.On && !LogAnalyzer.IsMowing && !LogAnalyzer.IsLost && (!LogAnalyzer.IsStuck || mowerLeftThisTurn) && !RainSensor.IsWet && WeatherForecast.CheckIfWeatherWillBeGood(ForecastHours) && !atLastMinuteOfInterval) { SetMowingStarted(); } // Turn on power if (PowerSwitch.Status != PowerStatus.On) { if (!RainSensor.IsWet) { foreach (var interval in Config.TimeIntervals) { // Om ett intervall håller på if (interval.ContainsTime(IterationTime)) { DateTime minutesFromEnd = (new DateTime(IterationTime.Year, IterationTime.Month, IterationTime.Day, interval.EndHour, interval.EndMin, 0).AddMinutes(-10)); // If the interval is not close to end if (IterationTime < minutesFromEnd) { //int forecastHours = interval.EndHour - IterationTime.Hour + 2; //if (Config.UsingContactHomeSensor) //{ // forecastHours = Config.MaxMowingHoursWithoutCharge + 1; //} string weatherAheadDescription; bool weatherWillBeGood = WeatherForecast.CheckIfWeatherWillBeGood(ForecastHours, out weatherAheadDescription); bool mowingNecessary = MowingNecessary(); if (weatherWillBeGood && mowingNecessary) { PowerSwitch.TurnOn(); Logger.Write(IterationTime, LogType.PowerOn, LogLevel.Info, "Power was turned on. " + weatherAheadDescription); SetMowingStarted(); } } } } } if (BetweenIntervals && !RightBeforeIntervalStarts && SafelyAfterIntervalEnd) { PowerSwitch.TurnOn(); Logger.Write(IterationTime, LogType.PowerOn, LogLevel.Info, "Power was turned on. In between intervals."); } } // Turn off power if (PowerSwitch.Status != PowerStatus.Off) { if (HomeSensor.IsHome) // TODO: Fel i TimeBasedHomeSensor när kraften är Unknown... { DateTime nextIntervalExactStartTime = new DateTime(IterationTime.Year, IterationTime.Month, IterationTime.Day, NextInterval.StartHour, NextInterval.StartMin, 0); if (nextIntervalExactStartTime < IterationTime) { nextIntervalExactStartTime = nextIntervalExactStartTime.AddDays(1); } double minutesLeftToIntervalStart = (nextIntervalExactStartTime - IterationTime).TotalMinutes; // If there will be rain, turn off power //int forecastHours = NextInterval.ToTimeSpan().Hours + 2; // If a contact home sensor is used, weather can be checked for much smaller time spans if (Config.UsingContactHomeSensor) { ForecastHours = Config.MaxMowingHoursWithoutCharge + 1; } if (minutesLeftToIntervalStart <= 5 || !BetweenIntervals && HomeSensor.IsHome && (IterationTime - HomeSensor.MowerCameTime).TotalMinutes >= 30 || PowerSwitch.Status == PowerStatus.Unknown) { string logMessage; bool weatherWillBeGood = WeatherForecast.CheckIfWeatherWillBeGood(ForecastHours, out logMessage); if (weatherWillBeGood && RainSensor.IsWet) { logMessage = "Grass is wet."; } if (RainSensor.IsWet || !weatherWillBeGood) { PowerSwitch.TurnOff(); Logger.Write(IterationTime, LogType.PowerOff, LogLevel.Info, "Power was turned off. " + logMessage); if (!BetweenIntervals) { SetMowingEnded(); } } else if (!MowingNecessary()) // If mowing not necessary, turn off power { PowerSwitch.TurnOff(); Logger.Write(IterationTime, LogType.PowerOff, LogLevel.Info, "Power was turned off. Mowing not necessary."); if (!BetweenIntervals) { SetMowingEnded(); } } } } } // Check if we're at an interval end, and in case we are, write a log message if (!BetweenIntervals && PowerSwitch.Status == PowerStatus.On && NextOrCurrentInterval.EndHour == IterationTime.Hour && NextOrCurrentInterval.EndMin == IterationTime.Minute) { SetMowingEnded(); } } catch (Exception ex) { string lastMsg = ""; if (Logger.LogItems.Count > 0) { lastMsg = Logger.LogItems[Logger.LogItems.Count - 1].Message; } if (ex.Message != lastMsg) { Logger.Write(IterationTime, LogType.Failure, LogLevel.Error, ex.Message); } } finally { _isActing = false; } }