public void Write(ControllerState controllerState, bool forceImmidiateAction) { var now = DateTime.UtcNow; foreach (var heater in controllerState.HeaterIdToState.Values) { PowerZoneState heaterPowerZoneState = null; if (heater.Heater.PowerZoneId.HasValue) { heaterPowerZoneState = controllerState.PowerZoneIdToState[heater.Heater.PowerZoneId.Value]; } if (CanSwitchState(now, heater, heaterPowerZoneState, forceImmidiateAction) && StateShouldBeUpdated(heater)) { _powerOutputProvider.Provide(heater.Heater.DigitalOutput.ProtocolName) .SetState(heater.Heater.DigitalOutput.OutputDescriptor, heater.OutputState); _usageCollector.Collect(heater, controllerState); heater.LastStateChange = now; if (heaterPowerZoneState != null) { heaterPowerZoneState.LastOutputStateChange = now; } } } }
public void Limit(PowerZoneState powerZoneState, ControllerState controllerState) { var availablePower = powerZoneState.PowerZone.MaxUsage; var requiredHeaterIdToUsage = powerZoneState.PowerZone .Heaters .Where(h => controllerState.HeaterIdToState[h.HeaterId].OutputState) .ToDictionary(h => h.HeaterId, h => h.UsagePerHour); if (requiredHeaterIdToUsage.Values.Sum(x => x) <= availablePower) { return; } var now = DateTime.UtcNow; if (powerZoneState.NextIntervalIncrementationDateTime < now) { unchecked { powerZoneState.NextIntervalOffset++; } powerZoneState.NextIntervalIncrementationDateTime = now.AddMinutes(powerZoneState.PowerZone.RoundRobinIntervalMinutes); } var heatersThatCantBeDisabled = powerZoneState.PowerZone .Heaters .Where(h => { var heaterState = controllerState.HeaterIdToState[h.HeaterId]; return(heaterState.OutputState && (now - heaterState.LastStateChange).TotalSeconds <= h.MinimumStateChangeIntervalSeconds); }) .ToDictionary(h => h.HeaterId);; var totalPower = 0m; foreach (var heater in heatersThatCantBeDisabled) { totalPower += heater.Value.UsagePerHour; } for (var i = 0; i < requiredHeaterIdToUsage.Count; i++) { var heaterIndex = (i + powerZoneState.NextIntervalOffset) % requiredHeaterIdToUsage.Count; var heater = requiredHeaterIdToUsage.ElementAt(heaterIndex); if (totalPower + heater.Value <= availablePower) { totalPower += heater.Value; } else if (!heatersThatCantBeDisabled.ContainsKey(heater.Key)) { controllerState.HeaterIdToState[heater.Key].OutputState = false; } } }
private static bool CanSwitchState(DateTime now, HeaterState heater, PowerZoneState heaterPowerZoneState, bool forceImmidiateAction) { if (forceImmidiateAction) { return(true); } return((now - heater.LastStateChange).TotalSeconds >= heater.Heater.MinimumStateChangeIntervalSeconds && (heaterPowerZoneState == null || (now - heaterPowerZoneState.LastOutputStateChange).TotalSeconds >= heaterPowerZoneState.PowerZone.SwitchDelayBetweenOutputsSeconds)); }
public void iterates_over_available_heaters(byte iteration, bool state1, bool state2, bool state3, bool state4) { // Arrange var heater1 = new Heater { HeaterId = 1, UsagePerHour = 0.9m }; var heater2 = new Heater { HeaterId = 2, UsagePerHour = 0.9m }; var heater3 = new Heater { HeaterId = 3, UsagePerHour = 0.9m }; var heater4 = new Heater { HeaterId = 4, UsagePerHour = 0.9m }; var heaterState1 = new HeaterState { Heater = heater1, OutputState = true }; var heaterState2 = new HeaterState { Heater = heater2, OutputState = true }; var heaterState3 = new HeaterState { Heater = heater3, OutputState = true }; var heaterState4 = new HeaterState { Heater = heater4, OutputState = true }; var powerZoneState = new PowerZoneState { NextIntervalOffset = iteration, PowerZone = new PowerZone { MaxUsage = 2.1m, Heaters = new List <Heater> { heater1, heater2, heater3, heater4 } } }; var controllerState = new ControllerState { HeaterIdToState = new Dictionary <int, HeaterState> { [1] = heaterState1, [2] = heaterState2, [3] = heaterState3, [4] = heaterState4 } }; // Act var powerZoneOutputAllowanceCalculator = new PowerZoneOutputLimiter(); powerZoneOutputAllowanceCalculator.Limit(powerZoneState, controllerState); // Assert Assert.Equal(state1, heaterState1.OutputState); Assert.Equal(state2, heaterState2.OutputState); Assert.Equal(state3, heaterState3.OutputState); Assert.Equal(state4, heaterState4.OutputState); }