Skip to content

Commit 200bfec

Browse files
committed
ArduinoManager GUI update & specific protocol/connection exception
* Now the ArduinoManager windows allows to setup one reading for every pin * Added specific protocol and connection expcetion for better error handling * Forced a arduino protocol setup just before experiment start in order to check the board status and refresh the firmware setup on every experiment run.
1 parent ccfd670 commit 200bfec

File tree

8 files changed

+563
-497
lines changed

8 files changed

+563
-497
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/ExperimentWindow.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,28 @@ 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 Exception, 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+
143165
from_gen = int(self._autogenerated_object.from_gen_combo.currentText())
144166
to_gen = int(self._autogenerated_object.to_gen_combo.currentText())
145167
number_of_gens = self._mlc_local.get_experiment_info(self._experiment_name)["generations"]

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__("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 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

MLC/arduino/protocol.py

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@
4141
PIN_MODES = collections.namedtuple(
4242
'PIN_MODES', ['INPUT', 'OUTPUT'], verbose=False)(INPUT=0, OUTPUT=1)
4343

44+
class ProtocolException(Exception):
45+
pass
46+
47+
class ProtocolIOException(ProtocolException):
48+
def __init__(self,what):
49+
ProtocolException.__init__(self, "Protocol IO error: %s" % (what))
50+
51+
class ProtocolSetupException(ProtocolException):
52+
def __init__(self, what):
53+
ProtocolException.__init__(self, "Setup error: %s" % (what))
4454

4555
class ArduinoInterfaceSingleton():
4656
_instance = None
@@ -53,16 +63,16 @@ def get_instance(protocol_config=None, conn_setup=None):
5363
serial_conn = None
5464
try:
5565
serial_conn = SerialConnection(**conn_setup)
56-
except Exception, err:
66+
except ConnectionException, err:
5767
lg.logger_.info("[PROTOCOL] Error while loading SerialConnection. "
58-
"Err msg: {0}".format(err))
68+
"Err info: {0}".format(err))
5969
raise
6070

6171
protocol_config = protocol_config._replace(connection=serial_conn)
6272
ArduinoInterfaceSingleton._instance = BuildSerial(protocol_config)
6373

6474
if ArduinoInterfaceSingleton._instance is None:
65-
raise Exception("ArduinoInterface was not configured.")
75+
raise ValueError("ArduinoInterface was not configured.")
6676

6777
return ArduinoInterfaceSingleton._instance
6878

@@ -108,31 +118,31 @@ def get_version(self):
108118

109119
def set_pwm(self, pin, duty_cicle):
110120
if port in self._anlg_inputs or port in self._digital_inputs:
111-
raise Exception("Port %s is configured as input!" % port)
121+
raise ProtocolSetupException("Port %s is configured as input!" % port)
112122

113123
self._connection.send(_PROTOCOL_CMDS["ANALAOG_WRITE"] % (
114124
chr(pin), chr((duty_cicle & 0xFF00) >> 8), chr(duty_cicle & 0x00FF)))
115125

116126
def set_precision(self, bits):
117127
if bits > 32 or bits < 1:
118-
raise Exception("Precision bits must be between 1 and 32!")
128+
raise ValueError("Precision bits must be between 1 and 32!")
119129
self._connection.send(_PROTOCOL_CMDS["ANALOG_PRECISION"] % chr(bits))
120130
self._anlg_precition = bits
121131

