220 lines
11 KiB
C#
220 lines
11 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts;
|
|
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
|
|
|
|
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
|
|
{
|
|
/// <summary>
|
|
/// Read of required registers and calculation of power correction
|
|
/// </summary>
|
|
public class PowerCorrectionCalc
|
|
{
|
|
private readonly GenesisMeter _currentGenesis;
|
|
private readonly PowerCorrection _powCorr;
|
|
|
|
/// <summary>
|
|
/// Ctor
|
|
/// </summary>
|
|
/// <param name="currentGenesis"></param>
|
|
/// <param name="powCorr"></param>
|
|
/// <returns>true if successful</returns>
|
|
public PowerCorrectionCalc(GenesisMeter currentGenesis, PowerCorrection powCorr)
|
|
{
|
|
_currentGenesis = currentGenesis;
|
|
_powCorr = powCorr;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calculate the values for power correction
|
|
/// </summary>
|
|
/// <returns>true if successful</returns>
|
|
/// <remarks date="2024-May-15" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean PowerCorrEstimation()
|
|
{
|
|
if (_currentGenesis == null || _powCorr == null)
|
|
return false;
|
|
|
|
var logOnReminder = _currentGenesis.IsLoggedOn;
|
|
var retVal = _currentGenesis.ReLogin();
|
|
if (retVal)
|
|
{
|
|
_powCorr.CorrectedTotalUsedCharge_uAs = null;
|
|
// read registers
|
|
_powCorr.ActualPulseSequenceCounter = RegisterConverter.ByteArrayToValue<Byte>(
|
|
_currentGenesis.ReadRegister(Register.Irda.PulseSequence));
|
|
_powCorr.ActualPulseEvenDistribution = RegisterConverter.ByteArrayToValue<Boolean>(
|
|
_currentGenesis.ReadRegister(Register.Metrologyasst.PulseEvenDistribution));
|
|
_powCorr.ActualPulseMode = RegisterConverter.ByteArrayToValue<Byte>(
|
|
_currentGenesis.ReadRegister(Register.Metrologyasst.PulseMode));
|
|
_powCorr.ActualRadioSystemState = RegisterConverter.ByteArrayToValue<Byte>(
|
|
_currentGenesis.ReadRegister(Register.Sensusradio.SystemState));
|
|
_powCorr.ActualTotalUsedSeconds_s = RegisterConverter.ByteArrayToValue<UInt32>(
|
|
_currentGenesis.ReadRegister(Register.Powermon.BatteryExceededSeconds));
|
|
_powCorr.ActualTotalUsedCharge_uAs = RegisterConverter.ByteArrayToValue<UInt64>(
|
|
_currentGenesis.ReadRegister(Register.Powermon.BatteryDrainedLoad, 8));
|
|
_powCorr.BatteryQuantity = RegisterConverter.ByteArrayToValue<Int32>(
|
|
_currentGenesis.ReadRegister(Register.Powermon.BatteryQuantity));
|
|
_powCorr.InitialBatteryLoad_mAh = RegisterConverter.ByteArrayToValue<UInt32>(
|
|
_currentGenesis.ReadRegister(Register.Powermon.BatteryInitialLoad));
|
|
|
|
if (!logOnReminder)
|
|
_currentGenesis.Logout();
|
|
|
|
// get actual time from PC
|
|
_powCorr.ActualDateTimeUtc = DateTime.UtcNow;
|
|
|
|
// skip if indicated that a pulse adapter never has been installed
|
|
_powCorr.PulseAdapterMarkedAsInstalled =
|
|
_powCorr.ProductionPulseAdapterIsInstalled ||
|
|
_powCorr.ActualPulseAdapterIsInstalled;
|
|
|
|
if (!_powCorr.PulseAdapterMarkedAsInstalled)
|
|
return true;
|
|
|
|
// calculate post production used seconds
|
|
if (_powCorr.ProductionTotalUsedSeconds_s > _powCorr.ActualTotalUsedSeconds_s)
|
|
_powCorr.PostProductionTotalUsedSeconds_s = _powCorr.ActualTotalUsedSeconds_s;
|
|
else
|
|
_powCorr.PostProductionTotalUsedSeconds_s = _powCorr.ActualTotalUsedSeconds_s -
|
|
_powCorr.ProductionTotalUsedSeconds_s;
|
|
|
|
// preset production used charge if not received from DB as logging of this value
|
|
// in early production processes was not granted
|
|
if (_powCorr.ProductionTotalUsedCharge_uAs == 0)
|
|
_powCorr.ProductionTotalUsedCharge_uAs = (UInt64)(_powCorr.ProductionTotalUsedSeconds_s *
|
|
PowCorrConst.FixProductionCurrent_uA);
|
|
// calculate pulse report length
|
|
if ((_powCorr.ActualPulseMode >= 1 && _powCorr.ActualPulseMode <= 4) ||
|
|
(_powCorr.ProductionPulseMode >= 1 && _powCorr.ProductionPulseMode <= 4))
|
|
{
|
|
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode1To4Even0;
|
|
if (_powCorr.ActualPulseEvenDistribution)
|
|
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode1To4Even1;
|
|
}
|
|
else if ((_powCorr.ActualPulseMode >= 5 && _powCorr.ActualPulseMode <= 6) ||
|
|
(_powCorr.ProductionPulseMode >= 5 && _powCorr.ProductionPulseMode <= 6))
|
|
{
|
|
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode5To6Even0;
|
|
if (_powCorr.ActualPulseEvenDistribution)
|
|
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode5To6Even1;
|
|
}
|
|
|
|
// calculate overestimation based on pulse report length
|
|
_powCorr.OverestimatedTotalUsedCharge_uAs = 0;
|
|
if ((_powCorr.ActualPulseMode >= 1 && _powCorr.ActualPulseMode <= 6) ||
|
|
(_powCorr.ProductionPulseMode >= 1 && _powCorr.ProductionPulseMode <= 6))
|
|
{
|
|
_powCorr.OverestimatedTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.PulseReportLength * PowCorrConst.PulseReportLengthEstimate_uA *
|
|
_powCorr.PostProductionTotalUsedSeconds_s);
|
|
}
|
|
|
|
Byte radioSystemState = 0;
|
|
if (_powCorr.ActualRadioSystemState != null)
|
|
{
|
|
radioSystemState = (Byte)_powCorr.ActualRadioSystemState;
|
|
}
|
|
else if (_powCorr.ProductionRadioSystemState != null)
|
|
{
|
|
radioSystemState = (Byte)_powCorr.ProductionRadioSystemState;
|
|
}
|
|
|
|
if (_currentGenesis.Region == "NA")
|
|
{
|
|
_powCorr.MinimumTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.MinNaCurrent_uA);
|
|
_powCorr.FixedEstimateTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.FixNaCurrent_uA);
|
|
}
|
|
else if (_currentGenesis.Region == "EMEA" &&
|
|
(radioSystemState & PowCorrConst.SensusRadioSystemStateTfXModeMask) ==
|
|
PowCorrConst.SensusRadioSystemStateTfXModeMask)
|
|
{
|
|
_powCorr.MinimumTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.MinEmeaRadioTfxModeCurrent_uA);
|
|
_powCorr.FixedEstimateTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.FixEmeaRadioTfxModeCurrent_uA);
|
|
}
|
|
else if (_currentGenesis.Region == "EMEA" &&
|
|
(radioSystemState & PowCorrConst.SensusRadioSystemStateTfXModeMask) !=
|
|
PowCorrConst.SensusRadioSystemStateTfXModeMask)
|
|
{
|
|
_powCorr.MinimumTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.MinEmeaRadioRfModeCurrent_uA);
|
|
_powCorr.FixedEstimateTotalUsedCharge_uAs =
|
|
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.FixEmeaRadioRfModeCurrent_uA);
|
|
}
|
|
|
|
if (_powCorr.OverestimatedTotalUsedCharge_uAs > _powCorr.ActualTotalUsedCharge_uAs)
|
|
_powCorr.CorrectedTotalUsedCharge_uAs = 0;
|
|
else
|
|
_powCorr.CorrectedTotalUsedCharge_uAs = _powCorr.ActualTotalUsedCharge_uAs -
|
|
_powCorr.OverestimatedTotalUsedCharge_uAs;
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
/// <summary>
|
|
/// Calculate the values for power correction
|
|
/// </summary>
|
|
/// <returns>true if successful</returns>
|
|
/// <remarks date="2024-May-14" author="Thomas Wiedebusch">
|
|
/// - Initial
|
|
/// </remarks>
|
|
public Boolean CorrectPowerConsumption()
|
|
{
|
|
if (_currentGenesis == null || _powCorr == null || _powCorr.CorrectedTotalUsedCharge_uAs == null)
|
|
return false;
|
|
|
|
// check which value should be applied and set the CorrectedTotalUsedCharge_uAs
|
|
if (_powCorr.ProductionPulseAdapterIsInstalled)
|
|
{
|
|
// avoid undershot below smallest value
|
|
if (_powCorr.CorrectedTotalUsedCharge_uAs < _powCorr.MinimumTotalUsedCharge_uAs)
|
|
_powCorr.CorrectedTotalUsedCharge_uAs = _powCorr.MinimumTotalUsedCharge_uAs;
|
|
}
|
|
else // retrofitted
|
|
{
|
|
// correct to smaller value and mark corrected = actual to log that correction not needed
|
|
_powCorr.CorrectedTotalUsedCharge_uAs =
|
|
_powCorr.ActualTotalUsedCharge_uAs > _powCorr.FixedEstimateTotalUsedCharge_uAs ?
|
|
_powCorr.FixedEstimateTotalUsedCharge_uAs : _powCorr.ActualTotalUsedCharge_uAs;
|
|
}
|
|
|
|
if (_powCorr.ActualTotalUsedCharge_uAs == _powCorr.CorrectedTotalUsedCharge_uAs)
|
|
return true;
|
|
|
|
// save corrected power consumption data to meter
|
|
var logOnReminder = _currentGenesis.IsLoggedOn;
|
|
var retVal = _currentGenesis.ReLogin();
|
|
if (retVal)
|
|
{
|
|
var retriesLeft = 2;
|
|
do
|
|
{
|
|
// write the new calculated value
|
|
retVal = _currentGenesis.WriteRegister(Register.Powermon.BatteryDrainedLoad,
|
|
RegisterConverter.ValueToByteArray(_powCorr.CorrectedTotalUsedCharge_uAs));
|
|
if (!retVal)
|
|
continue;
|
|
|
|
// store configuration
|
|
retVal = _currentGenesis.WriteRegister(Register.Powermon.StoreConfiguration, 1);
|
|
if (!retVal)
|
|
continue;
|
|
|
|
var readResult = _currentGenesis.ReadRegister(Register.Powermon.StoreConfiguration);
|
|
retVal = readResult != null && readResult.ToList().All(array => array == 0x00);
|
|
} while (!retVal && retriesLeft-- > 0);
|
|
}
|
|
|
|
if (!logOnReminder)
|
|
_currentGenesis.Logout();
|
|
return retVal;
|
|
}
|
|
}
|
|
}
|