Skip to content

Commit

Permalink
Added more detailed descriptions for some errors
Browse files Browse the repository at this point in the history
theonlydvr committed Oct 3, 2024
1 parent 15a1f6e commit 8d25862
Showing 7 changed files with 75 additions and 35 deletions.
18 changes: 11 additions & 7 deletions pybehave/Sources/SerialSource.py
Original file line number Diff line number Diff line change
@@ -34,14 +34,18 @@ def register_component(self, component, metadata):

def read(self, com):
while not self.closing[com]:
data = self.connections[com].read_until(expected='\n', size=None)
if len(data) > 0:
try:
data = self.connections[com].read_until(expected='\n', size=None)
if len(data) > 0:
for comp in self.components.values():
if comp.address == com and (comp.get_type() == Component.Type.DIGITAL_INPUT or
comp.get_type() == Component.Type.INPUT or
comp.get_type() == Component.Type.ANALOG_INPUT or
comp.get_type() == Component.Type.BOTH):
self.update_component(comp.id, data)
except SerialException:
for comp in self.components.values():
if comp.address == com and (comp.get_type() == Component.Type.DIGITAL_INPUT or
comp.get_type() == Component.Type.INPUT or
comp.get_type() == Component.Type.ANALOG_INPUT or
comp.get_type() == Component.Type.BOTH):
self.update_component(comp.id, data)
self.component_unavailable(comp)
del self.com_tasks[com]
del self.closing[com]
self.connections[com].close()
9 changes: 7 additions & 2 deletions pybehave/Sources/Source.py
Original file line number Diff line number Diff line change
@@ -58,9 +58,9 @@ def run(self):
if not self.handle_events(events):
return
except pyberror.ComponentRegisterError as e:
self.queue.send_bytes(self.encoder.encode(PybEvents.ErrorEvent(type(e).__name__, traceback.format_exc(), metadata={"sid": self.sid})))
self.queue.send_bytes(self.encoder.encode(PybEvents.ErrorEvent(type(e).__name__, traceback.format_exc(), metadata={"error_type": "Source Non-Fatal", "chamber": e.chamber, "sid": self.sid})))
except BaseException as e:
self.queue.send_bytes(self.encoder.encode(PybEvents.ErrorEvent(type(e).__name__, traceback.format_exc(), metadata={"sid": self.sid})))
self.queue.send_bytes(self.encoder.encode(PybEvents.ErrorEvent(type(e).__name__, traceback.format_exc(), metadata={"error_type": "Source Fatal", "sid": self.sid})))
self.unavailable()
raise

@@ -153,6 +153,11 @@ def unavailable(self):
self.available = False
self.queue.send_bytes(self.encoder.encode(UnavailableSourceEvent(self.sid)))

def component_unavailable(self, comp):
self.queue.send_bytes(self.encoder.encode(
PybEvents.ErrorEvent(pyberror.ComponentUnavailableError.__name__, traceback.format_exc(),
metadata={"error_type": "Source Non-Fatal", "chamber": comp.metadata['chamber'], "cid": comp.id, "sid": self.sid})))

@staticmethod
def metadata_defaults(comp_type: Component.Type = None) -> Dict:
"""Call to get the metadata names and default values required by this source."""
25 changes: 14 additions & 11 deletions pybehave/Tasks/TaskProcess.py
Original file line number Diff line number Diff line change
@@ -108,12 +108,18 @@ def run(self):
logger.log_events(self.logger_q)
self.logger_q.clear()
except BaseException as e:
print(traceback.format_exc())
metadata = {"chamber": event.chamber} if isinstance(event, PybEvents.TaskEvent) else {}
if isinstance(event, PybEvents.StopEvent):
metadata["error_type"] = "Stop"
elif isinstance(event, PybEvents.ClearEvent):
metadata["error_type"] = "Clear"
else:
metadata["error_type"] = "Task"
if isinstance(event, PybEvents.TaskEvent) and self.tasks[event.chamber].started:
self.tp_q.clear()
self.tp_q.append(PybEvents.StopEvent(event.chamber))
self.log_gui_event(PybEvents.ErrorEvent(type(e).__name__, traceback.format_exc(),
metadata=metadata))
if isinstance(event, PybEvents.TaskEvent) and self.tasks[event.chamber].started:
self.tp_q.append(PybEvents.StopEvent(event.chamber))
metadata=metadata))

if len(self.gui_out) > 0:
self.guiq.send_bytes(self.encoder.encode(self.gui_out))
@@ -236,13 +242,7 @@ def init_task(self, event: PybEvents.InitEvent):

def clear_task(self, event: PybEvents.ClearEvent):
task = self.tasks[event.chamber]
if task.dead:
try:
task.clear()
except:
print(traceback.format_exc())
else:
task.clear()
task.clear()
del_loggers = event.del_loggers
if del_loggers:
for logger in self.task_event_loggers[task.metadata["chamber"]].values():
@@ -321,6 +321,9 @@ def error(self, event: PybEvents.ErrorEvent):
# if "sid" in event.metadata and event.metadata["sid"] in self.sourceq:
# del self.sourceq[event.metadata["sid"]]
# del self.source_buffers[event.metadata["sid"]]
if "chamber" in event.metadata and self.tasks[event.metadata["chamber"]].started:
self.tp_q.clear()
self.tp_q.append(PybEvents.StopEvent(event.metadata["chamber"]))
self.mainq.send_bytes(self.encoder.encode(event))

