Skip to content

Commit f5b8bad

Browse files
committed
Merge branch 'arduino_manager_refactor'
2 parents 355239b + 5d9509c commit f5b8bad

File tree

10 files changed

+646
-508
lines changed

10 files changed

+646
-508
lines changed

MLC/GUI/Autogenerated/autogenerated.py

Lines changed: 456 additions & 455 deletions
Large diffs are not rendered by default.

MLC/GUI/Autogenerated/mlc_qtcreator/board_config_design.ui

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1191,11 +1191,14 @@
11911191
</sizepolicy>
11921192
</property>
11931193
<property name="minimum">
1194-
<number>2</number>
1194+
<number>1</number>
11951195
</property>
11961196
<property name="maximum">
11971197
<number>255</number>
11981198
</property>
1199+
<property name="value">
1200+
<number>1</number>
1201+
</property>
11991202
</widget>
12001203
</item>
12011204
</layout>

MLC/GUI/Experiment/ArduinoConfigManager/ArduinoBoardManager.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from MLC.arduino import boards
2929
from MLC.arduino.connection.serialconnection import SerialConnection, SerialConnectionConfig
3030
from MLC.arduino.protocol import ProtocolConfig, BuildSerial
31+
from MLC.arduino.connection.base import ConnectionException, ConnectionTimeoutException
3132

3233
from PyQt5.QtWidgets import QMessageBox
3334
from PyQt5.QtCore import QTimer
@@ -150,10 +151,12 @@ def conn_check(self):
150151
arduino_if = BuildSerial(config)
151152
version = arduino_if.get_version()
152153
self.__connection_status.set_ok()
153-
except serial.SerialTimeoutException:
154+
except ConnectionTimeoutException:
154155
self.__connection_status.set_error("Error: connection timeout")
155-
except serial.SerialException:
156+
except ConnectionException:
156157
self.__connection_status.set_error("Error: Board unreachable")
158+
except ValueError, err:
159+
self.__connection_status.set_error("Error: {0}".format(err))
157160

158161
def board_changed(self, new_idx, old_idx):
159162
ret = QMessageBox.Yes
@@ -180,7 +183,7 @@ def board_changed(self, new_idx, old_idx):
180183
def start_bench(self):
181184
try:
182185
self.__setup = self.__setup._replace(connection=self.start_connection(), **self.__main_window.checkout_board_setup())
183-
except serial.SerialException:
186+
except SerialConnectionException:
184187
self.show_error(
185188
"Error", "Connection failure", "Could not start connection to board", QMessageBox.Critical, QMessageBox.Ok)
186189
return

MLC/GUI/Experiment/ExperimentInProgress.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from MLC.Application import MLC_CALLBACKS
3333
from MLC.GUI.Autogenerated.autogenerated import Ui_ExperimentInProgressWindow
3434
from MLC.GUI.Experiment.QtCharts.QtChartWrapper import QtChartWrapper
35+
from MLC.arduino.protocol import ProtocolIOException, ProtocolSetupException
3536
from MLC.Log.log import get_gui_logger
3637

3738
from PyQt5.QtCore import pyqtSignal
@@ -59,6 +60,7 @@ def __init__(self):
5960
self._condition = Condition()
6061
self._experiment_stopped = False
6162
self._experiment_cancelled = False
63+
self._experiment_failure = False
6264

6365
def stop_experiment(self):
6466
self._condition.acquire()
@@ -78,6 +80,12 @@ def cancel_experiment(self):
7880
self._condition.notify()
7981
self._condition.release()
8082

83+
def fail_experiment(self):
84+
self._condition.acquire()
85+
self._experiment_failure = True
86+
self._condition.notify()
87+
self._condition.release()
88+
8189
def wait_if_experiment_stopped(self):
8290
had_to_wait = False
8391
self._condition.acquire()
@@ -94,11 +102,19 @@ def experiment_cancelled(self):
94102
self._condition.release()
95103
return cancelled
96104

105+
def experiment_failure(self):
106+
self._condition.acquire()
107+
failure = self._experiment_failure
108+
self._condition.release()
109+
return failure
110+
97111

