diff --git a/src/Solver/AdisTS.py b/src/Solver/AdisTS.py
index 5219c851..942afc16 100644
--- a/src/Solver/AdisTS.py
+++ b/src/Solver/AdisTS.py
@@ -112,7 +112,7 @@ class AdisTS(CommandLineSolver):
for line in lines:
rep_file.write(line.line)
- def _export_REP(self, study, repertory, files, qlog, name="0"):
+ def _export_REP(self, study, repertory, mage_rep, files, qlog, name="0"):
if qlog is not None:
qlog.put("Export REP file")
@@ -124,8 +124,8 @@ class AdisTS(CommandLineSolver):
), "w+"
) as f:
- f.write(f"NET ../default-mage/{name}.NET\n")
- f.write(f"REP ../default-mage/{name}.REP\n")
+ f.write(f"NET ../{mage_rep}/{name}.NET\n")
+ f.write(f"REP ../{mage_rep}/{name}.REP\n")
for file in files:
EXT = file.split('.')[1]
@@ -133,7 +133,7 @@ class AdisTS(CommandLineSolver):
self._export_REP_additional_lines(study, f)
- path_mage_net = os.path.join(os.path.abspath(os.path.join(repertory, os.pardir)), f"default-mage/net")
+ path_mage_net = os.path.join(os.path.abspath(os.path.join(repertory, os.pardir)), f"{mage_rep}/net")
path_adists_net = os.path.join(repertory, "net")
if os.path.exists(path_mage_net):
@@ -466,7 +466,7 @@ class AdisTSlc(AdisTS):
]
@timer
- def export(self, study, repertory, qlog=None, name="0"):
+ def export(self, study, repertory, mage_rep, qlog=None, name="0"):
print("cmd solver adistslc : ", self._cmd_solver)
self._study = study
name = study.name.replace(" ", "_")
@@ -479,7 +479,7 @@ class AdisTSlc(AdisTS):
files = files + func(study, repertory, qlog, name=name)
self.export_additional_files(study, repertory, qlog, name=name)
- self._export_REP(study, repertory, files, qlog, name=name)
+ self._export_REP(study, repertory, mage_rep, files, qlog, name=name)
return True
except Exception as e:
diff --git a/src/View/CheckList/WindowAdisTS.py b/src/View/CheckList/WindowAdisTS.py
new file mode 100644
index 00000000..40bba4b4
--- /dev/null
+++ b/src/View/CheckList/WindowAdisTS.py
@@ -0,0 +1,200 @@
+# WindowAdisTS.py -- Pamhyr
+# Copyright (C) 2023-2024 INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+# -*- coding: utf-8 -*-
+
+from tools import trace, timer
+
+from View.Tools.PamhyrWindow import PamhyrWindow
+
+from PyQt5.QtGui import (
+ QKeySequence,
+)
+
+from PyQt5.QtCore import (
+ Qt, QVariant, QAbstractTableModel,
+ QCoreApplication, QModelIndex, QRect, QThread,
+ pyqtSlot, pyqtSignal,
+)
+
+from PyQt5.QtWidgets import (
+ QDialogButtonBox, QPushButton, QLineEdit,
+ QFileDialog, QTableView, QAbstractItemView,
+ QUndoStack, QShortcut, QAction, QItemDelegate,
+ QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
+ QProgressBar, QLabel,
+)
+
+from View.CheckList.Table import TableModel
+from View.CheckList.Worker import Worker
+from View.CheckList.Translate import CheckListTranslate
+
+_translate = QCoreApplication.translate
+
+
+class CheckListWindowAdisTS(PamhyrWindow):
+ _pamhyr_ui = "CheckList"
+ _pamhyr_name = "Check list"
+
+ signalStatus = pyqtSignal(str)
+
+ def __init__(self, autorun: bool = True,
+ study=None, config=None,
+ solver=None, parent=None, mage_rep=None):
+ trad = CheckListTranslate()
+
+ self._autorun = autorun
+ self._solver = solver
+ self._mage_rep = mage_rep
+
+ name = trad[self._pamhyr_name] + " - " + study.name
+
+ super(CheckListWindowAdisTS, self).__init__(
+ title=name,
+ study=study,
+ config=config,
+ trad=trad,
+ options=[],
+ parent=parent
+ )
+
+ # Add solver to hash computation data
+ self._hash_data.append(self._solver)
+
+ self._checker_list = (
+ self._study.checkers() +
+ self._solver.checkers()
+ )
+
+ self.setup_table()
+ self.setup_progress_bar()
+ self.setup_connections()
+ self.setup_thread()
+ self.setup_statusbar()
+
+ def setup_table(self):
+ table = self.find(QTableView, f"tableView")
+ self._table = TableModel(
+ table_view=table,
+ table_headers=self._trad.get_dict("table_headers"),
+ data=self._checker_list,
+ )
+
+ def setup_progress_bar(self):
+ self._progress = self.find(QProgressBar, f"progressBar")
+ self._p = 0 # Progress current step
+
+ self._progress.setRange(0, len(self._checker_list))
+ self._progress.setValue(self._p)
+
+ def setup_connections(self):
+ self.find(QPushButton, "pushButton_ok").clicked.connect(self.accept)
+ self.find(QPushButton, "pushButton_retry").clicked.connect(self.retry)
+ self.find(QPushButton, "pushButton_cancel")\
+ .clicked.connect(self.reject)
+
+ def setup_thread(self):
+ self._worker = Worker(self._study, self._checker_list)
+ self._worker_thread = QThread()
+ self._worker.moveToThread(self._worker_thread)
+
+ # Connect any worker signals
+ self._worker.signalStatus.connect(self.update)
+ self._worker_thread.started.connect(self._worker.process)
+
+ self._worker_thread.start()
+
+ def retry(self):
+ self._worker_thread.terminate()
+ self._worker_thread.wait()
+
+ self.find(QPushButton, "pushButton_retry").setEnabled(False)
+ self.find(QPushButton, "pushButton_ok").setEnabled(False)
+
+ self.setup_thread()
+
+ def _compute_status(self):
+ ok = len(list(filter(lambda c: c.is_ok(), self._checker_list)))
+ warning = len(
+ list(filter(lambda c: c.is_warning(), self._checker_list)))
+ error = len(list(filter(lambda c: c.is_error(), self._checker_list)))
+
+ return ok, warning, error
+
+ def _compute_status_label(self):
+ ok, warning, error = self._compute_status()
+ return (f"Ok: {ok} |" +
+ f"Warning: {warning} |" +
+ f"Error: {error}")
+
+ def setup_statusbar(self):
+ txt = self._compute_status_label()
+ self._status_label = QLabel(txt)
+ self.statusbar.addPermanentWidget(self._status_label)
+
+ def update_statusbar(self):
+ txt = self._compute_status_label()
+ self._status_label.setText(txt)
+
+ def progress(self):
+ self._p += 1
+ self._progress.setValue(self._p)
+ self._table.update()
+
+ def start_compute(self):
+ self._p = 0
+ self._progress.setValue(self._p)
+
+ def info_compute(self, str):
+ self.statusbar.showMessage(str, 3000)
+
+ def end_compute(self):
+ self._table.layoutChanged.emit()
+ self.find(QPushButton, "pushButton_retry").setEnabled(True)
+
+ errors = any(filter(lambda c: c.is_error(), self._checker_list))
+ if not errors:
+ self.find(QPushButton, "pushButton_ok").setEnabled(True)
+ if self._autorun:
+ self._parent.solver_log_adists(self._solver, self._mage_rep)
+ self.end()
+
+ self.update_statusbar()
+
+ def update(self, key: str):
+ if key == "start":
+ self.start_compute()
+ self.info_compute("Starting ...")
+ elif key == "end":
+ self.info_compute("Finish")
+ self.end_compute()
+ elif key == "progress":
+ self.progress()
+ else:
+ self.info_compute(key)
+
+ self.update_statusbar()
+
+ def end(self):
+ # self._worker.join()b
+ self.close()
+
+ def reject(self):
+ self.end()
+
+ def accept(self):
+ self._parent.solver_log_adists(self._solver, self._mage_rep)
+ # self.end()
diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py
index aeaac94c..f7fdcce6 100644
--- a/src/View/MainWindow.py
+++ b/src/View/MainWindow.py
@@ -71,8 +71,9 @@ from View.AdditionalFiles.Window import AddFileListWindow
from View.REPLines.Window import REPLineListWindow
from View.SolverParameters.Window import SolverParametersWindow
from View.RunSolver.Window import SelectSolverWindow, SolverLogWindow
-from View.RunSolver.WindowAdisTS import SelectSolverWindowAdisTS
+from View.RunSolver.WindowAdisTS import SelectSolverWindowAdisTS, SolverLogWindowAdisTS
from View.CheckList.Window import CheckListWindow
+from View.CheckList.WindowAdisTS import CheckListWindowAdisTS
from View.Results.Window import ResultsWindow
from View.Results.ReadingResultsDialog import ReadingResultsDialog
from View.Debug.Window import ReplWindow
@@ -242,7 +243,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
"action_menu_dif": self.open_dif,
"action_menu_d90": self.open_d90,
"action_menu_pollutants": self.open_pollutants,
- "action_menu_run_adists":self.run_solver_adists,
+ "action_menu_run_adists":self.select_run_solver_adists,
"action_menu_output_kp": self.open_output_kp_adists,
"action_menu_config": self.open_configure,
"action_menu_new": self.open_new_study,
@@ -1258,7 +1259,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
if run.exec():
self.run_solver(run.solver)
- def run_solver_adists(self):
+ def select_run_solver_adists(self):
if self._study is None:
return
@@ -1273,7 +1274,31 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
parent=self
)
if run.exec():
- self.run_solver(run.solver)
+ self.run_solver_adists(run.solver, run.mage_rep)
+
+ def run_solver_adists(self, solver, mage_rep):
+ if self._study is None:
+ return
+
+ if self.sub_window_exists(
+ CheckListWindowAdisTS,
+ data=[
+ self._study,
+ self.conf,
+ solver,
+ mage_rep
+ ]
+ ):
+ return
+
+ check = CheckListWindowAdisTS(
+ study=self._study,
+ config=self.conf,
+ solver=solver,
+ parent=self,
+ mage_rep=mage_rep,
+ )
+ check.show()
def run_solver(self, solver):
if self._study is None:
@@ -1297,6 +1322,16 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
)
check.show()
+ def solver_log_adists(self, solver, mage_rep):
+ sol = SolverLogWindowAdisTS(
+ study=self._study,
+ config=self.conf,
+ solver=solver,
+ parent=self,
+ mage_rep=mage_rep,
+ )
+ sol.show()
+
def solver_log(self, solver):
sol = SolverLogWindow(
study=self._study,
diff --git a/src/View/RunSolver/WindowAdisTS.py b/src/View/RunSolver/WindowAdisTS.py
index fd10c6d4..52e31dd5 100644
--- a/src/View/RunSolver/WindowAdisTS.py
+++ b/src/View/RunSolver/WindowAdisTS.py
@@ -58,7 +58,7 @@ logger = logging.getLogger()
class SelectSolverWindowAdisTS(PamhyrDialog):
- _pamhyr_ui = "SelectSolver"
+ _pamhyr_ui = "SelectSolverAdisTS"
_pamhyr_name = "Select solver"
def __init__(self, study=None, config=None,
@@ -89,7 +89,21 @@ class SelectSolverWindowAdisTS(PamhyrDialog):
)
)
+ solvers_mage = list(filter(lambda x: "mage" in x._type, self._config.solvers))
+ solvers_mage_names = list(map(lambda x: x._name, solvers_mage))
+
+ solvers_dir = os.path.join(
+ os.path.dirname(self._study.filename),
+ "_PAMHYR_",
+ self._study.name.replace(" ", "_"),
+ )
+
+ dir_solvers_List = os.listdir(solvers_dir)
+
+ display_mage_names = list(filter(lambda x: x in solvers_mage_names, dir_solvers_List))
+
self.combobox_add_items("comboBox", solvers_name)
+ self.combobox_add_items("comboBoxRepMage", display_mage_names)
def setup_connections(self):
self.find(QPushButton, "pushButton_run").clicked.connect(self.accept)
@@ -120,10 +134,16 @@ class SelectSolverWindowAdisTS(PamhyrDialog):
def solver(self):
return self._solver
+ @property
+ def mage_rep(self):
+ return self._mage_result_rep
+
def accept(self):
solver_name = self.get_combobox_text("comboBox")
solver_name = solver_name.rsplit(" - ", 1)[0]
+ self._mage_result_rep = self.get_combobox_text("comboBoxRepMage")
+
self._config.update_last_solver_used(solver_name)
self._solver = next(
@@ -135,3 +155,273 @@ class SelectSolverWindowAdisTS(PamhyrDialog):
super(SelectSolverWindowAdisTS, self).accept()
+class SolverLogWindowAdisTS(PamhyrWindow):
+ _pamhyr_ui = "SolverLog"
+ _pamhyr_name = "Solver Log"
+
+ def __init__(self, study=None, config=None,
+ solver=None, parent=None, mage_rep=None):
+ self._solver = solver
+ self._results = None
+ self._mage_rep = mage_rep
+
+ name = _translate("Solver", "Select log")
+ super(SolverLogWindowAdisTS, self).__init__(
+ title=name,
+ study=study,
+ config=config,
+ options=[],
+ parent=parent
+ )
+
+ self.setup_action()
+ self.setup_alarm()
+ self.setup_connections()
+ self.setup_workdir()
+ self.setup_process()
+
+ ok = self.export()
+ if ok:
+ self.run()
+ else:
+ self._log(
+ f" *** Failed to export study to {self._solver._type}",
+ color="red"
+ )
+
+ def setup_action(self):
+ self.find(QAction, "action_start").setEnabled(False)
+ if _signal:
+ self.find(QAction, "action_pause").setEnabled(True)
+ else:
+ self.find(QAction, "action_pause").setEnabled(False)
+
+ self.find(QAction, "action_stop").setEnabled(True)
+ self.find(QAction, "action_log_file").setEnabled(False)
+ self.find(QAction, "action_results").setEnabled(False)
+
+ def setup_alarm(self):
+ self._alarm = QTimer()
+
+ def setup_connections(self):
+ self.find(QAction, "action_start").triggered.connect(self.start)
+ self.find(QAction, "action_pause").triggered.connect(self.pause)
+ self.find(QAction, "action_stop").triggered.connect(self.stop)
+ self.find(QAction, "action_log_file").triggered.connect(self.log_file)
+ self.find(QAction, "action_results").triggered.connect(self.results)
+
+ self._alarm.timeout.connect(self.update)
+
+ def setup_workdir(self):
+ self._workdir = ""
+ if self._study.filename == "":
+ self._workdir = tempfile.TemporaryDirectory()
+ else:
+ self._workdir = os.path.join(
+ os.path.dirname(self._study.filename),
+ "_PAMHYR_",
+ self._study.name.replace(" ", "_"),
+ self._solver.name.replace(" ", "_"),
+ )
+ os.makedirs(self._workdir, exist_ok=True)
+
+ def setup_process(self):
+ self._alarm.start(100)
+ self._output = Queue()
+ self._process = self.new_process(self._parent)
+
+ def new_process(self, parent):
+ new = QProcess(parent)
+ new.setWorkingDirectory(self._workdir)
+ new.setProcessChannelMode(QProcess.MergedChannels)
+ return new
+
+ def export(self):
+ self._log(f" *** Export study {self._solver.name}", color="blue")
+ ok = self._solver.export(self._study, self._workdir, self._mage_rep, qlog=self._output)
+ self.update()
+
+ return ok
+
+ def closeEvent(self, event):
+ self._alarm.stop()
+ super(SolverLogWindowAdisTS, self).closeEvent(event)
+
+ def _copy(self):
+ self.find(QTextEdit, "textEdit").copy()
+
+ #######
+ # LOG #
+ #######
+
+ def _log(self, msg, color=None):
+ if type(msg) is str:
+ self._log_str(msg, color)
+ elif type(msg) is int:
+ self._log_int(msg, color)
+
+ def _log_str(self, msg, color=None):
+ if msg == "":
+ return
+
+ logger.info(f"solver: {msg}")
+ msg = msg.rsplit('\n', 1)[0]
+
+ if color is not None:
+ msg = f"" + msg + ""
+
+ self.find(QTextEdit, "textEdit").append(msg)
+
+ def _log_int(self, int_code, color=None):
+ logger.info(f"solver: Returns {int_code}")
+ color = "blue" if int_code == 0 else "red"
+
+ self.find(QTextEdit, "textEdit")\
+ .append(
+ f"" +
+ f" *** Finished with code {int_code}" +
+ ""
+ )
+
+ self.statusbar.showMessage(
+ "Done" if int_code == 0 else "Failed",
+ 3000
+ )
+
+ ##########
+ # UPDATE #
+ ##########
+
+ def update(self):
+ if self._solver.is_stoped():
+ self.find(QAction, "action_start").setEnabled(True)
+ self.find(QAction, "action_pause").setEnabled(False)
+ self.find(QAction, "action_stop").setEnabled(False)
+ self.find(QAction, "action_results").setEnabled(True)
+
+ if self._solver.log_file() != "":
+ self.find(QAction, "action_log_file").setEnabled(True)
+
+ self._update_logs_all()
+ # self._update_get_results()
+
+ self._update_logs_all()
+
+ def _update_get_results(self):
+ if self._results is None:
+ def reading_fn():
+ try:
+ self._results = self._solver.results(
+ self._study, self._workdir, qlog=self._output
+ )
+ self._parent.set_results(self._solver, self._results)
+ except Exception as e:
+ logger.error(f"Failed to open results")
+ logger_exception(e)
+
+ dlg = ReadingResultsDialog(reading_fn=reading_fn, parent=self)
+ dlg.exec_()
+
+ def _update_logs_all(self):
+ while self._output.qsize() != 0:
+ s = self._output.get()
+
+ try:
+ if type(s) is str and "[ERROR]" in s:
+ self._log(s.encode("utf-8"), color="red")
+ else:
+ self._log(s)
+ except Exception as e:
+ logger_exception(e)
+
+ ####################
+ # Process controle #
+ ####################
+
+ def run(self):
+ self._log(f" *** Run solver {self._solver.name}", color="blue")
+ self._solver.run(
+ self._study,
+ process=self._process,
+ output_queue=self._output
+ )
+
+ def start(self):
+ if self._solver.is_stoped():
+ self._log(f" *** Export study {self._solver.name}", color="blue")
+
+ ok = self._solver.export(
+ self._study, self._workdir, self._mage_rep, qlog=self._output
+ )
+
+ if not ok:
+ self._log(f" *** Failed to export", color="red")
+ self.update()
+ return
+ else:
+ self.update()
+ self._process = self.new_process(self._parent)
+
+ self._log(" *** Start", color="blue")
+ self._results = None
+ self._solver.start(self._study, process=self._process)
+
+ self.find(QAction, "action_start").setEnabled(False)
+ if _signal:
+ self.find(QAction, "action_pause").setEnabled(True)
+ else:
+ self.find(QAction, "action_pause").setEnabled(False)
+ self.find(QAction, "action_stop").setEnabled(True)
+ self.find(QAction, "action_log_file").setEnabled(False)
+ self.find(QAction, "action_results").setEnabled(False)
+
+ def pause(self):
+ self._log(" *** Pause", color="blue")
+ self._solver.pause()
+
+ self.find(QAction, "action_start").setEnabled(True)
+ self.find(QAction, "action_pause").setEnabled(False)
+ self.find(QAction, "action_stop").setEnabled(True)
+ self.find(QAction, "action_results").setEnabled(False)
+
+ def stop(self):
+ self._log(" *** Stop", color="blue")
+ self._solver.kill()
+
+ self.find(QAction, "action_start").setEnabled(True)
+ self.find(QAction, "action_pause").setEnabled(False)
+ self.find(QAction, "action_stop").setEnabled(False)
+ self.find(QAction, "action_results").setEnabled(True)
+ if self._solver.log_file() != "":
+ self.find(QAction, "action_log_file").setEnabled(True)
+
+ ###########
+ # Results #
+ ###########
+
+ def results(self):
+ if self._results is None:
+ def reading_fn():
+ self._results = self._solver.results(
+ self._study, self._workdir, qlog=self._output
+ )
+
+ dlg = ReadingResultsDialog(reading_fn=reading_fn, parent=self)
+ dlg.exec_()
+
+ self._parent.set_results(self._solver, self._results)
+ self._parent.open_solver_results(self._solver, self._results)
+
+ self._solver.has_results_loaded()
+
+ def log_file(self):
+ file_name = os.path.join(self._workdir, self._solver.log_file())
+ log = SolverLogFileWindow(
+ file_name=file_name,
+ study=self._study,
+ config=self._config,
+ solver=self._solver,
+ parent=self,
+ )
+ log.show()
+
diff --git a/src/View/ui/SelectSolverAdisTS.ui b/src/View/ui/SelectSolverAdisTS.ui
new file mode 100644
index 00000000..a0145865
--- /dev/null
+++ b/src/View/ui/SelectSolverAdisTS.ui
@@ -0,0 +1,65 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 384
+ 107
+
+
+
+ Dialog
+
+
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Run
+
+
+
+ ressources/run.pngressources/run.png
+
+
+
+ -
+
+
+ Cancel
+
+
+
+
+
+ -
+
+
+
+
+
+
+