From 977a3ef05bab9a25312be59c0af2ac4fa50df752 Mon Sep 17 00:00:00 2001 From: gbakeman Date: Sun, 16 Nov 2025 15:37:52 -0500 Subject: [PATCH 1/2] Socket, connection handling changes Enums: Removed noresponse enum, replaced with empty and unrecognized. Socket: - Modified Disconnect sub to have more concise parameter, remove unnecessary stream disposal - Restructuring of core Query_Data subroutine. Moved or eliminated variable initialization, early sanity check for connection status, removed redundant newline in stream write, process response using .NET functions, move Enum parsing inline to subroutine, early check for null/empty response, distinct handling of good vs. bad results - Removed unused Watchdog code (essentially became null/empty case in Query subroutine) - Removed exception from Socket_Broken parameter UPS: - Stop variable update loop from calling Socket_Broken() - In multi-variable get subroutine, only recurse on VARNOTSUPPORTED or STALE errors, everything else should be thrown. --- .../WinNUT-Client_Common/Common_Enums.vb | 3 +- WinNUT_V2/WinNUT-Client_Common/Nut_Socket.vb | 123 +++++++----------- WinNUT_V2/WinNUT-Client_Common/UPS_Device.vb | 7 +- 3 files changed, 52 insertions(+), 81 deletions(-) diff --git a/WinNUT_V2/WinNUT-Client_Common/Common_Enums.vb b/WinNUT_V2/WinNUT-Client_Common/Common_Enums.vb index 2d6a8e6..abec61b 100644 --- a/WinNUT_V2/WinNUT-Client_Common/Common_Enums.vb +++ b/WinNUT_V2/WinNUT-Client_Common/Common_Enums.vb @@ -61,7 +61,8 @@ End Enum ' Define possible responses according to NUT protcol v1.2 Public Enum NUTResponse - NORESPONSE + EMPTY + UNRECOGNIZED OK VAR ACCESSDENIED diff --git a/WinNUT_V2/WinNUT-Client_Common/Nut_Socket.vb b/WinNUT_V2/WinNUT-Client_Common/Nut_Socket.vb index a8f616f..daf48f5 100644 --- a/WinNUT_V2/WinNUT-Client_Common/Nut_Socket.vb +++ b/WinNUT_V2/WinNUT-Client_Common/Nut_Socket.vb @@ -35,7 +35,7 @@ Public Class Nut_Socket ''' Private streamInUse As Boolean - Public Event Socket_Broken(ex As NutException) + Public Event Socket_Broken() Public Sub New(Nut_Config As Nut_Parameter, ByRef logger As Logger) LogFile = logger @@ -122,9 +122,10 @@ Public Class Nut_Socket ''' ''' Perform various functions necessary to disconnect the socket from the NUT server. ''' - ''' Skip sending the LOGOUT command to the NUT server. Unknown effects. - Public Sub Disconnect(Optional forceful = False) - If IsLoggedIn AndAlso Not forceful Then + ''' Do not send the LOGOUT command to the NUT server. Unknown effects. + Public Sub Disconnect(Optional skipLogout = False) + If IsLoggedIn AndAlso Not skipLogout Then + ' TODO: Move to new subroutine. Query_Data("LOGOUT") End If @@ -138,47 +139,11 @@ Public Class Nut_Socket ReaderStream.Dispose() End If - If NutStream IsNot Nothing Then - NutStream.Dispose() - End If - If client IsNot Nothing Then client.Close() End If End Sub - ''' - ''' Parse and enumerate a NUT protocol response. - ''' - ''' The raw response given from a query. - ''' - Private Function EnumResponse(Data As String) As NUTResponse - Dim Response As NUTResponse - ' Remove hyphens to prepare for parsing. - Dim SanitisedString = UCase(Data.Replace("-", String.Empty)) - ' Break the response down so we can get specifics. - Dim SplitString = SanitisedString.Split(" "c) - - Select Case SplitString(0) - Case "OK", "VAR", "DESC", "UPS" - Response = NUTResponse.OK - Case "BEGIN" - Response = NUTResponse.BEGINLIST - Case "END" - Response = NUTResponse.ENDLIST - Case "ERR" - Response = DirectCast([Enum].Parse(GetType(NUTResponse), SplitString(1)), NUTResponse) - Case "NETWORK", "1.0", "1.1", "1.2", "1.3" - 'In case of "VER" or "NETVER" Query - Response = NUTResponse.OK - Case Else - ' We don't recognize the response, throw an error. - Response = NUTResponse.NORESPONSE - 'Throw New Exception("Unknown response from NUT server: " & Response) - End Select - Return Response - End Function - ''' ''' Attempt to send a query to the NUT server, and do some basic parsing. ''' @@ -188,43 +153,59 @@ Public Class Nut_Socket ''' call is in progress. ''' Thrown when the NUT server returns an error or unexpected response. Function Query_Data(Query_Msg As String) As Transaction - Dim Response As NUTResponse - Dim DataResult As String - Dim finalTransaction As Transaction + If Not ConnectionStatus Then + Throw New InvalidOperationException("Attempted to send query " & Query_Msg & " while disconnected.") + End If If streamInUse Then - Throw New InvalidOperationException("Attempted to query " & Query_Msg & " while stream is in use.") + Throw New InvalidOperationException("Attempted to send query " & Query_Msg & " while stream is in use.") End If - If ConnectionStatus Then + Try streamInUse = True + WriterStream.WriteLine(Query_Msg) + WriterStream.Flush() + Catch + Throw + Finally + streamInUse = False + End Try - Try - WriterStream.WriteLine(Query_Msg & vbCr) - WriterStream.Flush() - Catch - Throw - Finally - streamInUse = False - End Try + Dim responseEnum = NUTResponse.EMPTY + Dim response = ReaderStream.ReadLine() - DataResult = Trim(ReaderStream.ReadLine()) - Response = EnumResponse(DataResult) - finalTransaction = New Transaction(Query_Msg, DataResult, Response) - - ' Handle error conditions - If DataResult = Nothing OrElse DataResult.StartsWith("ERR") Then - ' TODO: Does null dataresult really mean an error condition? - ' https://stackoverflow.com/a/6523010/530172 - 'Disconnect(True, True) - 'RaiseEvent Socket_Broken(New NutException(Query_Msg, Nothing)) - Throw New NutException(finalTransaction) - End If + If String.IsNullOrEmpty(response) Then + ' End of stream reached, likely server terminated connection. + Disconnect(True) + RaiseEvent Socket_Broken() Else - Throw New InvalidOperationException("Attempted to send query while disconnected.") + Dim parseResponse = response.Trim().ToUpper().Split(" "c) ' TODO: Is Trim unnecessary? + + Select Case parseResponse(0) + Case "OK", "VAR", "DESC", "UPS" + responseEnum = NUTResponse.OK + Case "BEGIN" + responseEnum = NUTResponse.BEGINLIST + Case "END" + responseEnum = NUTResponse.ENDLIST + Case "NETWORK", "1.0", "1.1", "1.2", "1.3" + 'In case of "VER" or "NETVER" Query + responseEnum = NUTResponse.OK + Case "ERR" + responseEnum = DirectCast([Enum].Parse(GetType(NUTResponse), + parseResponse(1).Replace("-", String.Empty)), NUTResponse) + Case Else + responseEnum = NUTResponse.UNRECOGNIZED + End Select End If - Return finalTransaction + Dim transaction = New Transaction(Query_Msg, response, responseEnum) + + If responseEnum = NUTResponse.OK OrElse responseEnum = NUTResponse.BEGINLIST OrElse responseEnum = NUTResponse.ENDLIST Then + Return transaction + End If + + Throw New NutException(transaction) End Function Public Function Query_List_Datas(Query_Msg As String) As List(Of UPS_List_Datas) @@ -351,12 +332,4 @@ Public Class Nut_Socket Throw New NutException(Nut_Query) End If End Function - - Private Sub Event_WatchDog(sender As Object, e As EventArgs) - Dim Nut_Query = Query_Data("") - If Nut_Query.ResponseType = NUTResponse.NORESPONSE Then - Disconnect(True) - RaiseEvent Socket_Broken(New NutException(Nut_Query)) - End If - End Sub End Class diff --git a/WinNUT_V2/WinNUT-Client_Common/UPS_Device.vb b/WinNUT_V2/WinNUT-Client_Common/UPS_Device.vb index 64dcf2e..a133eae 100644 --- a/WinNUT_V2/WinNUT-Client_Common/UPS_Device.vb +++ b/WinNUT_V2/WinNUT-Client_Common/UPS_Device.vb @@ -429,7 +429,6 @@ Public Class UPS_Device Catch Excep As Exception LogFile.LogTracing("Something went wrong in Retrieve_UPS_Datas:", LogLvl.LOG_ERROR, Me) LogFile.LogException(Excep, Me) - Socket_Broken() End Try End Sub @@ -481,12 +480,10 @@ Public Class UPS_Device Continue For End If End If - Case Else - LogFile.LogTracing("Error with " & varName & ", trying next", LogLvl.LOG_WARNING, Me) - ' Continue to next variable - Continue For + Throw End Select + Catch ex As Exception LogFile.LogTracing("Exception for variable " & varName & ": " & ex.Message & ", trying next", LogLvl.LOG_WARNING, Me) ' Continue to next variable From 4fd2ceb58cffb9d5c4e9cf36cf05cae7a526b9e5 Mon Sep 17 00:00:00 2001 From: gbakeman Date: Sun, 16 Nov 2025 15:56:30 -0500 Subject: [PATCH 2/2] Update connection menu buttons on lost connection Main menu state was not being updated in a lost connection event handler. --- WinNUT_V2/WinNUT-Client/WinNUT.vb | 1 + 1 file changed, 1 insertion(+) diff --git a/WinNUT_V2/WinNUT-Client/WinNUT.vb b/WinNUT_V2/WinNUT-Client/WinNUT.vb index bdd1e55..deec7d5 100644 --- a/WinNUT_V2/WinNUT-Client/WinNUT.vb +++ b/WinNUT_V2/WinNUT-Client/WinNUT.vb @@ -446,6 +446,7 @@ Public Class WinNUT UpdateIcon_NotifyIcon() LogFile.LogTracing("Update Icon", LogLvl.LOG_DEBUG, Me) RaiseEvent UpdateBatteryState("Lost Connect") + UpdateMainMenuState() End Sub '''