98112
class ExperimentInProgressWindow(QMainWindow):
99113
indiv_evaluated = pyqtSignal([int, int, int, float])
100114
new_generation = pyqtSignal()
101115
simulation_finished = pyqtSignal()
116+
board_setup_failure = pyqtSignal([str])
117+
board_io_error = pyqtSignal([str])
102118

103119
def __init__(self, parent, parent_signal, chart_params, from_gen, to_gen):
104120
QMainWindow.__init__(self, parent)
@@ -115,6 +131,8 @@ def __init__(self, parent, parent_signal, chart_params, from_gen, to_gen):
115131
self.indiv_evaluated.connect(self._update_dialog)
116132
self.simulation_finished.connect(self._simulation_finished)
117133
self.new_generation.connect(self._create_new_chart)
134+
self.board_setup_failure.connect(self._board_setup_failure)
135+
self.board_io_error.connect(self._board_io_error)
118136

119137
# Signal to be emited when the experiment finished
120138
self._parent_signal = parent_signal
@@ -172,7 +190,8 @@ def _update_dialog(self, indivs_per_gen_counter, total_indivs_counter, gen_count
172190

173191
def _simulation_finished(self):
174192
logger.debug('{0} [SIM_FINISHED] - Executing _simulation_finished function'.format(self._log_prefix))
175-
self._parent_signal.emit(self._experiment_condition.experiment_cancelled())
193+
self._parent_signal.emit(self._experiment_condition.experiment_cancelled(),
194+
self._experiment_condition.experiment_failure())
176195

177196
def _update_current_gen_experiment(self, indiv_index, cost):
178197
if cost > self._chart_params["max_cost"]:
@@ -227,6 +246,21 @@ def _create_new_chart(self):
227246
picture_layout.addWidget(indiv_canvas)
228247
self._indiv_chart = indiv_chart
229248

249+
def _board_setup_failure(self, error):
250+
logger.debug('{0} [SIM_FINISHED] - Board setup failure: {1}'.format(self._log_prefix, error))
251+
selection = QMessageBox.critical(self, "Board Setup error",
252+
error,
253+
QMessageBox.Ok)
254+
self._experiment_condition.cancel_experiment()
255+
self._simulation_finished()
256+
257+
def _board_io_error(self, error):
258+
logger.debug('{0} [SIM_FINISHED] - Board IO error: {1}'.format(self._log_prefix, error))
259+
selection = QMessageBox.critical(self, "Board IO error",
260+
error,
261+
QMessageBox.Ok)
262+
self._experiment_condition.cancel_experiment()
263+
self._simulation_finished()
230264

231265
class ExperimentInProgress(Thread):
232266

@@ -283,6 +317,16 @@ def run(self):
283317
logger.info('{0} [RUN] - Thread was cancelled by the user'
284318
.format(self._log_prefix))
285319
self._dialog.simulation_finished.emit()
320+
except ProtocolSetupException, err:
321+
logger.info('{0} [RUN] - Error in arduino configuration'
322+
.format(self._log_prefix))
323+
self._experiment_condition.fail_experiment()
324+
self._dialog.board_setup_failure.emit(str(err))
325+
except ProtocolIOException, err:
326+
logger.info('{0} [RUN] - I/O error in arduino connection'
327+
.format(self._log_prefix))
328+
self._experiment_condition.fail_experiment()
329+
self._dialog.board_io_error.emit(srt(err))
286330
except Exception:
287331
logger.info('{0} [RUN] - Unknown Exception catch. Aborting experiment'
288332
.format(self._log_prefix))

MLC/GUI/Experiment/ExperimentWindow.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,16 @@
5555
from PyQt5.QtWidgets import QMessageBox
5656
from PyQt5.QtWidgets import QInputDialog
5757

58-
from MLC.arduino.protocol import ProtocolConfig
58+
from MLC.arduino.protocol import ProtocolConfig, ProtocolSetupException, ProtocolIOException
5959
from MLC.arduino.protocol import ArduinoInterfaceSingleton
60-
from MLC.arduino.connection.serialconnection import SerialConnectionConfig, SerialConnection
60+
from MLC.arduino.connection.serialconnection import SerialConnectionConfig, SerialConnection, ConnectionException
6161
from MLC.arduino.connection.base import BaseConnection
6262

6363
logger = get_gui_logger()
6464

6565

6666
class ExperimentWindow(QMainWindow):
67-
experiment_finished = pyqtSignal([bool])
67+
experiment_finished = pyqtSignal([bool, bool])
6868

6969
MAX_GENERATIONS = 30
7070

@@ -140,6 +140,31 @@ def closeEvent(self, event):
140140
def on_start_button_clicked(self):
141141

142142
logger.debug('[EXPERIMENT {0}] [START_BUTTON] - Executing on_start_button_clicked function'.format(self._experiment_name))
143+
144+
try:
145+
ArduinoInterfaceSingleton.get_instance(protocol_config=self._board_config,
146+
conn_setup=self._serial_conn._asdict())
147+
148+
except (ProtocolSetupException, ConnectionException) as err:
149+
logger.debug('[EXPERIMENT {0}] [BOARD_CONFIG] - '
150+
'Serial port could not be initialized. Error Msg: {1}'
151+
.format(self._experiment_name, err))
152+
selection = QMessageBox.critical(self, "Connection failure",
153+
"The current connection arduino setup failed during initialization "
154+
"(Error: {0}), this may generate an error in your experiment.\n"
155+
"Do you want to continue?".format(err),
156+
QMessageBox.Yes | QMessageBox.No,
157+
QMessageBox.Yes)
158+
if selection == QMessageBox.No:
159+
logger.debug('[EXPERIMENT {0}] [START_BUTTON] - Experiment cancelled by user before start'.format(
160+
self._experiment_name))
161+
QMessageBox.information(self, "Check setup", "Please, open the arduino board configurator in order to "
162+
"analize and resolve the error.", QMessageBox.Ok)
163+
return
164+
except Exception as err:
165+
self._report_arduino_unhandled_error(err)
166+
return
167+
143168
from_gen = int(self._autogenerated_object.from_gen_combo.currentText())
144169
to_gen = int(self._autogenerated_object.to_gen_combo.currentText())
145170
number_of_gens = self._mlc_local.get_experiment_info(self._experiment_name)["generations"]
@@ -837,12 +862,12 @@ def _ask_if_experiment_config_must_be_saved(self):
837862

838863
self._autogenerated_object.save_config_button.setDisabled(True)
839864

840-
def _update_experiment(self, cancelled):
865+
def _update_experiment(self, cancelled, failed):
841866
self._progress_dialog.close_window()
842-
if not cancelled:
867+
if not cancelled and not failed:
843868
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
844869
'Experiment was succesfully executed.', QMessageBox.Ok)
845-
else:
870+
if cancelled and not failed:
846871
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
847872
'Experiment was cancelled by the user.', QMessageBox.Ok)
848873