def prepare_exit(self, event: PybEvents.ExitEvent):
14 changes: 10 additions & 4 deletions pybehave/Utilities/Exceptions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class ChamberException(BaseException):
chamber: int
def __init__(self, chamber: int):
self.chamber = chamber


class InvalidComponentTypeError(ChamberException):
@@ -19,16 +20,21 @@ class MalformedAddressFileError(ChamberException):


class SourceUnavailableError(ChamberException):
sid: str
def __init__(self, chamber: int, sid: str):
super().__init__(chamber)
self.sid = sid


class ComponentUnavailableError(ChamberException):
cid: str
def __init__(self, chamber: int, cid: str):
super().__init__(chamber)
self.cid = cid


class AddTaskError(ChamberException):
pass


class MissingExtraError(BaseException):
extra: str
def __init__(self, extra: str):
self.extra = extra
30 changes: 26 additions & 4 deletions pybehave/Workstation/ErrorMessageBox.py
Original file line number Diff line number Diff line change
@@ -11,14 +11,36 @@


class ErrorMessageBox(QMessageBox):
def __init__(self, wsg: WorkstationGUI, message: str):
def __init__(self, wsg: WorkstationGUI, message: str, error_type: str):
super().__init__()
self.wsg = wsg

self.setIcon(QMessageBox.Critical)
self.setText(message)
self.setInformativeText(message)
self.setTextInteractionFlags(Qt.TextBrowserInteraction)
self.setWindowTitle("Error")
if error_type == "GUI":
self.setIcon(QMessageBox.Warning)
self.setWindowTitle("Error in GUI")
self.setText("The task could still be running but the GUI may no longer update properly.")
elif error_type == "Stop":
self.setIcon(QMessageBox.Warning)
self.setWindowTitle("Error While Stopping Task")
self.setText("Some components may not have turned off correctly.")
elif error_type == "Clear":
self.setIcon(QMessageBox.Warning)
self.setWindowTitle("Error While Clearing Task")
self.setText("Some components may not have reset correctly.")
elif error_type == "Source Non-Fatal":
self.setIcon(QMessageBox.Warning)
self.setWindowTitle("Error in Component")
self.setText("A component has experienced an error and the corresponding task will stop but other tasks and components may still be functional.")
elif error_type == "Source Fatal":
self.setIcon(QMessageBox.Critical)
self.setWindowTitle("Error in Source")
self.setText("A fatal exception has occurred in a Source. All tasks using the Source will stop and the Source will have to be reset before continuing.")
else:
self.setIcon(QMessageBox.Critical)
self.setWindowTitle("Error in Task")
self.setText("A fatal exception has occurred while running the task and it has stopped.")
self.ignore_cb = QCheckBox("Ignore future errors")
self.ignore_cb.stateChanged.connect(self.on_ignore_errors_changed)
self.finished.connect(self.on_error_close)
8 changes: 4 additions & 4 deletions pybehave/Workstation/Workstation.py
Original file line number Diff line number Diff line change
@@ -277,7 +277,7 @@ def update_gui(self) -> None:
element.draw()
self.gui_updates.append(element.rect.move(col * self.w, row * self.h))
elif isinstance(event, PybEvents.ErrorEvent):
self.handle_error(event)
self.handle_error(event, error_type=event.metadata['error_type'])
elif isinstance(event, PybEvents.UnavailableSourceEvent):
self.sources[event.sid].available = False
if self.wsg.sd is not None and self.wsg.sd.isVisible():
@@ -293,9 +293,9 @@ def update_gui(self) -> None:
except BaseException as e:
metadata = {"chamber": event.chamber} if isinstance(event, PybEvents.TaskEvent) else {}
tb = traceback.format_exc()
self.handle_error(PybEvents.ErrorEvent(type(e).__name__, tb, metadata=metadata))
self.handle_error(PybEvents.ErrorEvent(type(e).__name__, tb, metadata=metadata), error_type="GUI")

def handle_error(self, event: PybEvents.ErrorEvent):
def handle_error(self, event: PybEvents.ErrorEvent, error_type: str = "Task"):
print(event.traceback)
if 'chamber' in event.metadata:
chamber = event.metadata['chamber']
@@ -321,7 +321,7 @@ def handle_error(self, event: PybEvents.ErrorEvent):
error_message = "Unhandled exception " + chamber_suffix + event.traceback
else:
error_message = f"Unhandled exception in pybehave processing code. <a href='https://github.com/tne-lab/py-behav-box-v2/issues/new?title=Unhandled%20Exception&body={event.traceback}'>Click here</a> to create a GitHub issue<br>" + event.traceback
self.wsg.error.emit(error_message)
self.wsg.error.emit(error_message, error_type)

def fatal_chamber_exception(self, chamber):
col = chamber % self.n_col
6 changes: 3 additions & 3 deletions pybehave/Workstation/WorkstationGUI.py
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@


class WorkstationGUI(QWidget):
error = pyqtSignal(str, name="error_signal")
error = pyqtSignal(str, str, name="error_signal")

def __init__(self, workstation: Workstation):
QWidget.__init__(self)
@@ -172,9 +172,9 @@ def remove_task(self, chamber_index: int) -> None:
del self.chambers[chamber_index - 1]
self.n_active -= 1 # Decrement the number of active tasks

def on_error(self, message):
def on_error(self, message, error_type):
if len(self.emsgs) < 10 and not self.ignore_errors:
self.emsgs.append(ErrorMessageBox(self, message))
self.emsgs.append(ErrorMessageBox(self, message, error_type))
self.emsgs[-1].show()

def confirm_exit(self):

0 comments on commit 8d25862

Please sign in to comment.