def test_switch_heating_and_cooling_without_threshold( self, pid_mock_class, control, h_current, h_switch, c_current, c_switch): pid_mock = pid_mock_class.return_value self.heater.get.return_value = h_current self.cooler.get.return_value = c_current pid_mock.return_value = control tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) controller.control() if h_switch is None: self.heater.set.assert_not_called() else: self.heater.set.assert_called_with(h_switch) if c_switch is None: self.cooler.set.assert_not_called() else: self.cooler.set.assert_called_with(c_switch)
def test_updates_pid_values_on_control(self, pid_mock_class): pid_mock = pid_mock_class.return_value tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0.5, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) self.config.get.side_effect = lambda key: { "p": 4, "i": 5, "d": 6, "target": 23, "heating_limit": 40, "limit_window": 5 }.get(key) pid_mock.return_value = 5 controller.control() tunings_mock.assert_called_with((4, 5, 6))
def test_initiates_with_pid_values_and_target_from_config( self, pid_mock_class): Controller(self.config, 5, 0.5, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) pid_mock_class.assert_called_with(Kp=1, Ki=2, Kd=3, setpoint=23, sample_time=5)
def main(): config = Config("./config.json") secrets = Config("./secret.json") display = Display(["environment", "vessel", "fridge", "target"], ["heater", "cooler", "limiter"]) display.handle_temperature("target", config.get("target"), config.get("target")) data_collector = DataCollector(["environment", "vessel", "fridge", "target"], ["heater", "cooler", "limiter"]) csv_writer = CsvWriter(data_collector) influxdb_writer = InfluxDBWriter(secrets, data_collector) data_collector.handle_temperature("target", config.get("target"), config.get("target")) env_sensor = Sensor("environment", "28-0301a2798a9f", "/sys/bus/w1/devices", config.get("average_window"), [display, data_collector]) vessel_sensor = Sensor("vessel", "28-0301a2799ddf", "/sys/bus/w1/devices", config.get("average_window"), [display, data_collector]) fridge_sensor = Sensor("fridge", "28-0301a27988e2", "/sys/bus/w1/devices", config.get("average_window"), [display, data_collector]) heater_ssr = Ssr("heater", 16, [display, data_collector]) cooler_ssr = Ssr("cooler", 20, [display, data_collector]) limiter = Limiter("limiter", heater_ssr, [display, data_collector]) controller = Controller(config, config.get("control_interval"), config.get("control_deadband"), heater_ssr, cooler_ssr, limiter, vessel_sensor, fridge_sensor, [data_collector]) runnables = [(env_sensor, config.get("sensor_interval"), 0), (vessel_sensor, config.get("sensor_interval"), 0), (fridge_sensor, config.get("sensor_interval"), 0), (controller, config.get("control_interval"), 2), (config, config.get("config_interval"), 0), (csv_writer, config.get("csv_interval"), 0), (influxdb_writer, config.get("influxdb_interval"), 0)] threads = [] for runnable, interval, init_delay in runnables: t = Thread(target=runnable.start, args=(interval, init_delay)) threads.append(t) t.start() shutdown.wait() logger.info("Received interrupt, shutting down") for index, (runnable, interval, init_delay) in enumerate(runnables): runnable.stop() threads[index].join() display.shutdown() heater_ssr.shutdown()
def test_only_updates_pid_values_if_changed(self, pid_mock_class): pid_mock = pid_mock_class.return_value tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0.5, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) pid_mock.return_value = 5 controller.control() tunings_mock.assert_called_once_with() # the getter, not the setter
def test_publishes_to_listeners(self, pid_mock_class): pid_mock = pid_mock_class.return_value tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0.5, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, [self.listener]) pid_mock.return_value = .4 controller.control() self.listener.handle_controller.assert_called_with(4, 3, 2, .4)
def test_dont_switch_heating_on_if_under_heating_threshold( self, pid_mock_class): pid_mock = pid_mock_class.return_value self.heater.get.return_value = False pid_mock.return_value = 0.4 tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0.5, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) controller.control() self.heater.set.assert_not_called()
def test_feeds_current_temperature_to_pid(self, pid_mock_class): pid_mock = pid_mock_class.return_value current_temp = Mock() self.current_temp.get_average.return_value = current_temp tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0.5, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) pid_mock.return_value = 5 controller.control() pid_mock.assert_called_with(current_temp)
def test_switch_off_heating_if_over_heating_limit(self, pid_mock_class, fridge_temperature, cb_current, cb_switch, h_current, h_switch): pid_mock = pid_mock_class.return_value self.fridge_temp.get.return_value = fridge_temperature self.heater.get.return_value = h_current self.cooler.get.return_value = False self.limiter.get.return_value = cb_current pid_mock.return_value = 3 # would trigger the heater tunings_mock = PropertyMock() type(pid_mock).tunings = tunings_mock tunings_mock.return_value = (1, 2, 3) components_mock = PropertyMock() type(pid_mock).components = components_mock components_mock.return_value = (4, 3, 2) controller = Controller(self.config, 5, 0, self.heater, self.cooler, self.limiter, self.current_temp, self.fridge_temp, []) controller.control() if cb_switch is None: self.limiter.set.assert_not_called() else: self.limiter.set.assert_called_with(cb_switch) if h_switch is None: self.heater.set.assert_not_called() else: self.heater.set.assert_called_with(h_switch)