@@ -871,7 +896,7 @@ def _store_board_configuration(self, board_config, serial_conn):
871896
# Update board configuration in the DB
872897
self._mlc_local.save_board_configuration(self._experiment_name, self._board_config, self._serial_conn)
873898

874-
except Exception, err:
899+
except (ProtocolSetupException, ConnectionException) as err:
875900
logger.debug('[EXPERIMENT {0}] [BOARD_CONFIG] - '
876901
'Serial port could not be initialized. Error Msg: {1}'
877902
.format(self._experiment_name, err))
@@ -888,3 +913,13 @@ def _store_board_configuration(self, board_config, serial_conn):
888913
self._mlc_local.save_board_configuration(self._experiment_name,
889914
self._board_config,
890915
self._serial_conn)
916+
917+
except Exception as err:
918+
self._report_arduino_unhandled_error(err)
919+
return
920+
921+
def _report_arduino_unhandled_error(self, error):
922+
logger.debug('[EXPERIMENT {0}] [BOARD_CONFIG] - '
923+
'Unhandled error. Error Msg: {1}'
924+
.format(self._experiment_name, error))
925+
QMessageBox.critical(self, "Critical error", "Error: {0}".format(error))

MLC/arduino/Firmware/Firmware.ino

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ void loop() {
1717
controller.handle_commands();
1818

1919
/**
20-
*
21-
*
2220
* HERE the user can insert any command
2321
*/
2422

MLC/arduino/connection/base.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
# You should have received a copy of the GNU General Public License
2020
# along with this program. If not, see <http://www.gnu.org/licenses/>
2121

22+
class ConnectionException(Exception):
23+
pass
24+
25+
class ConnectionTimeoutException(ConnectionException):
26+
def __init__(self, what):
27+
ConnectionException.__init__("Connection timeout: %s" % (what))
2228

2329
class BaseConnection:
2430
'''

MLC/arduino/connection/serialconnection.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@
1919
# You should have received a copy of the GNU General Public License
2020
# along with this program. If not, see <http://www.gnu.org/licenses/>
2121

22-
from base import BaseConnection
22+
from base import BaseConnection, ConnectionException, ConnectionTimeoutException
2323
from collections import namedtuple
2424
import serial
2525

26+
class SerialConnectionException(ConnectionException):
27+
def __init__(self, what):
28+
ConnectionException.__init__(self, "Error in connection initialization. {0}".format(what))
2629

2730
class SerialConnection(BaseConnection):
2831

@@ -35,9 +38,13 @@ def __init__(self, **args):
3538
parity -- parity check bits from pySerial. By default it is set to PARITY_ONE. Available options: PARITY_EVEN, PARITY_ODD, PARITY_MARK & PARITY_SPACE
3639
stopbits -- stop bits from pySerial. By default it is set to STOPBITS_ONE. Available options: STOPBITS_ONE_POINT_FIVE & STOPBITS_TWO
3740
bytesize -- byte size from pySerial. By default it is set to EIGHTBITS. Available options: FIVEBITS, SIXBITS & SEVENBITS
41+
42+
Raises:
43+
SerialConnectionException: If the port could not be open or configured
44+
ValueError: In case that port is not specified or if any of the parameters have errors
3845
"""
3946
if "port" not in args.keys():
40-
raise Exception("Port is mandatory!")
47+
raise ValueError("Port is mandatory!")
4148

4249
args["baudrate"] = 115200 if "baudrate" not in args.keys() else args["baudrate"]
4350
args["parity"] = serial.PARITY_NONE if "parity" not in args.keys() else args["parity"]
@@ -46,22 +53,43 @@ def __init__(self, **args):
4653
args["timeout"] = 5
4754
args["write_timeout"] = 5
4855

49-
self._connection = serial.Serial(**args)
56+
self._timeout = args["timeout"]
57+
self._write_timeout = args["write_timeout"]
58+
59+
try:
60+
self._connection = serial.Serial(**args)
61+
except serial.SerialException, err:
62+
raise SerialConnectionException(str(err))
5063

5164
def send(self, data):
52-
self._connection.write(data)
65+
"""
66+
Sends data through serial connection
67+
68+
Args:
69+
data: Bytes to be send
70+
71+
Raises:
72+
ConnectionTimeoutException: If could not write all data through serial connection
73+
"""
74+
try:
75+
self._connection.write(data)
76+
except serial.SerialTimeoutException, err:
77+
raise ConnectionTimeoutException("write operation timeout after {0} seconds".format(self._write_timeout))
5378

5479
def recv(self, length):
5580
""" Receives the specified amount of bytes
56-
WARNING: This function is blocked until receive the amount of bytes
81+
WARNING: This function will raise SerialTimeoutException if the recv bytes are not equals to expected bytes
5782
5883
Keyword arguments:
5984
length -- amount of bytes to receive
85+
86+
Raises:
87+
ConnectionTimeoutException: In case that the "length" of bytes is not received
6088
"""
6189
recv = self._connection.read(length)
6290

6391
if len(recv) != length:
64-
raise serial.SerialTimeoutException
92+
raise ConnectionTimeoutException("timeout when receiving expected data")
6593
else:
6694
return recv
6795

0 commit comments

Comments
 (0)