@@ -11,6 +11,15 @@ Imports System.Globalization
1111Imports System.Windows.Forms
1212
1313Public Class UPS_Device
14+ # Region "Statics/Defaults"
15+ Private ReadOnly INVARIANT_CULTURE = CultureInfo.InvariantCulture
16+ Private Const CosPhi As Double = 0.6
17+
18+ ' How many milliseconds to wait before the Reconnect routine tries again.
19+ Private Const DEFAULT_RECONNECT_WAIT_MS As Double = 5000
20+ Private Const DEFAULT_UPDATE_INTERVAL_MS As Double = 1000
21+ # End Region
22+
1423# Region "Properties"
1524
1625 Public ReadOnly Property Name As String
@@ -35,6 +44,10 @@ Public Class UPS_Device
3544 End Get
3645 End Property
3746
47+ ''' <summary>
48+ ''' How often UPS data is updated, in milliseconds.
49+ ''' </summary>
50+ ''' <returns></returns>
3851 Public Property PollingInterval As Integer
3952 Get
4053 Return Update_Data.Interval
@@ -102,16 +115,11 @@ Public Class UPS_Device
102115
103116# End Region
104117
105- Private Const CosPhi As Double = 0.6
106- ' How many milliseconds to wait before the Reconnect routine tries again.
107- Private Const DEFAULT_RECONNECT_WAIT_MS As Double = 5000
108-
109118 Private WithEvents Update_Data As New Timer
110119 Private WithEvents Reconnect_Nut As New Timer
111120 Private WithEvents Nut_Socket As Nut_Socket
112121
113122 Private Freq_Fallback As Double
114- Private ciClone As CultureInfo
115123 Public Nut_Config As Nut_Parameter
116124 Public Retry As Integer = 0
117125 Public MaxRetry As Integer = 30
@@ -121,13 +129,18 @@ Public Class UPS_Device
121129 Me .LogFile = LogFile
122130 Me .Nut_Config = Nut_Config
123131 PollingInterval = pollInterval
124- ciClone = CType (CultureInfo.InvariantCulture.Clone(), CultureInfo)
125- ciClone.NumberFormat.NumberDecimalSeparator = "."
126132 Nut_Socket = New Nut_Socket( Me .Nut_Config, LogFile)
127133
128134 With Reconnect_Nut
129135 .Interval = DEFAULT_RECONNECT_WAIT_MS
130136 .Enabled = False
137+ AddHandler .Tick, AddressOf AttemptReconnect
138+ End With
139+
140+ With Update_Data
141+ .Interval = DEFAULT_UPDATE_INTERVAL_MS
142+ .Enabled = False
143+ AddHandler .Tick, AddressOf Retrieve_UPS_Datas
131144 End With
132145 End Sub
133146
@@ -191,7 +204,7 @@ Public Class UPS_Device
191204 End If
192205 End Sub
193206
194- Private Sub Reconnect_Socket (sender As Object , e As EventArgs) Handles Reconnect_Nut.Tick
207+ Private Sub AttemptReconnect (sender As Object , e As EventArgs)
195208 Retry += 1
196209 If Retry <= MaxRetry Then
197210 RaiseEvent New_Retry()
@@ -216,13 +229,15 @@ Public Class UPS_Device
216229 ''' </summary>
217230 ''' <returns></returns>
218231 Private Function GetUPSProductInfo() As UPSData
232+ LogFile.LogTracing( "Retrieving basic UPS product information..." , LogLvl.LOG_NOTICE, Me )
233+
219234 Dim freshData = New UPSData(
220235 Trim(GetUPSVar( "ups.mfr" , "Unknown" )),
221236 Trim(GetUPSVar( "ups.model" , "Unknown" )),
222237 Trim(GetUPSVar( "ups.serial" , "Unknown" )),
223238 Trim(GetUPSVar( "ups.firmware" , "Unknown" )))
224239
225- ' Determine available power & load data
240+ LogFile.LogTracing( "Determining best method to calculate power usage..." , LogLvl.LOG_NOTICE, Me )
226241 Try
227242 GetUPSVar( "ups.realpower" )
228243 _PowerCalculationMethod = PowerMethod.RealPower
@@ -248,45 +263,74 @@ Public Class UPS_Device
248263 End Try
249264
250265 ' Other constant values for UPS calibration.
251- freshData.UPS_Value.Batt_Capacity = Double .Parse(GetUPSVar( "battery.capacity" , 7 ), ciClone )
252- Freq_Fallback = Double .Parse(GetUPSVar( "output.frequency.nominal" , ( 50 + CInt (Arr_Reg_Key.Item( "FrequencySupply" )) * 10 )), ciClone )
266+ freshData.UPS_Value.Batt_Capacity = Double .Parse(GetUPSVar( "battery.capacity" , 7 ), INVARIANT_CULTURE )
267+ Freq_Fallback = Double .Parse(GetUPSVar( "output.frequency.nominal" , ( 50 + CInt (Arr_Reg_Key.Item( "FrequencySupply" )) * 10 )), INVARIANT_CULTURE )
253268
269+ LogFile.LogTracing( "Completed retrieval of basic UPS product information." , LogLvl.LOG_NOTICE, Me )
254270 Return freshData
255271 End Function
256272
257273 Private oldStatusBitmask As Integer
258-
259- Public Sub Retrieve_UPS_Datas() Handles Update_Data.Tick ' As UPSData
274+ Private Sub Retrieve_UPS_Datas(sender As Object , e As EventArgs)
260275 LogFile.LogTracing( "Enter Retrieve_UPS_Datas" , LogLvl.LOG_DEBUG, Me )
276+
261277 Try
262278 Dim UPS_rt_Status As String
263279
264280 If IsConnected Then
265281 With UPS_Datas.UPS_Value
266- .Batt_Charge = Double .Parse(GetUPSVar( "battery.charge" , 255 ), ciClone)
267- .Batt_Voltage = Double .Parse(GetUPSVar( "battery.voltage" , 12 ), ciClone)
268- .Batt_Runtime = Double .Parse(GetUPSVar( "battery.runtime" , 86400 ), ciClone)
269- .Power_Frequency = Double .Parse(GetUPSVar( "input.frequency" , Double .Parse(GetUPSVar( "output.frequency" , Freq_Fallback), ciClone)), ciClone)
270- .Input_Voltage = Double .Parse(GetUPSVar( "input.voltage" , 220 ), ciClone)
271- .Output_Voltage = Double .Parse(GetUPSVar( "output.voltage" , .Input_Voltage), ciClone)
272- .Load = Double .Parse(GetUPSVar( "ups.load" , 0 ), ciClone)
273- .Output_Power = If (_PowerCalculationMethod <> PowerMethod.Unavailable, GetPowerUsage(), 0 )
282+ .Batt_Charge = Double .Parse(GetUPSVar( "battery.charge" , 255 ), INVARIANT_CULTURE)
283+ .Batt_Voltage = Double .Parse(GetUPSVar( "battery.voltage" , 12 ), INVARIANT_CULTURE)
284+ .Batt_Runtime = Double .Parse(GetUPSVar( "battery.runtime" , 86400 ), INVARIANT_CULTURE)
285+ .Power_Frequency = Double .Parse(GetUPSVar( "input.frequency" , Double .Parse(GetUPSVar( "output.frequency" , Freq_Fallback), INVARIANT_CULTURE)), INVARIANT_CULTURE)
286+ .Input_Voltage = Double .Parse(GetUPSVar( "input.voltage" , 220 ), INVARIANT_CULTURE)
287+ .Output_Voltage = Double .Parse(GetUPSVar( "output.voltage" , .Input_Voltage), INVARIANT_CULTURE)
288+ .Load = Double .Parse(GetUPSVar( "ups.load" , 0 ), INVARIANT_CULTURE)
289+
290+ ' Retrieve and/or calculate output power if possible.
291+ If _PowerCalculationMethod <> PowerMethod.Unavailable Then
292+ Dim parsedValue As Double
293+
294+ Try
295+ If _PowerCalculationMethod = PowerMethod.RealPower Then
296+ parsedValue = Double .Parse(GetUPSVar( "ups.realpower" ), INVARIANT_CULTURE)
297+
298+ ElseIf _PowerCalculationMethod = PowerMethod.NominalPowerCalc Then
299+ parsedValue = Double .Parse(GetUPSVar( "ups.realpower.nominal" ), INVARIANT_CULTURE)
300+ parsedValue *= UPS_Datas.UPS_Value.Load / 100.0
301+
302+ ElseIf _PowerCalculationMethod = PowerMethod.VoltAmpCalc Then
303+ Dim nomCurrent = Double .Parse(GetUPSVar( "input.current.nominal" ), INVARIANT_CULTURE)
304+ Dim nomVoltage = Double .Parse(GetUPSVar( "input.voltage.nominal" ), INVARIANT_CULTURE)
305+
306+ parsedValue = (nomCurrent * nomVoltage * 0.8 ) * (UPS_Datas.UPS_Value.Load / 100 . 0 )
307+ Else
308+ Throw New InvalidOperationException( "Insufficient variables to calculate power." )
309+ End If
310+ Catch ex As FormatException
311+ LogFile.LogTracing( "Unexpected format trying to parse value from UPS. Exception:" , LogLvl.LOG_ERROR, Me )
312+ LogFile.LogTracing(ex.ToString(), LogLvl.LOG_ERROR, Me )
313+ LogFile.LogTracing( "parsedValue: " & parsedValue, LogLvl.LOG_ERROR, Me )
314+ End Try
274315
316+ .Output_Power = parsedValue
317+ End If
318+
319+ ' Handle cases of UPSs that are unable to report battery runtime or load correctly while on battery.
275320 Dim PowerDivider As Double = 0.5
276321 Select Case .Load
277322 Case 76 To 100
278323 PowerDivider = 0.4
279324 Case 51 To 75
280325 PowerDivider = 0.3
281326 End Select
327+
282328 If .Batt_Charge = 255 Then
283329 Dim nBatt = Math.Floor(.Batt_Voltage / 12 )
284330 .Batt_Charge = Math.Floor((.Batt_Voltage - ( 11.6 * nBatt)) / ( 0 . 02 * nBatt))
285331 End If
332+
286333 If .Batt_Runtime >= 86400 Then
287- 'If Load is 0, the calculation results in infinity. This causes an exception in DataUpdated(), causing Me.Disconnect to run in the exception handler below.
288- 'Thus a connection is established, but is forcefully disconneced almost immediately. This cycle repeats on each connect until load is <> 0
289- '(Example: I have a 0% load if only Pi, Microtik Router, Wifi AP and switches are running)
290334 .Load = If (.Load <> 0 , .Load, 0 . 1 )
291335 Dim BattInstantCurrent = (.Output_Voltage * .Load) / (.Batt_Voltage * 100 )
292336 .Batt_Runtime = Math.Floor(.Batt_Capacity * 0.6 * .Batt_Charge * ( 1 - PowerDivider) * 3600 / (BattInstantCurrent * 100 ))
@@ -299,7 +343,7 @@ Public Class UPS_Device
299343 .UPS_Status = [Enum].Parse( GetType (UPS_States), UPS_rt_Status)
300344 Catch ex As ArgumentException
301345 LogFile.LogTracing( "Likely encountered an unknown/invalid UPS status. Using previous status." &
302- vbNewLine & ex.Message, LogLvl.LOG_ERROR, Me )
346+ vbNewLine & ex.Message, LogLvl.LOG_ERROR, Me )
303347 End Try
304348
305349 ' Get the difference between the old and new statuses, and filter only for active ones.
@@ -324,27 +368,6 @@ Public Class UPS_Device
324368 End Try
325369 End Sub
326370
327- ''' <summary>
328- ''' Attempts to get the power usage of this UPS.
329- ''' </summary>
330- ''' <returns></returns>
331- ''' <throws><see cref="NutException"/></throws>
332- Private Function GetPowerUsage() As Double
333- If _PowerCalculationMethod = PowerMethod.RealPower Then
334- Return Integer .Parse(GetUPSVar( "ups.realpower" ))
335- ElseIf _PowerCalculationMethod = PowerMethod.NominalPowerCalc Then
336- Return Integer .Parse(GetUPSVar( "ups.realpower.nominal" )) *
337- (UPS_Datas.UPS_Value.Load / 100.0 )
338- ElseIf _PowerCalculationMethod = PowerMethod.VoltAmpCalc Then
339- Dim nomCurrent = Double .Parse(GetUPSVar( "input.current.nominal" ))
340- Dim nomVoltage = Double .Parse(GetUPSVar( "input.voltage.nominal" ))
341-
342- Return (nomCurrent * nomVoltage * 0.8 ) * (UPS_Datas.UPS_Value.Load / 100 . 0 )
343- Else
344- Throw New InvalidOperationException( "Insufficient variables to calculate power." )
345- End If
346- End Function
347-
348371 Private Const MAX_VAR_RETRIES = 3
349372 Public Function GetUPSVar(varName As String , Optional Fallback_value As Object = Nothing , Optional recursing As Boolean = False ) As String
350373 If Not IsConnected Then
0 commit comments