mirror of
https://github.com/paparazzi/paparazzi.git
synced 2026-05-23 21:36:28 +08:00
[supervision] Add programs status. (#3014)
* [supervision] Add programs status. * [supervision] turn program line red on error.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
from PyQt5.QtWidgets import *
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
from PyQt5.QtCore import QProcess
|
||||
import utils
|
||||
from generated.ui_configuration_panel import Ui_ConfigurationPanel
|
||||
from program_widget import ProgramWidget
|
||||
from program_widget import ProgramWidget, TabProgramsState
|
||||
from conf import *
|
||||
from programs_conf import parse_tools
|
||||
import subprocess
|
||||
@@ -12,6 +13,7 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
|
||||
|
||||
clear_error = QtCore.pyqtSignal()
|
||||
ac_edited = QtCore.pyqtSignal(Aircraft)
|
||||
program_state_changed = QtCore.pyqtSignal(TabProgramsState)
|
||||
|
||||
def __init__(self, parent=None, *args, **kwargs):
|
||||
QWidget.__init__(self, parent=parent, *args, **kwargs)
|
||||
@@ -19,6 +21,7 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
|
||||
self.console_widget.filter_widget.hide()
|
||||
self.currentAC = None # type: Aircraft
|
||||
self.flight_plan_editor = None
|
||||
self.programs_state: TabProgramsState = TabProgramsState.IDLE
|
||||
self.conf_widget.conf_changed.connect(self.handle_conf_changed)
|
||||
self.conf_widget.setting_changed.connect(self.handle_setting_changed)
|
||||
self.conf_widget.flight_plan.edit_alt.connect(self.edit_flightplan_gcs)
|
||||
@@ -84,7 +87,7 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
|
||||
self.programs_widget.layout().addWidget(pw)
|
||||
pw.ready_read_stderr.connect(lambda: self.console_widget.handle_stderr(pw))
|
||||
pw.ready_read_stdout.connect(lambda: self.console_widget.handle_stdout(pw))
|
||||
pw.finished.connect(lambda c, s: self.console_widget.handle_program_finished(pw, c, s))
|
||||
pw.finished.connect(lambda c, s: self.handle_program_finished(pw, c, s))
|
||||
if cb is not None:
|
||||
pw.finished.connect(cb)
|
||||
pw.remove.connect(lambda: self.remove_program(pw))
|
||||
@@ -92,6 +95,17 @@ class ConfigurationPanel(QWidget, Ui_ConfigurationPanel):
|
||||
if not settings.value("keep_build_programs", False, bool):
|
||||
pw.finished.connect(lambda: self.remove_program(pw))
|
||||
pw.start_program()
|
||||
self.programs_state = TabProgramsState.RUNNING
|
||||
self.program_state_changed.emit(self.programs_state)
|
||||
|
||||
def handle_program_finished(self, pw: ProgramWidget, exit_code: int, exit_status: QProcess.ExitStatus):
|
||||
self.console_widget.handle_program_finished(pw, exit_code, exit_status)
|
||||
if exit_code != 0 and exit_code != 15:
|
||||
self.programs_state = TabProgramsState.ERROR
|
||||
else:
|
||||
if len(self.programs_widget.layout().children()) == 0 and self.programs_state != TabProgramsState.ERROR:
|
||||
self.programs_state = TabProgramsState.IDLE
|
||||
self.program_state_changed.emit(self.programs_state)
|
||||
|
||||
def remove_program(self, pw: ProgramWidget):
|
||||
self.programs_widget.layout().removeWidget(pw)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 8.4666665 8.4666669"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
|
||||
sodipodi:docname="error.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.9195959"
|
||||
inkscape:cx="18.390889"
|
||||
inkscape:cy="24.391595"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1848"
|
||||
inkscape:window-height="1016"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="1107"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-288.53331)">
|
||||
<circle
|
||||
style="fill:#ff4444;fill-opacity:1;stroke:none;stroke-width:0.25604835;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path815"
|
||||
cx="4.2333331"
|
||||
cy="292.76663"
|
||||
r="3.8407259" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -13,6 +13,14 @@ from conf import Conf, Aircraft, ConfError
|
||||
from app_settings import AppSettings
|
||||
from generated.ui_supervision_window import Ui_SupervisionWindow
|
||||
from generated.ui_new_ac_dialog import Ui_NewACDialog
|
||||
from program_widget import TabProgramsState
|
||||
|
||||
dirname = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
TAB_ICONS = {TabProgramsState.IDLE: QtGui.QIcon(),
|
||||
TabProgramsState.RUNNING: QtGui.QIcon(os.path.join(dirname, "running.svg")),
|
||||
TabProgramsState.ERROR: QtGui.QIcon(os.path.join(dirname, "error.svg"))}
|
||||
|
||||
|
||||
class PprzCenter(QMainWindow, Ui_SupervisionWindow):
|
||||
@@ -40,6 +48,9 @@ class PprzCenter(QMainWindow, Ui_SupervisionWindow):
|
||||
self.header.ac_save.connect(lambda _: self.conf.save())
|
||||
self.header.ac_new.connect(self.handle_new_ac)
|
||||
|
||||
self.configuration_panel.program_state_changed.connect(lambda state: self.programs_state_changed(state, 0))
|
||||
self.operation_panel.session.program_state_changed.connect(lambda state: self.programs_state_changed(state, 1))
|
||||
|
||||
self.operation_panel.session.program_spawned.connect(self.header.disable_sets)
|
||||
self.operation_panel.session.programs_all_stopped.connect(self.header.enable_sets)
|
||||
|
||||
@@ -51,6 +62,9 @@ class PprzCenter(QMainWindow, Ui_SupervisionWindow):
|
||||
self.operation_panel.session.init()
|
||||
self.header.update_sets()
|
||||
|
||||
def programs_state_changed(self, state: TabProgramsState, tab_index):
|
||||
self.tabwidget.setTabIcon(tab_index, TAB_ICONS[state])
|
||||
|
||||
def handle_set_changed(self, conf_file):
|
||||
self.conf = Conf(conf_file)
|
||||
Conf.set_current_conf(conf_file)
|
||||
|
||||
@@ -9,12 +9,20 @@ from PyQt5.QtCore import QProcess
|
||||
from PyQt5.QtGui import QIcon
|
||||
import utils
|
||||
from typing import List
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class TabProgramsState(Enum):
|
||||
IDLE = 0
|
||||
RUNNING = 1
|
||||
ERROR = 2
|
||||
|
||||
|
||||
class ProgramWidget(QWidget, Ui_Program):
|
||||
|
||||
ready_read_stdout = QtCore.pyqtSignal()
|
||||
ready_read_stderr = QtCore.pyqtSignal()
|
||||
started = QtCore.pyqtSignal()
|
||||
finished = QtCore.pyqtSignal(int, QProcess.ExitStatus)
|
||||
remove = QtCore.pyqtSignal()
|
||||
|
||||
@@ -38,8 +46,10 @@ class ProgramWidget(QWidget, Ui_Program):
|
||||
self.icon_label.setToolTip(shortname)
|
||||
|
||||
def start_program(self):
|
||||
self.program_lineedit.setStyleSheet("")
|
||||
if self.process.state() == QProcess.NotRunning:
|
||||
self.process.start(self.cmd[0], self.cmd[1:])
|
||||
self.started.emit()
|
||||
|
||||
def handle_cmd_return(self):
|
||||
if self.process.state() == QProcess.NotRunning:
|
||||
@@ -68,6 +78,8 @@ class ProgramWidget(QWidget, Ui_Program):
|
||||
self.program_lineedit.setReadOnly(True)
|
||||
|
||||
def handle_finished(self, exit_code: int, exit_status: QProcess.ExitStatus):
|
||||
if exit_code not in (0, 15):
|
||||
self.program_lineedit.setStyleSheet("background: #f56464")
|
||||
icon = QIcon.fromTheme("media-playback-start")
|
||||
self.run_button.setIcon(icon)
|
||||
self.program_lineedit.setReadOnly(False)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 8.4666665 8.4666669"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
|
||||
sodipodi:docname="running.svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="7.9195959"
|
||||
inkscape:cx="18.390889"
|
||||
inkscape:cy="24.391595"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
inkscape:window-width="1848"
|
||||
inkscape:window-height="1016"
|
||||
inkscape:window-x="72"
|
||||
inkscape:window-y="1107"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-288.53331)">
|
||||
<circle
|
||||
style="fill:#277eff;fill-opacity:1;stroke:none;stroke-width:0.25604835;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path815"
|
||||
cx="4.2333331"
|
||||
cy="292.76663"
|
||||
r="3.8407259" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -10,7 +10,7 @@ import utils
|
||||
import lxml.etree as ET
|
||||
|
||||
from typing import List, Optional, Tuple, Dict
|
||||
from program_widget import ProgramWidget
|
||||
from program_widget import ProgramWidget, TabProgramsState
|
||||
from tools_menu import ToolMenu
|
||||
from programs_conf import *
|
||||
from conf import *
|
||||
@@ -21,6 +21,7 @@ class SessionWidget(QWidget, Ui_Session):
|
||||
|
||||
programs_all_stopped = QtCore.pyqtSignal()
|
||||
program_spawned = QtCore.pyqtSignal()
|
||||
program_state_changed = QtCore.pyqtSignal(TabProgramsState)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent=parent)
|
||||
@@ -33,6 +34,7 @@ class SessionWidget(QWidget, Ui_Session):
|
||||
self.tools_menu = ToolMenu()
|
||||
self.sessions_combo.addItems(["Simulation", "Replay"])
|
||||
self.sessions_combo.insertSeparator(2)
|
||||
self.programs_state: TabProgramsState = TabProgramsState.IDLE
|
||||
self.menu_button.addAction(self.save_session_action)
|
||||
self.menu_button.addAction(self.save_as_action)
|
||||
self.menu_button.addAction(self.rename_session_action)
|
||||
@@ -71,6 +73,7 @@ class SessionWidget(QWidget, Ui_Session):
|
||||
return self.sessions_combo.currentText()
|
||||
|
||||
def start_session(self):
|
||||
self.reset_programs_status()
|
||||
combo_text = self.sessions_combo.currentText()
|
||||
if combo_text == "Simulation":
|
||||
self.start_simulation()
|
||||
@@ -145,6 +148,7 @@ class SessionWidget(QWidget, Ui_Session):
|
||||
pw.ready_read_stderr.connect(lambda: self.console.handle_stderr(pw))
|
||||
pw.ready_read_stdout.connect(lambda: self.console.handle_stdout(pw))
|
||||
pw.finished.connect(lambda c, s: self.handle_program_finished(pw, c, s))
|
||||
pw.started.connect(self.handle_program_started)
|
||||
pw.remove.connect(lambda: self.remove_program(pw))
|
||||
# if REMOVE_PROGRAMS_FINISHED:
|
||||
# pw.finished.connect(lambda: self.remove_program(pw))
|
||||
@@ -169,17 +173,34 @@ class SessionWidget(QWidget, Ui_Session):
|
||||
if not self.any_program_running():
|
||||
self.programs_all_stopped.emit()
|
||||
|
||||
if c != 0 and c != 15:
|
||||
self.programs_state = TabProgramsState.ERROR
|
||||
else:
|
||||
if not self.any_program_running() and self.programs_state != TabProgramsState.ERROR:
|
||||
self.programs_state = TabProgramsState.IDLE
|
||||
self.program_state_changed.emit(self.programs_state)
|
||||
|
||||
def handle_program_started(self):
|
||||
self.programs_state = TabProgramsState.RUNNING
|
||||
self.program_state_changed.emit(self.programs_state)
|
||||
def reset_programs_status(self):
|
||||
self.programs_state = TabProgramsState.IDLE
|
||||
self.program_state_changed.emit(self.programs_state)
|
||||
|
||||
def stop_all(self):
|
||||
for pw in self.program_widgets:
|
||||
pw.terminate()
|
||||
self.reset_programs_status()
|
||||
|
||||
def start_all(self):
|
||||
for pw in self.program_widgets:
|
||||
pw.start_program()
|
||||
self.reset_programs_status()
|
||||
|
||||
def remove_all(self):
|
||||
for pw in list(self.program_widgets):
|
||||
pw.handle_remove()
|
||||
self.reset_programs_status()
|
||||
|
||||
def init_tools_menu(self):
|
||||
for t in self.tools.values():
|
||||
|
||||
Reference in New Issue
Block a user