From dd66faff459f74f9bbe74b322d544cb611de8c70 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Mon, 19 Jun 2023 16:48:38 +0200
Subject: [PATCH] SolverLog: Working solver start.

---
 src/Solver/ASolver.py        | 85 +++++++++++++++++++++++++++++++++---
 src/Solver/Mage.py           |  2 +
 src/View/RunSolver/Window.py | 30 ++++++++++---
 3 files changed, 104 insertions(+), 13 deletions(-)

diff --git a/src/Solver/ASolver.py b/src/Solver/ASolver.py
index ba1e511e..78eb87d5 100644
--- a/src/Solver/ASolver.py
+++ b/src/Solver/ASolver.py
@@ -1,23 +1,28 @@
 # -*- coding: utf-8 -*-
 
 import os
-import subprocess
+
+from signal import SIGTERM, SIGSTOP, SIGCONT
+from subprocess import Popen, PIPE, STDOUT, TimeoutExpired
 from enum import Enum
 
 from Model.Except import NotImplementedMethodeError
 
 class STATUS(Enum):
+    NOT_LAUNCHED = -1
     STOPED = 0
     RUNNING = 1
     FAILED = 2
     CONF_ERROR = 3
+    KILLED = 4
+    PAUSED = 5
 
 class AbstractSolver(object):
     def __init__(self, name):
         super(AbstractSolver, self).__init__()
 
         self._current_process = None
-        self._status = STATUS.STOPED
+        self._status = STATUS.NOT_LAUNCHED
 
         # Informations
         self._type = ""
@@ -32,6 +37,9 @@ class AbstractSolver(object):
         self._cmd_solver = ""
         self._cmd_output = ""
 
+        self._process = None
+        self._output = None
+
     def __str__(self):
         return f"{self._name} : {self._type} : {self._description}"
 
@@ -117,16 +125,79 @@ class AbstractSolver(object):
         if self._cmd_solver == "":
             return True
 
-        return False
+        cmd = self._cmd_solver
+        cmd = cmd.replace("@path", self._path_solver).split()
+
+        exe = cmd[0]
+        args = cmd[1:]
+
+        self._process.start(
+            exe, args,
+        )
+        self._process.readyRead.connect(self._data_ready)
 
-    def run_output_data_fomater(self, ):
+        return True
+
+    def run_output_data_fomater(self):
         if self._cmd_output == "":
             return True
 
         return False
 
+    def _data_ready(self):
+        s = self._process.readAll().data().decode()
+        if self._output is not None:
+            self._output.put(s)
+
+    def run(self, process, output_queue):
+        self._process = process
+        self._output = output_queue
+
+        if self.run_input_data_fomater():
+            if self.run_solver():
+                return self.run_output_data_fomater()
+
     def kill(self):
-        return False
+        if self._process is None:
+            return True
 
-    def readline(self):
-        return ""
+        self._process.kill()
+        self._status = STATUS.KILLED
+        return True
+
+    def start(self):
+        if self._process is None:
+            return False
+
+        if self._status == STATUS.PAUSED:
+            os.kill(self._process.pid(), SIGCONT)
+            self._status = STATUS.RUNNING
+        else:
+            self.run_solver()
+
+        return True
+
+    def pause(self):
+        if self._process is None:
+            return False
+
+        os.kill(self._process.pid(), SIGSTOP)
+        self._status = STATUS.PAUSED
+        return True
+
+    def stop(self):
+        if self._process is None:
+            return False
+
+        self._process.terminate()
+        self._status = STATUS.STOPED
+        return True
+
+
+    def wait(self):
+        if self._process is None:
+            return False
+
+        self._process.wait()
+        self._status = STATUS.STOPED
+        return True
diff --git a/src/Solver/Mage.py b/src/Solver/Mage.py
index e038495f..57e32d61 100644
--- a/src/Solver/Mage.py
+++ b/src/Solver/Mage.py
@@ -12,6 +12,7 @@ class Mage(AbstractSolver):
         self._cmd_solver = "@path @input -o @output"
         self._cmd_output = ""
 
+
     @classmethod
     def default_parameters(cls):
         lst = super(Mage, cls).default_parameters()
@@ -44,6 +45,7 @@ class Mage(AbstractSolver):
 
         return lst
 
+
 class Mage7(Mage):
     def __init__(self, name):
         super(Mage7, self).__init__(name)
diff --git a/src/View/RunSolver/Window.py b/src/View/RunSolver/Window.py
index 5c5c23c3..42d7e464 100644
--- a/src/View/RunSolver/Window.py
+++ b/src/View/RunSolver/Window.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 
+from queue import Queue
 from tools import trace, timer
 
 from View.ASubWindow import ASubWindow, ASubMainWindow
@@ -12,7 +13,7 @@ from PyQt5.QtGui import (
 from PyQt5.QtCore import (
     Qt, QVariant, QAbstractTableModel,
     QCoreApplication, QModelIndex, pyqtSlot,
-    QRect, QTimer,
+    QRect, QTimer, QProcess,
 )
 
 from PyQt5.QtWidgets import (
@@ -20,6 +21,7 @@ from PyQt5.QtWidgets import (
     QFileDialog, QTableView, QAbstractItemView,
     QUndoStack, QShortcut, QAction, QItemDelegate,
     QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
+    QTextEdit,
 )
 
 _translate = QCoreApplication.translate
@@ -51,7 +53,6 @@ class SelectSolverWindow(ASubWindow, ListedSubWindow):
     def setup_connections(self):
         self.find(QPushButton, "pushButton_run").clicked.connect(self.accept)
         self.find(QPushButton, "pushButton_cancel").clicked.connect(self.reject)
-
     @property
     def solver(self):
         return self._solver
@@ -77,6 +78,7 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
         self._study = study
         self._config = config
         self._solver = solver
+        self._process = QProcess(parent)
 
         super(SolverLogWindow, self).__init__(
             name=self._title, ui="SolverLog", parent=parent
@@ -88,6 +90,10 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
         self.setup_connections()
 
         self._alarm.start(500)
+        self._output = Queue()
+
+        self._log(f" *** Run solver {self._solver.name}")
+        self._solver.run(self._process, self._output)
 
     def setup_action(self):
         self.find(QAction, "action_start").setEnabled(False)
@@ -104,26 +110,38 @@ class SolverLogWindow(ASubMainWindow, ListedSubWindow):
 
         self._alarm.timeout.connect(self.update)
 
+    def _log(self, msg):
+        msg = msg.rsplit('\n')[0]
+        self.find(QTextEdit, "textEdit").append(msg)
 
     def update(self):
-        print("update")
+        while self._output.qsize() != 0:
+            s = self._output.get()
+            print(s)
+            self._log(s)
 
     def start(self):
-        print("start")
+        self._log(" *** Start")
+
+        self._solver.start()
 
         self.find(QAction, "action_start").setEnabled(False)
         self.find(QAction, "action_pause").setEnabled(True)
         self.find(QAction, "action_stop").setEnabled(True)
 
     def pause(self):
-        print("pause")
+        self._log(" *** Pause")
+
+        self._solver.pause()
 
         self.find(QAction, "action_start").setEnabled(True)
         self.find(QAction, "action_pause").setEnabled(False)
         self.find(QAction, "action_stop").setEnabled(True)
 
     def stop(self):
-        print("stop")
+        self._log(" *** Stop")
+
+        self._solver.kill()
 
         self.find(QAction, "action_start").setEnabled(True)
         self.find(QAction, "action_pause").setEnabled(False)
-- 
GitLab