|
34 | 34 |
|
35 | 35 | import gtk, gtk.glade, gobject |
36 | 36 | import sys |
| 37 | +import socket |
37 | 38 | import base64 |
38 | 39 | import os, os.path |
39 | 40 | import stat |
@@ -116,6 +117,7 @@ class interface : |
116 | 117 | __ups_rw_vars = None |
117 | 118 | __gui_thread = None |
118 | 119 | __current_ups = None |
| 120 | + __reconnect_attempted = False |
119 | 121 |
|
120 | 122 | def __init__( self ) : |
121 | 123 |
|
@@ -389,8 +391,11 @@ class interface : |
389 | 391 | def quit( self, widget=None ) : |
390 | 392 | # If we are connected to an UPS, disconnect first... |
391 | 393 | if self.__connected : |
392 | | - self.gui_status_message( _("Disconnecting from device") ) |
393 | | - self.disconnect_from_ups() |
| 394 | + try: |
| 395 | + self.gui_status_message( _("Disconnecting from device") ) |
| 396 | + self.disconnect_from_ups() |
| 397 | + except Exception: |
| 398 | + pass |
394 | 399 |
|
395 | 400 | gtk.main_quit() |
396 | 401 |
|
@@ -746,6 +751,7 @@ class interface : |
746 | 751 | # Connect to the selected UPS using parameters (host,port,login,pass) |
747 | 752 | def connect_to_ups( self, widget=None ) : |
748 | 753 |
|
| 754 | + self.__reconnect_attempted = False |
749 | 755 | host = self.__widgets["ups_host_entry"].get_text() |
750 | 756 | port = int( self.__widgets["ups_port_entry"].get_value() ) |
751 | 757 | login = None |
@@ -862,6 +868,7 @@ class gui_updater( threading.Thread ) : |
862 | 868 | __parent_class = None |
863 | 869 | __stop_thread = False |
864 | 870 | was_online = True |
| 871 | + __reconnect_attempted = False |
865 | 872 |
|
866 | 873 | def __init__( self, parent_class ) : |
867 | 874 | threading.Thread.__init__( self ) |
@@ -977,12 +984,91 @@ class gui_updater( threading.Thread ) : |
977 | 984 | # Display UPS status as tooltip for tray icon |
978 | 985 | self.__parent_class._interface__widgets["status_icon"].set_tooltip_markup( status_text ) |
979 | 986 |
|
980 | | - except : |
981 | | - self.__parent_class.gui_status_message( _("Error from '{0}' ({1})").format( ups, sys.exc_info()[1] ) ) |
982 | | - self.__parent_class.gui_status_notification( _("Error from '{0}'\n{1}").format( ups, sys.exc_info()[1] ), "warning.png" ) |
| 987 | + except (socket.error, EOFError, PyNUT.PyNUTError) as e: |
| 988 | + # Connection error detected - attempt automatic reconnection |
| 989 | + if not self.__reconnect_attempted and self.__parent_class._interface__connected: |
| 990 | + self.__reconnect_attempted = True |
| 991 | + self.__parent_class.gui_status_message( _("Connection lost - attempting to reconnect...") ) |
| 992 | + self.__parent_class.gui_status_notification( _("Connection lost"), "warning.png" ) |
| 993 | + |
| 994 | + # Wait for 2 seconds before attempting reconnection |
| 995 | + time.sleep( 2 ) |
| 996 | + self.__attempt_reconnect() |
| 997 | + else: |
| 998 | + self.__parent_class.gui_status_message( _("Error from '{0}' ({1})").format( ups, sys.exc_info()[1] ) ) |
| 999 | + self.__parent_class.gui_status_notification( _("Error from '{0}'\n{1}").format( ups, sys.exc_info()[1] ), "warning.png" ) |
983 | 1000 |
|
984 | 1001 | time.sleep( 1 ) |
985 | 1002 |
|
| 1003 | + def __attempt_reconnect( self ) : |
| 1004 | + """Attempt to reconnect to the UPS after a connection loss.""" |
| 1005 | + if self.__stop_thread: |
| 1006 | + return |
| 1007 | + |
| 1008 | + try: |
| 1009 | + # Get connection parameters from the parent class |
| 1010 | + host = self.__parent_class._interface__widgets["ups_host_entry"].get_text() |
| 1011 | + port = int(self.__parent_class._interface__widgets["ups_port_entry"].get_value()) |
| 1012 | + login = None |
| 1013 | + password = None |
| 1014 | + |
| 1015 | + if self.__parent_class._interface__widgets["ups_authentication_check"].get_active(): |
| 1016 | + login = self.__parent_class._interface__widgets["ups_authentication_login"].get_text() |
| 1017 | + password = self.__parent_class._interface__widgets["ups_authentication_password"].get_text() |
| 1018 | + |
| 1019 | + # Teardown old connection handler properly |
| 1020 | + old_handler = self.__parent_class._interface__ups_handler |
| 1021 | + if old_handler is not None: |
| 1022 | + try: |
| 1023 | + # Close the underlying telnet connection if it exists |
| 1024 | + if hasattr(old_handler, '_PyNUTClient__tn') and old_handler._PyNUTClient__tn is not None: |
| 1025 | + old_handler._PyNUTClient__tn.close() |
| 1026 | + elif hasattr(old_handler, 'tn') and old_handler.tn is not None: |
| 1027 | + old_handler.tn.close() |
| 1028 | + except Exception: |
| 1029 | + pass # Best effort cleanup |
| 1030 | + del old_handler |
| 1031 | + |
| 1032 | + # Create a new connection handler |
| 1033 | + ups_handler = PyNUT.PyNUTClient(host=host, port=port, login=login, password=password) |
| 1034 | + |
| 1035 | + # Verify the UPS is available |
| 1036 | + ups = self.__parent_class._interface__current_ups |
| 1037 | + srv_upses = ups_handler.GetUPSList() |
| 1038 | + |
| 1039 | + if ups not in srv_upses: |
| 1040 | + self.__parent_class.gui_status_message( _("UPS '{0}' not found after reconnection").format(ups) ) |
| 1041 | + self.__reconnect_attempted = False |
| 1042 | + return |
| 1043 | + |
| 1044 | + if not ups_handler.CheckUPSAvailable(ups): |
| 1045 | + self.__parent_class.gui_status_message( _("UPS '{0}' is still not reachable").format(ups) ) |
| 1046 | + self.__reconnect_attempted = False |
| 1047 | + return |
| 1048 | + |
| 1049 | + # Connection successful - update the parent class |
| 1050 | + self.__parent_class._interface__ups_handler = ups_handler |
| 1051 | + self.__parent_class._interface__connected = True |
| 1052 | + self.__reconnect_attempted = False |
| 1053 | + |
| 1054 | + # Refresh UPS data |
| 1055 | + commands = ups_handler.GetUPSCommands(ups) |
| 1056 | + self.__parent_class._interface__ups_commands = list(commands.keys()) |
| 1057 | + self.__parent_class._interface__ups_commands.sort() |
| 1058 | + |
| 1059 | + self.__parent_class._interface__ups_vars = ups_handler.GetUPSVars(ups) |
| 1060 | + self.__parent_class._interface__ups_rw_vars = ups_handler.GetRWVars(ups) |
| 1061 | + |
| 1062 | + # Update the GUI (must be done on the main thread in GTK) |
| 1063 | + gobject.idle_add( self.__parent_class._interface__gui_update_ups_vars_view ) |
| 1064 | + gobject.idle_add( self.__parent_class.change_status_icon, "on_line", False ) |
| 1065 | + self.__parent_class.gui_status_message( _("Reconnected to '{0}' on {1}").format(ups, host) ) |
| 1066 | + self.__parent_class.gui_status_notification( _("Reconnected to UPS"), "on_line.png" ) |
| 1067 | + |
| 1068 | + except (socket.error, EOFError, PyNUT.PyNUTError) as e: |
| 1069 | + self.__parent_class.gui_status_message( _("Reconnection failed: {0}").format(str(e)) ) |
| 1070 | + self.__reconnect_attempted = False |
| 1071 | + |
986 | 1072 | def stop_thread( self ) : |
987 | 1073 | self.__stop_thread = True |
988 | 1074 |
|
|
0 commit comments