common/Hardware/WaterMeter/Genesis/GenesisCore/PowerCorrectionCalc.cs
2026-04-23 17:50:07 +02:00

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;
}
}
}