Skip to content

Commit 12fa7ec

Browse files
authored
Merge pull request #151 from nutdotnet/150-huawei-load-support
Power value calculation updates (specifically for Huawei UPS)
2 parents ccf076d + 4ab53c1 commit 12fa7ec

File tree

3 files changed

+91
-31
lines changed

3 files changed

+91
-31
lines changed

WinNUT_V2/WinNUT-Client_Common/Common_Classes.vb

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Public Class UPS_Values
1616
Public Batt_Runtime As Double = Nothing
1717
Public Input_Voltage As Double = Nothing
1818
Public Output_Voltage As Double = Nothing
19+
Public Output_Current As Single?
1920
Public Power_Frequency As Double = Nothing
2021
Public Load As Double = Nothing
2122
Public Output_Power As Double = Nothing

WinNUT_V2/WinNUT-Client_Common/Common_Enums.vb

+7-2
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,14 @@ Public Enum UPS_States
118118
ALARM = 1 << 13
119119
End Enum
120120

121+
''' <summary>
122+
''' Ref. https://github.com/nutdotnet/WinNUT-Client/pull/112
123+
''' </summary>
121124
Public Enum PowerMethod
122125
Unavailable ' No methods are available to calculate power.
123126
RealPower ' The ups.realpower variable is available for direct reading.
124-
NominalPowerCalc ' Power can be calculated by taking the load percentage of the nominal power variable.
125-
VoltAmpCalc ' Power will have be calculated as a function of volts and amps.
127+
RealOutputPower ' output.realpower is available for direct reading.
128+
RPNomLoadPct ' Power is calcualted as the load percentage of nominal realpower.
129+
InputNomVALoadPct ' Power is given as the nominal power calculated from nominal input volts/amps (and PF), multiplied by the percent load.
130+
OutputVACalc ' Power is calculated with output voltage and current (seen on Huawei, issue #150)
126131
End Enum

WinNUT_V2/WinNUT-Client_Common/UPS_Device.vb

+83-29
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Imports System.Windows.Forms
1313
Public Class UPS_Device
1414
#Region "Statics/Defaults"
1515
Private ReadOnly INVARIANT_CULTURE = CultureInfo.InvariantCulture
16-
Private Const CosPhi As Double = 0.6
16+
Private Const POWER_FACTOR = 0.8
1717

1818
' How many milliseconds to wait before the Reconnect routine tries again.
1919
Private Const DEFAULT_RECONNECT_WAIT_MS As Double = 5000
@@ -238,27 +238,69 @@ Public Class UPS_Device
238238
Trim(GetUPSVar("ups.serial", "Unknown")),
239239
Trim(GetUPSVar("ups.firmware", "Unknown")))
240240

241+
With freshData.UPS_Value
242+
LogFile.LogTracing("Initializing other well-known UPS variables...", LogLvl.LOG_DEBUG, Me)
243+
Try
244+
Dim value = Single.Parse(GetUPSVar("output.current"), INVARIANT_CULTURE)
245+
.Output_Current = value
246+
LogFile.LogTracing("output.current: " & value, LogLvl.LOG_DEBUG, Me)
247+
Catch ex As Exception
248+
If ex.GetType() <> GetType(NutException) Then
249+
LogFile.LogException(ex, Me)
250+
End If
251+
End Try
252+
Try
253+
Dim value = Single.Parse(GetUPSVar("output.voltage"), INVARIANT_CULTURE)
254+
.Output_Voltage = value
255+
LogFile.LogTracing("output.voltage: " & value, LogLvl.LOG_DEBUG, Me)
256+
Catch ex As Exception
257+
If ex.GetType() <> GetType(NutException) Then
258+
LogFile.LogException(ex, Me)
259+
End If
260+
End Try
261+
Try
262+
Dim value = Single.Parse(GetUPSVar("output.realpower"), INVARIANT_CULTURE)
263+
.Output_Power = value
264+
LogFile.LogTracing("output.power: " & value, LogLvl.LOG_DEBUG, Me)
265+
Catch ex As Exception
266+
267+
End Try
268+
End With
269+
270+
' Determine optimal method for measuring power output from the UPS.
241271
LogFile.LogTracing("Determining best method to calculate power usage...", LogLvl.LOG_NOTICE, Me)
272+
' Start with directly reading a variable from the UPS.
242273
Try
243-
GetUPSVar("ups.realpower")
244-
_PowerCalculationMethod = PowerMethod.RealPower
245-
LogFile.LogTracing("Using RealPower method to calculate power usage.", LogLvl.LOG_NOTICE, Me)
274+
If freshData.UPS_Value.Output_Power <> Nothing Then
275+
_PowerCalculationMethod = PowerMethod.RealOutputPower
276+
LogFile.LogTracing("Using RealOutputPower method.", LogLvl.LOG_NOTICE, Me)
277+
Else
278+
GetUPSVar("ups.realpower")
279+
_PowerCalculationMethod = PowerMethod.RealPower
280+
LogFile.LogTracing("Using RealPower method.", LogLvl.LOG_NOTICE, Me)
281+
End If
246282
Catch
247283
Try
248284
GetUPSVar("ups.realpower.nominal")
249285
GetUPSVar("ups.load")
250-
_PowerCalculationMethod = PowerMethod.NominalPowerCalc
251-
LogFile.LogTracing("Using NominalPowerCalc method to calculate power usage.", LogLvl.LOG_NOTICE, Me)
286+
_PowerCalculationMethod = PowerMethod.RPNomLoadPct
287+
LogFile.LogTracing("Using RPNomLoadPct method.", LogLvl.LOG_NOTICE, Me)
252288
Catch
253289
Try
254290
GetUPSVar("input.current.nominal")
255291
GetUPSVar("input.voltage.nominal")
256292
GetUPSVar("ups.load")
257-
_PowerCalculationMethod = PowerMethod.VoltAmpCalc
258-
LogFile.LogTracing("Using VoltAmpCalc method to calculate power usage.", LogLvl.LOG_NOTICE, Me)
293+
_PowerCalculationMethod = PowerMethod.InputNomVALoadPct
294+
LogFile.LogTracing("Using InputNomVALoadPct method.", LogLvl.LOG_NOTICE, Me)
259295
Catch
260-
_PowerCalculationMethod = PowerMethod.Unavailable
261-
LogFile.LogTracing("Unable to find a suitable method to calculate power usage.", LogLvl.LOG_WARNING, Me)
296+
If freshData.UPS_Value.Output_Current IsNot Nothing AndAlso
297+
freshData.UPS_Value.Output_Voltage <> Nothing Then
298+
_PowerCalculationMethod = PowerMethod.OutputVACalc
299+
LogFile.LogTracing("Using OutputVACalc method.", LogLvl.LOG_NOTICE, Me)
300+
Else
301+
_PowerCalculationMethod = PowerMethod.Unavailable
302+
LogFile.LogTracing("Unable to find a suitable method to calculate power usage.", LogLvl.LOG_WARNING, Me)
303+
End If
262304
End Try
263305
End Try
264306
End Try
@@ -283,38 +325,51 @@ Public Class UPS_Device
283325
.Batt_Charge = Double.Parse(GetUPSVar("battery.charge", -1), INVARIANT_CULTURE)
284326
.Batt_Voltage = Double.Parse(GetUPSVar("battery.voltage", -1), INVARIANT_CULTURE)
285327
.Batt_Runtime = Double.Parse(GetUPSVar("battery.runtime", -1), INVARIANT_CULTURE)
286-
.Power_Frequency = Double.Parse(GetUPSVar("input.frequency", Double.Parse(GetUPSVar("output.frequency", Freq_Fallback), INVARIANT_CULTURE)), INVARIANT_CULTURE)
328+
.Power_Frequency = Double.Parse(GetUPSVar("input.frequency", Freq_Fallback), INVARIANT_CULTURE)
287329
.Input_Voltage = Double.Parse(GetUPSVar("input.voltage", -1), INVARIANT_CULTURE)
288-
.Output_Voltage = Double.Parse(GetUPSVar("output.voltage", .Input_Voltage), INVARIANT_CULTURE)
330+
.Output_Voltage = Double.Parse(GetUPSVar("output.voltage", -1), INVARIANT_CULTURE)
289331
.Load = Double.Parse(GetUPSVar("ups.load", 0), INVARIANT_CULTURE)
290332

291333
' Retrieve and/or calculate output power if possible.
292334
If _PowerCalculationMethod <> PowerMethod.Unavailable Then
293335
Dim parsedValue As Double
294336

295337
Try
296-
If _PowerCalculationMethod = PowerMethod.RealPower Then
297-
parsedValue = Double.Parse(GetUPSVar("ups.realpower"), INVARIANT_CULTURE)
298-
299-
ElseIf _PowerCalculationMethod = PowerMethod.NominalPowerCalc Then
300-
parsedValue = Double.Parse(GetUPSVar("ups.realpower.nominal"), INVARIANT_CULTURE)
301-
parsedValue *= UPS_Datas.UPS_Value.Load / 100.0
302-
303-
ElseIf _PowerCalculationMethod = PowerMethod.VoltAmpCalc Then
304-
Dim nomCurrent = Double.Parse(GetUPSVar("input.current.nominal"), INVARIANT_CULTURE)
305-
Dim nomVoltage = Double.Parse(GetUPSVar("input.voltage.nominal"), INVARIANT_CULTURE)
306-
307-
parsedValue = (nomCurrent * nomVoltage * 0.8) * (UPS_Datas.UPS_Value.Load / 100.0)
308-
Else
309-
Throw New InvalidOperationException("Insufficient variables to calculate power.")
310-
End If
338+
Select Case _PowerCalculationMethod
339+
Case PowerMethod.RealPower
340+
parsedValue = Double.Parse(GetUPSVar("ups.realpower"), INVARIANT_CULTURE)
341+
342+
Case PowerMethod.RealOutputPower
343+
parsedValue = Single.Parse(GetUPSVar("output.realpower"), INVARIANT_CULTURE)
344+
345+
Case PowerMethod.RPNomLoadPct
346+
parsedValue = Double.Parse(GetUPSVar("ups.realpower.nominal"), INVARIANT_CULTURE)
347+
parsedValue *= UPS_Datas.UPS_Value.Load / 100.0
348+
349+
Case PowerMethod.InputNomVALoadPct
350+
Dim nomCurrent = Double.Parse(GetUPSVar("input.current.nominal"), INVARIANT_CULTURE)
351+
Dim nomVoltage = Double.Parse(GetUPSVar("input.voltage.nominal"), INVARIANT_CULTURE)
352+
353+
parsedValue = nomCurrent * nomVoltage * POWER_FACTOR
354+
parsedValue *= UPS_Datas.UPS_Value.Load / 100.0
355+
Case PowerMethod.OutputVACalc
356+
.Output_Current = Single.Parse(GetUPSVar("output.current"), INVARIANT_CULTURE)
357+
parsedValue = .Output_Current * .Output_Voltage * POWER_FACTOR
358+
Case Else
359+
' Should not trigger - something has gone wrong.
360+
Throw New InvalidOperationException("Reached Else case when attempting to get power output for method " & _PowerCalculationMethod)
361+
End Select
311362
Catch ex As FormatException
312363
LogFile.LogTracing("Unexpected format trying to parse value from UPS. Exception:", LogLvl.LOG_ERROR, Me)
313364
LogFile.LogTracing(ex.ToString(), LogLvl.LOG_ERROR, Me)
314365
LogFile.LogTracing("parsedValue: " & parsedValue, LogLvl.LOG_ERROR, Me)
366+
Catch ex As Exception
367+
LogFile.LogException(ex, Me)
315368
End Try
316369

317-
.Output_Power = parsedValue
370+
' Apply rounding to this number since calculations have extended to three decimal places.
371+
' TODO: Remove this round function once gauges can handle decimal places better.
372+
.Output_Power = Math.Round(parsedValue, 1)
318373
End If
319374

320375
' Handle out-of-range battery charge
@@ -428,7 +483,6 @@ Public Class UPS_Device
428483
LogFile.LogTracing("Apply Fallback Value when retrieving " & varName, LogLvl.LOG_WARNING, Me)
429484
Return Fallback_value
430485
Else
431-
LogFile.LogTracing("Unhandled error while getting " & varName, LogLvl.LOG_ERROR, Me)
432486
Throw
433487
End If
434488
End Try

0 commit comments

Comments
 (0)