Skip to content

Commit 5d9509c

Browse files
committed
Error handling improvements
* Added specific arduino protocol exception catchs and error report * Now an arduino protocol error during evaluation doesn't break experiment and report to the user the problem * The error text on arduino pin setup shows the human friendly name of the port (when possible)
1 parent 200bfec commit 5d9509c

File tree

5 files changed

+92
-20
lines changed

5 files changed

+92
-20
lines changed

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: 22 additions & 9 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

@@ -145,13 +145,13 @@ def on_start_button_clicked(self):
145145
ArduinoInterfaceSingleton.get_instance(protocol_config=self._board_config,
146146
conn_setup=self._serial_conn._asdict())
147147

148-
except Exception, err:
148+
except (ProtocolSetupException, ConnectionException) as err:
149149
logger.debug('[EXPERIMENT {0}] [BOARD_CONFIG] - '
150150
'Serial port could not be initialized. Error Msg: {1}'
151151
.format(self._experiment_name, err))
152152
selection = QMessageBox.critical(self, "Connection failure",
153153
"The current connection arduino setup failed during initialization "
154-
"(Error: {0}), This may generate an error in your experiment.\n"
154+
"(Error: {0}), this may generate an error in your experiment.\n"
155155
"Do you want to continue?".format(err),
156156
QMessageBox.Yes | QMessageBox.No,
157157
QMessageBox.Yes)
@@ -161,6 +161,9 @@ def on_start_button_clicked(self):
161161
QMessageBox.information(self, "Check setup", "Please, open the arduino board configurator in order to "
162162
"analize and resolve the error.", QMessageBox.Ok)
163163
return
164+
except Exception as err:
165+
self._report_arduino_unhandled_error(err)
166+
return
164167

165168
from_gen = int(self._autogenerated_object.from_gen_combo.currentText())
166169
to_gen = int(self._autogenerated_object.to_gen_combo.currentText())
@@ -859,12 +862,12 @@ def _ask_if_experiment_config_must_be_saved(self):
859862

860863
self._autogenerated_object.save_config_button.setDisabled(True)
861864

862-
def _update_experiment(self, cancelled):
865+
def _update_experiment(self, cancelled, failed):
863866
self._progress_dialog.close_window()
864-
if not cancelled:
867+
if not cancelled and not failed:
865868
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
866869
'Experiment was succesfully executed.', QMessageBox.Ok)
867-
else:
870+
if cancelled and not failed:
868871
QMessageBox.information(self, 'Experiment {0}'.format(self._experiment_name),
869872
'Experiment was cancelled by the user.', QMessageBox.Ok)
870873

@@ -893,7 +896,7 @@ def _store_board_configuration(self, board_config, serial_conn):
893896
# Update board configuration in the DB
894897
self._mlc_local.save_board_configuration(self._experiment_name, self._board_config, self._serial_conn)
895898

896-
except Exception, err:
899+
except (ProtocolSetupException, ConnectionException) as err:
897900
logger.debug('[EXPERIMENT {0}] [BOARD_CONFIG] - '
898901
'Serial port could not be initialized. Error Msg: {1}'
899902
.format(self._experiment_name, err))
@@ -910,3 +913,13 @@ def _store_board_configuration(self, board_config, serial_conn):
910913
self._mlc_local.save_board_configuration(self._experiment_name,
911914
self._board_config,
912915
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/connection/serialconnection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
class SerialConnectionException(ConnectionException):
2727
def __init__(self, what):
28-
ConnectionException.__init__("Error in connection initialization. {0}".format(what))
28+
ConnectionException.__init__(self, "Error in connection initialization. {0}".format(what))
2929

3030
class SerialConnection(BaseConnection):
3131

@@ -58,7 +58,7 @@ def __init__(self, **args):
5858

5959
try:
6060
self._connection = serial.Serial(**args)
61-
except SerialException, err:
61+
except serial.SerialException, err:
6262
raise SerialConnectionException(str(err))
6363

6464
def send(self, data):

MLC/arduino/protocol.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import collections
2323
import boards
2424
from collections import namedtuple
25+
from connection.base import ConnectionException
2526
import MLC.Log.log as lg
2627

2728
_PROTOCOL_CMDS = {"ANALOG_PRECISION": '\x01\x00\x00\x00\x01%s',
@@ -72,7 +73,7 @@ def get_instance(protocol_config=None, conn_setup=None):
7273
ArduinoInterfaceSingleton._instance = BuildSerial(protocol_config)
7374

7475
if ArduinoInterfaceSingleton._instance is None:
75-
raise ValueError("ArduinoInterface was not configured.")
76+
raise ProtocolSetupException("The arduino interface cannot be used if it isn't configured.")
7677

7778
return ArduinoInterfaceSingleton._instance
7879

@@ -118,7 +119,7 @@ def get_version(self):
118119

119120
def set_pwm(self, pin, duty_cicle):
120121
if port in self._anlg_inputs or port in self._digital_inputs:
121-
raise ProtocolSetupException("Port %s is configured as input!" % port)
122+
raise ProtocolSetupException("Port %s is configured as input!" % self.__get_arduino_pin(port))
122123

123124
self._connection.send(_PROTOCOL_CMDS["ANALAOG_WRITE"] % (
124125
chr(pin), chr((duty_cicle & 0xFF00) >> 8), chr(duty_cicle & 0x00FF)))
@@ -153,7 +154,7 @@ def set_report_mode(self, mode, read_count=1, read_delay=0):
153154

154155
def add_input(self, port):
155156
if port in self._anlg_outputs or port in self._digital_outputs:
156-
raise ProtocolSetupException("Pin %s is configured as output!" % port)
157+
raise ProtocolSetupException("Pin %s is configured as output!" % self.__get_arduino_pin(port))
157158

158159
self.__validate_pin(port)
159160

@@ -171,7 +172,7 @@ def add_input(self, port):
171172

172173
def add_output(self, port):
173174
if port in self._anlg_inputs or port in self._digital_inputs:
174-
raise ProtocolSetupException("Port %s is configured as input!" % port)
175+
raise ProtocolSetupException("Pin %s is configured as input!" % self.__get_arduino_pin(port))
175176

176177
self.__validate_pin(port)
177178

@@ -191,6 +192,16 @@ def __validate_pin(self, pin):
191192
raise ValueError("Invalid pin %s for board %s" %
192193
(pin, self._board["NAME"]))
193194

195+
def __get_arduino_pin_id(self, pin):
196+
ret = pin
197+
if pin in self._board["DIGITAL_PINS"]:
198+
ret = "D{0}".format(pin)
199+
200+
if pin in self._board["ANALOG_PINS"]:
201+
ret = "A{0}".format(pin - len(self._board["DIGITAL_PINS"]))
202+
203+
return ret
204+
194205
def reset(self):
195206
self._connection.send(_PROTOCOL_CMDS["RESET"])
196207
self._anlg_outputs = []
@@ -210,7 +221,8 @@ def actuate(self, data):
210221
# Sets as payload every digital or analog port
211222
for i in data:
212223
if i[0] not in self._anlg_outputs and i[0] not in self._digital_outputs:
213-
raise ProtocolSetupException("Port %s not configured as output!" % i[0])
224+
pin = self.__get_arduino_pin_id(i[0])
225+
raise ProtocolSetupException("Port %s not configured as output!" % pin)
214226
if i[0] in self._anlg_outputs:
215227
payload = "".join(
216228
[payload, chr(i[0]), chr((i[1] & 0xFF00) >> 8), chr(i[1] & 0x00FF)])

0 commit comments

Comments
 (0)