122132
def __set_pin_mode(self, port, mode):
123133
if mode not in PIN_MODES._asdict().values():
124-
raise Exception("Pind mode error. Unknown mode: %s. Modes availables: %s " %
134+
raise ValueError("Pind mode error. Unknown mode: %s. Modes availables: %s " %
125135
(mode, str(PIN_MODES_asdict().keys())))
126136

127137
self._connection.send(
128138
_PROTOCOL_CMDS["PIN_MODE"] % (chr(port), chr(mode)))
129139

130140
def set_report_mode(self, mode, read_count=1, read_delay=0):
131141
if mode not in REPORT_MODES._asdict().values():
132-
raise Exception("Report mode error. Unknown value: %s" % mode)
142+
raise ValueError("Report mode error. Unknown value: %s" % mode)
133143

134144
if read_count <= 0:
135-
raise Exception("Read count value must be > 0")
145+
raise ValueError("Read count value must be > 0")
136146

137147
self._report_mode = mode
138148
self._read_count = read_count - 1
@@ -143,7 +153,7 @@ def set_report_mode(self, mode, read_count=1, read_delay=0):
143153

144154
def add_input(self, port):
145155
if port in self._anlg_outputs or port in self._digital_outputs:
146-
raise Exception("Pin %s is configured as output!" % port)
156+
raise ProtocolSetupException("Pin %s is configured as output!" % port)
147157

148158
self.__validate_pin(port)
149159

@@ -161,7 +171,7 @@ def add_input(self, port):
161171

162172
def add_output(self, port):
163173
if port in self._anlg_inputs or port in self._digital_inputs:
164-
raise Exception("Port %s is configured as input!" % port)
174+
raise ProtocolSetupException("Port %s is configured as input!" % port)
165175

166176
self.__validate_pin(port)
167177

@@ -178,7 +188,7 @@ def add_output(self, port):
178188

179189
def __validate_pin(self, pin):
180190
if pin not in self._board["DIGITAL_PINS"] and pin not in self._board["ANALOG_PINS"]:
181-
raise Exception("Invalid pin %s for board %s" %
191+
raise ValueError("Invalid pin %s for board %s" %
182192
(pin, self._board["NAME"]))
183193

184194
def reset(self):
@@ -200,7 +210,7 @@ def actuate(self, data):
200210
# Sets as payload every digital or analog port
201211
for i in data:
202212
if i[0] not in self._anlg_outputs and i[0] not in self._digital_outputs:
203-
raise Exception("Port %s not configured as output!" % i[0])
213+
raise ProtocolSetupException("Port %s not configured as output!" % i[0])
204214
if i[0] in self._anlg_outputs:
205215
payload = "".join(
206216
[payload, chr(i[0]), chr((i[1] & 0xFF00) >> 8), chr(i[1] & 0x00FF)])
@@ -212,20 +222,21 @@ def actuate(self, data):
212222
self._connection.send(
213223
"".join([_PROTOCOL_CMDS["ACTUATE"], chr((size & 0xFF000000) >> 24), chr((size & 0x00FF0000) >> 16),
214224
chr((size & 0x0000FF00) >> 8), chr(size & 0x000000FF), payload]))
215-
225+
#FIXME catch connection exceptions
216226
response = self._connection.recv(1)
217227

218228
if response == _PROTOCOL_CMDS["ACK"]:
219229
response = self._connection.recv(4) # Clears buffer
220-
raise Exception("Actuate error. Code: %s" % response)
230+
raise ProtocolIOException("Actuate error. Code: %s" % response)
221231

222232
if response != _PROTOCOL_CMDS["ACTUATE_REPORT"]:
223233
response = self._connection.recv(4)
224-
raise Exception(
234+
raise ProtocolIOException(
225235
"Actuate error. Unknown response %s after actuate operation" % ord(response))
226-
236+
# FIXME catch connection exceptions
227237
raw_len = self._connection.recv(4)
228238
length = (ord(raw_len[0]) << 24) + (ord(raw_len[1]) << 16) + (ord(raw_len[2]) << 8) + ord(raw_len[3])
239+
# FIXME catch connection exceptions
229240
data = self._connection.recv(length)
230241

231242
pos = 0
@@ -261,8 +272,8 @@ def actuate(self, data):
261272
(sum(results["D%d" % (pin)]) * 2) > (self._read_count + 1)]
262273

263274
else:
264-
raise Exception(
265-
"Unknown port \"%d\" in response. Restart Arduino board, your software and pray" % pin)
275+
raise ProtocolIOException(
276+
"Unknown port \"%d\" in response. Please, restart the Arduino board and the experiment." % pin)
266277

267278
return results
268279

@@ -287,11 +298,9 @@ def __new__(cls, connection, report_mode=REPORT_MODES.AVERAGE, read_count=2, rea
287298

288299

289300
def BuildSerial(protocol_config):
290-
interface = ArduinoInterface(
291-
protocol_config.connection, protocol_config.board_type)
301+
interface = ArduinoInterface(protocol_config.connection, protocol_config.board_type)
292302
interface.reset()
293-
interface.set_report_mode(
294-
protocol_config.report_mode, protocol_config.read_count, protocol_config.read_delay)
303+
interface.set_report_mode(protocol_config.report_mode, protocol_config.read_count, protocol_config.read_delay)
295304

296305
interface.set_precision(protocol_config.analog_resolution)
297306

@@ -307,7 +316,6 @@ def BuildSerial(protocol_config):
307316
for port in protocol_config.analog_output_pins:
308317
interface.add_output(port)
309318

310-
# FIXME: Make this variable configurable
311-
interface.set_precision(12)
319+
interface.set_precision(protocol_config.analog_resolution)
312320

313321
return interface

tests/mlc/arduino_protocol/test_protocol.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import unittest
2323
from MLC.arduino.connection import MockConnection
24-
from MLC.arduino.protocol import ArduinoInterface
24+
from MLC.arduino.protocol import ArduinoInterface, ProtocolSetupException, ProtocolIOException
2525
from MLC.arduino.protocol import REPORT_MODES
2626
from MLC.arduino import boards
2727

@@ -41,8 +41,8 @@ def test_set_precision(self):
4141
self._interface.set_precision(12)
4242
data = self._connection.pop_data()
4343
self.assertEqual("\x01\x00\x00\x00\x01\x0C", data)
44-
with self.assertRaises(Exception):
45-
self._interface.set_precition(33)
44+
with self.assertRaises(ValueError):
45+
self._interface.set_precision(33)
4646

4747
def test_report_mode(self):
4848
self._interface.set_report_mode(REPORT_MODES.AVERAGE)
@@ -51,7 +51,7 @@ def test_report_mode(self):
5151
self._interface.set_report_mode(REPORT_MODES.AVERAGE, read_count=10, read_delay=5)
5252
data = self._connection.pop_data()
5353
self.assertEqual("\x05\x00\x00\x00\x03\x00\x09\x05", data)
54-
with self.assertRaises(Exception):
54+
with self.assertRaises(ValueError):
5555
self._interface.set_report_mode(12334) # Invalid report mode
5656

5757
def test_add_input(self):
@@ -63,7 +63,7 @@ def test_add_input(self):
6363
self._interface.add_input(60)
6464
with self.assertRaises(Exception): # Checks that no data has been sent
6565
data = self._connection.pop_data()
66-
with self.assertRaises(Exception): # Checks that no data has been sent
66+
with self.assertRaises(ValueError): # Checks that no data has been sent
6767
self._interface.add_output(128)
6868

6969
def test_add_output(self):
@@ -75,18 +75,18 @@ def test_add_output(self):
7575
self._interface.add_output(60)
7676
with self.assertRaises(Exception): # Checks that no data has been sent
7777
data = self._connection.pop_data()
78-
with self.assertRaises(Exception): # Checks that no data has been sent
78+
with self.assertRaises(ValueError): # Checks that no data has been sent
7979
self._interface.add_output(128)
8080

8181
def test_error_adding_output_that_is_input(self):
8282
self._interface.add_input(60)
83-
with self.assertRaises(Exception):
84-
self._interface.add_analog_output(60)
83+
with self.assertRaises(ProtocolSetupException):
84+
self._interface.add_output(60)
8585

8686
def test_actuate_error_response(self):
8787
self._interface.add_output(60)
8888
self._connection.pop_data()
89-
with self.assertRaises(Exception):
89+
with self.assertRaises(ProtocolIOException):
9090
self._interface.actuate([(60, 128)])
9191

9292
def test_actuate_with_one_read(self):

0 commit comments

Comments
 (0)