diff --git a/brewpi/brewpi.py b/brewpi/brewpi.py index 64a10a0..f4bc9a2 100755 --- a/brewpi/brewpi.py +++ b/brewpi/brewpi.py @@ -96,11 +96,24 @@ if __name__ == '__main__': # Static, global info that doesn't belong to any one task - sent once; # a client connecting later still gets it via the dispatcher's replay - # of accumulated global_state on subscribe. + # of accumulated global_state on subscribe. AmbientTemp can also be + # changed live by a client (e.g. the GUI's ambient temperature field). + msg_system = dispatcher.msgio_get("System") + async def send_system_info(): - await dispatcher.msgio_get("System").send({'AmbientTemp': theta_amb, 'WarpFactor': DT / DT_TASK}) + await msg_system.send({'AmbientTemp': theta_amb, 'WarpFactor': DT / DT_TASK}) asyncio.ensure_future(send_system_info()) + async def on_system_recv(data): + global theta_amb + if 'AmbientTemp' in data: + theta_amb = data['AmbientTemp'] + pot.set_ambient_temperature(theta_amb) + if hasattr(tc, 'set_ambient_temperature'): + tc.set_ambient_temperature(theta_amb) + await msg_system.send({'AmbientTemp': theta_amb}) + msg_system.set_recv_handler(on_system_recv) + # Message dispatcher h_dispatcher = taskmgr.start() h_server = server.listen("0.0.0.0", 8765) diff --git a/client/brewpi.ui b/client/brewpi.ui index 4787b55..3f7e0b7 100644 --- a/client/brewpi.ui +++ b/client/brewpi.ui @@ -3682,6 +3682,35 @@ Connect + + + + 520 + 500 + 91 + 31 + + + + Ambient [°C] + + + Qt::AlignCenter + + + + + + 620 + 500 + 70 + 31 + + + + Set the simulated/assumed ambient temperature - press Enter to apply + + diff --git a/client/brewpi_gui.py b/client/brewpi_gui.py index e8a0527..509593c 100755 --- a/client/brewpi_gui.py +++ b/client/brewpi_gui.py @@ -329,6 +329,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): self.checkbox_heater_activate.stateChanged.connect(self.on_checkbox_changed) self.checkbox_stirrer_activate.stateChanged.connect(self.on_checkbox_stirrer_activate_changed) + self.lineEdit_ambient.returnPressed.connect(self.on_ambient_entry_changed) self.send_data = Queue() self.slider_pwr_initial_update = True @@ -460,6 +461,7 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): self.sud_state = None self.sud_user_message = None self.ambient_temp = None + self.lineEdit_ambient.clear() self.warp_factor = 1.0 self.sud_start_time = None self.sud_paused_total = 0.0 @@ -491,10 +493,22 @@ class Window(QtWidgets.QMainWindow, Ui_MainWindow): for key in msg: if "AmbientTemp" in key: self.ambient_temp = msg['AmbientTemp'] + # Only sync the entry's text while the user isn't actively + # editing it, so a server echo doesn't clobber in-progress + # typing. + if not self.lineEdit_ambient.hasFocus(): + self.lineEdit_ambient.setText("{:.1f}".format(self.ambient_temp)) elif "WarpFactor" in key: self.warp_factor = msg['WarpFactor'] self.update_status_env_label() + def on_ambient_entry_changed(self): + try: + value = float(self.lineEdit_ambient.text()) + except ValueError: + return + self.msg_system.send({'AmbientTemp': value}) + def update_status_env_label(self): ambient = "{:.1f}°C".format(self.ambient_temp) if self.ambient_temp is not None else "-" self.status_label_env.setText("Ambient: {} Warp: {:.1f}x".format(ambient, self.warp_factor)) diff --git a/client/main_window.py b/client/main_window.py index 6a01936..d0d1661 100644 --- a/client/main_window.py +++ b/client/main_window.py @@ -1230,6 +1230,13 @@ class Ui_MainWindow(object): self.btn_connect = QtWidgets.QPushButton(self.centralwidget) self.btn_connect.setGeometry(QtCore.QRect(440, 500, 70, 31)) self.btn_connect.setObjectName("btn_connect") + self.label_ambient = QtWidgets.QLabel(self.centralwidget) + self.label_ambient.setGeometry(QtCore.QRect(520, 500, 91, 31)) + self.label_ambient.setAlignment(QtCore.Qt.AlignCenter) + self.label_ambient.setObjectName("label_ambient") + self.lineEdit_ambient = QtWidgets.QLineEdit(self.centralwidget) + self.lineEdit_ambient.setGeometry(QtCore.QRect(620, 500, 70, 31)) + self.lineEdit_ambient.setObjectName("lineEdit_ambient") self.label_8 = QtWidgets.QLabel(self.centralwidget) self.label_8.setGeometry(QtCore.QRect(200, 500, 67, 31)) self.label_8.setAlignment(QtCore.Qt.AlignCenter) @@ -1298,6 +1305,8 @@ class Ui_MainWindow(object): self.label_state.setText(_translate("MainWindow", "State")) self.plainTextUri.setPlainText(_translate("MainWindow", "ws://localhost:8765")) self.btn_connect.setText(_translate("MainWindow", "Connect")) + self.label_ambient.setText(_translate("MainWindow", "Ambient [°C]")) + self.lineEdit_ambient.setToolTip(_translate("MainWindow", "Set the simulated/assumed ambient temperature - press Enter to apply")) self.label_8.setText(_translate("MainWindow", "Host")) self.menu_File.setTitle(_translate("MainWindow", "&File")) self.menu_Help.setTitle(_translate("MainWindow", "&Help")) diff --git a/components/pid/temp_controller_smith.py b/components/pid/temp_controller_smith.py index 066fdfb..c3008b0 100755 --- a/components/pid/temp_controller_smith.py +++ b/components/pid/temp_controller_smith.py @@ -28,6 +28,10 @@ class TempController(TempControllerBase): self.model.set_thermal_params(M, C) self.model_delay.set_thermal_params(M, C) + def set_ambient_temperature(self, theta_amb): + self.model.set_ambient_temperature(theta_amb) + self.model_delay.set_ambient_temperature(theta_amb) + def on_state_entered(self, state): if state == States.HEAT: self.model.initial(self.theta_ist)