Refactorization : BenchControl -> Rig

This commit is contained in:
Milan Hanajik
2021-03-05 16:12:24 +01:00
parent 84c8ec26b6
commit 6cf3bd6160
1461 changed files with 6330 additions and 6330 deletions
@@ -0,0 +1,242 @@
///
/// Copyright (c) 2015-2020 Sensus Metering Systems
///
using System;
using System.IO;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class CalibrationStruct
{
public const int Length = 35;
public Byte Version;
public MeterType MeterType;
public UInt16 Calibration;
public VolumeUnits VolumeUnits;
public FlowArrow FlowArrow;
public UInt16 FWVersion;
public UInt16[] TargetField;
public UInt16 RecipMeanCurrent;
public UInt16 ThresholdVolume;
public UInt16 ThresholdTime;
public UInt16 FlowActivationThr;
public UInt16 VolumeArrowThr;
public UInt32 CalibrationTime;
public ulong SerialNumber;
public MeterSealed MeterSealed;
public byte CheckSum;
public CalibrationStruct()
{
TargetField = new UInt16[3];
}
public byte[] ToByteArray()
{
byte[] result = new byte[Length];
result[0] = Version;
result[1] = (byte)MeterType;
result[2] = (byte)(Calibration & 0x00FF);
result[3] = (byte)((Calibration >> 8) & 0x00FF);
result[4] = (byte)VolumeUnits;
result[5] = (byte)FlowArrow;
result[6] = (byte)(FWVersion & 0x00FF);
result[7] = (byte)((FWVersion >> 8) & 0x00FF);
result[8] = (byte)( TargetField[0] & 0x00FF);
result[9] = (byte)((TargetField[0] >> 8) & 0x00FF);
result[10] = (byte)( TargetField[1] & 0x00FF);
result[11] = (byte)((TargetField[1] >> 8) & 0x00FF);
result[12] = (byte)( TargetField[2] & 0x00FF);
result[13] = (byte)((TargetField[2] >> 8) & 0x00FF);
result[14] = (byte)(RecipMeanCurrent & 0x00FF);
result[15] = (byte)((RecipMeanCurrent >> 8) & 0x00FF);
result[16] = (byte)(ThresholdVolume & 0x00FF);
result[17] = (byte)((ThresholdVolume >> 8) & 0x00FF);
result[18] = (byte)(ThresholdTime & 0x00FF);
result[19] = (byte)((ThresholdTime >> 8) & 0x00FF);
result[20] = (byte)(FlowActivationThr & 0x00FF);
result[21] = (byte)((FlowActivationThr >> 8) & 0x00FF);
result[22] = (byte)(VolumeArrowThr & 0x00FF);
result[23] = (byte)((VolumeArrowThr >> 8) & 0x00FF);
result[24] = (byte)(CalibrationTime & 0x000000FF);
result[25] = (byte)((CalibrationTime >> 8) & 0x000000FF);
result[26] = (byte)((CalibrationTime >> 16) & 0x000000FF);
result[27] = (byte)((CalibrationTime >> 24) & 0x000000FF);
result[28] = (byte)(SerialNumber & 0x00000000000000FF);
result[29] = (byte)((SerialNumber >> 8) & 0x00000000000000FF);
result[30] = (byte)((SerialNumber >> 16) & 0x00000000000000FF);
result[31] = (byte)((SerialNumber >> 24) & 0x00000000000000FF);
result[32] = (byte)((SerialNumber >> 32) & 0x00000000000000FF);
result[33] = (byte)MeterSealed;
result[34] = CheckSum;
return result;
}
/// <summary>
/// Create a calibration structure from a complete byte array
/// </summary>
/// <param name="data">A complete byte array data</param>
/// <returns>CalibrationStruct or null when byte array was not complete</returns>
public static CalibrationStruct FromByteArray(byte[] data)
{
if (data.Length != Length) return null;
CalibrationStruct result = new CalibrationStruct();
result.Version = data[0];
result.MeterType = (MeterType)data[1];
result.Calibration = (UInt16)(data[2] + 256 * data[3]);
result.VolumeUnits = (VolumeUnits)data[4];
result.FlowArrow = (FlowArrow)data[5];
result.FWVersion = (UInt16)(data[6] + 256 * data[7]);
result.TargetField[0] = (UInt16)(data[8] + 256 * data[9]);
result.TargetField[1] = (UInt16)(data[10] + 256 * data[11]);
result.TargetField[2] = (UInt16)(data[12] + 256 * data[13]);
result.RecipMeanCurrent = (UInt16)(data[14] + 256 * data[15]);
result.ThresholdVolume = (UInt16)(data[16] + 256 * data[17]);
result.ThresholdTime = (UInt16)(data[18] + 256 * data[19]);
result.FlowActivationThr = (UInt16)(data[20] + 256 * data[21]);
result.VolumeArrowThr = (UInt16)(data[22] + 256 * data[23]);
result.CalibrationTime = (((UInt32)data[27] * 256 + data[26]) * 256 + data[25]) * 256 + data[24];
result.SerialNumber = ((((UInt64)data[32] * 256 + data[31]) * 256 + data[30]) * 256 + data[29]) * 256 + data[28];
result.MeterSealed = (MeterSealed)data[33];
result.CheckSum = data[34];
return result;
}
/// <summary>
/// Update the calibration structure from an incomplete byte array
/// </summary>
/// <param name="data">Byte array data</param>
/// <param name="offset">Offset of byte array data in CalibrationStruct</param>
/// <returns>true when successful, false when data are not appropriate</returns>
public bool Update(byte[] data, int offset)
{
if ((data.Length == 2) && (offset == 2))
{
/// Data containing iPerl calibration factor
Calibration = (UInt16)(data[0] + 256 * data[1]);
return true;
}
else if ((data.Length == Length) && (offset == 0))
{
/// Data containing a complete CalibrationStruct
Version = data[0];
MeterType = (MeterType)data[1];
Calibration = (UInt16)(data[2] + 256 * data[3]);
VolumeUnits = (VolumeUnits)data[4];
FlowArrow = (FlowArrow)data[5];
FWVersion = (UInt16)(data[6] + 256 * data[7]);
TargetField[0] = (UInt16)(data[8] + 256 * data[9]);
TargetField[1] = (UInt16)(data[10] + 256 * data[11]);
TargetField[2] = (UInt16)(data[12] + 256 * data[13]);
RecipMeanCurrent = (UInt16)(data[14] + 256 * data[15]);
ThresholdVolume = (UInt16)(data[16] + 256 * data[17]);
ThresholdTime = (UInt16)(data[18] + 256 * data[19]);
FlowActivationThr = (UInt16)(data[20] + 256 * data[21]);
VolumeArrowThr = (UInt16)(data[22] + 256 * data[23]);
CalibrationTime = (((UInt32)data[27] * 256 + data[26]) * 256 + data[25]) * 256 + data[24];
SerialNumber = ((((UInt64)data[32] * 256 + data[31]) * 256 + data[30]) * 256 + data[29]) * 256 + data[28];
MeterSealed = (MeterSealed)data[33];
CheckSum = data[34];
return true;
}
else
return false;
}
public string FWVersionStr()
{
int d1 = (FWVersion >> 8) & 0x000F;
int d2 = (FWVersion >> 12) & 0x000F;
int d3 = (FWVersion >> 4) & 0x000F;
int d4 = FWVersion & 0x000F;
return string.Format("{0}.{1}{2}{3}", d1, d2, d3, d4);
}
public override string ToString()
{
return string.Format("Calibration: V{0} Type={1} Cal={2} Units={3} FlowArrow.{4} FW={5} Hi={6} Norm={7} Low={8} RMC={9} ThrVol={10} ThrTime={11} FlActThr={12} VolArrThr={13} CalTm={14} SN={15} MeterSealed={16} Chksum={17}",
Version,
MeterType,
Calibration,
VolumeUnits,
FlowArrow,
FWVersion,
TargetField[0],
TargetField[1],
TargetField[2],
RecipMeanCurrent,
ThresholdVolume,
ThresholdTime,
FlowActivationThr,
VolumeArrowThr,
CalibrationTime,
SerialNumber,
MeterSealed,
CheckSum.ToString("X2"));
}
public virtual void WriteBinary(BinaryWriter writer)
{
writer.Write(Version);
writer.Write((byte)MeterType);
writer.Write(Calibration);
writer.Write((byte)VolumeUnits);
writer.Write((byte)FlowArrow);
writer.Write(FWVersion);
writer.Write(TargetField[0]);
writer.Write(TargetField[1]);
writer.Write(TargetField[2]);
writer.Write(RecipMeanCurrent);
writer.Write(ThresholdVolume);
writer.Write(ThresholdTime);
writer.Write(FlowActivationThr);
writer.Write(VolumeArrowThr);
writer.Write(CalibrationTime);
writer.Write(SerialNumber);
writer.Write((byte)MeterSealed);
writer.Write(CheckSum);
}
public virtual void ReadBinary(BinaryReader reader)
{
Version = reader.ReadByte();
MeterType = (MeterType)reader.ReadByte();
Calibration = reader.ReadUInt16();
VolumeUnits = (VolumeUnits)reader.ReadByte();
FlowArrow = (FlowArrow)reader.ReadByte();
FWVersion = reader.ReadUInt16();
TargetField[0] = reader.ReadUInt16();
TargetField[1] = reader.ReadUInt16();
TargetField[2] = reader.ReadUInt16();
RecipMeanCurrent = reader.ReadUInt16();
ThresholdVolume = reader.ReadUInt16();
ThresholdTime = reader.ReadUInt16();
FlowActivationThr = reader.ReadUInt16();
VolumeArrowThr = reader.ReadUInt16();
CalibrationTime = reader.ReadUInt32();
SerialNumber = reader.ReadUInt64();
MeterSealed = (MeterSealed)reader.ReadByte();
CheckSum = reader.ReadByte();
}
}
}
@@ -0,0 +1,258 @@
///
/// Copyright (c) 2018-2020 Sensus Slovensko a.s.
///
using System;
using System.IO;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class CalibrationStructV4
{
public const int Length = 37;
public Byte Version;
public MeterType MeterType;
public UInt16 Calibration;
public VolumeUnits VolumeUnits;
public FlowArrow FlowArrow;
public UInt16 FWVersion;
public UInt16[] TargetField;
public UInt16 RecipMeanCurrent;
public UInt16 ThresholdVolume;
public UInt16 ThresholdTime;
public UInt16 FlowActivationThr;
public UInt16 VolumeArrowThr;
public UInt32 CalibrationTime;
public ulong SerialNumber;
public MeterSealed MeterSealed;
public UInt16 CalibrationLNA;
public byte CheckSum;
public CalibrationStructV4()
{
TargetField = new UInt16[3];
}
public byte[] ToByteArray()
{
byte[] result = new byte[Length];
result[0] = Version;
result[1] = (byte)MeterType;
result[2] = (byte)(Calibration & 0x00FF);
result[3] = (byte)((Calibration >> 8) & 0x00FF);
result[4] = (byte)VolumeUnits;
result[5] = (byte)FlowArrow;
result[6] = (byte)(FWVersion & 0x00FF);
result[7] = (byte)((FWVersion >> 8) & 0x00FF);
result[8] = (byte)( TargetField[0] & 0x00FF);
result[9] = (byte)((TargetField[0] >> 8) & 0x00FF);
result[10] = (byte)( TargetField[1] & 0x00FF);
result[11] = (byte)((TargetField[1] >> 8) & 0x00FF);
result[12] = (byte)( TargetField[2] & 0x00FF);
result[13] = (byte)((TargetField[2] >> 8) & 0x00FF);
result[14] = (byte)(RecipMeanCurrent & 0x00FF);
result[15] = (byte)((RecipMeanCurrent >> 8) & 0x00FF);
result[16] = (byte)(ThresholdVolume & 0x00FF);
result[17] = (byte)((ThresholdVolume >> 8) & 0x00FF);
result[18] = (byte)(ThresholdTime & 0x00FF);
result[19] = (byte)((ThresholdTime >> 8) & 0x00FF);
result[20] = (byte)(FlowActivationThr & 0x00FF);
result[21] = (byte)((FlowActivationThr >> 8) & 0x00FF);
result[22] = (byte)(VolumeArrowThr & 0x00FF);
result[23] = (byte)((VolumeArrowThr >> 8) & 0x00FF);
result[24] = (byte)(CalibrationTime & 0x000000FF);
result[25] = (byte)((CalibrationTime >> 8) & 0x000000FF);
result[26] = (byte)((CalibrationTime >> 16) & 0x000000FF);
result[27] = (byte)((CalibrationTime >> 24) & 0x000000FF);
result[28] = (byte)(SerialNumber & 0x00000000000000FF);
result[29] = (byte)((SerialNumber >> 8) & 0x00000000000000FF);
result[30] = (byte)((SerialNumber >> 16) & 0x00000000000000FF);
result[31] = (byte)((SerialNumber >> 24) & 0x00000000000000FF);
result[32] = (byte)((SerialNumber >> 32) & 0x00000000000000FF);
result[33] = (byte)MeterSealed;
result[34] = (byte)(CalibrationLNA & 0x00FF);
result[35] = (byte)((CalibrationLNA >> 8) & 0x00FF);
result[36] = CheckSum;
return result;
}
/// <summary>
/// Create a calibration structure from a complete byte array
/// </summary>
/// <param name="data">A complete byte array data</param>
/// <returns>CalibrationStructV4 or null when byte array was not complete</returns>
public static CalibrationStructV4 FromByteArray(byte[] data)
{
if (data.Length != Length) return null;
CalibrationStructV4 result = new CalibrationStructV4();
result.Version = data[0];
result.MeterType = (MeterType)data[1];
result.Calibration = (UInt16)(data[2] + 256 * data[3]);
result.VolumeUnits = (VolumeUnits)data[4];
result.FlowArrow = (FlowArrow)data[5];
result.FWVersion = (UInt16)(data[6] + 256 * data[7]);
result.TargetField[0] = (UInt16)(data[8] + 256 * data[9]);
result.TargetField[1] = (UInt16)(data[10] + 256 * data[11]);
result.TargetField[2] = (UInt16)(data[12] + 256 * data[13]);
result.RecipMeanCurrent = (UInt16)(data[14] + 256 * data[15]);
result.ThresholdVolume = (UInt16)(data[16] + 256 * data[17]);
result.ThresholdTime = (UInt16)(data[18] + 256 * data[19]);
result.FlowActivationThr = (UInt16)(data[20] + 256 * data[21]);
result.VolumeArrowThr = (UInt16)(data[22] + 256 * data[23]);
result.CalibrationTime = (((UInt32)data[27] * 256 + data[26]) * 256 + data[25]) * 256 + data[24];
result.SerialNumber = ((((UInt64)data[32] * 256 + data[31]) * 256 + data[30]) * 256 + data[29]) * 256 + data[28];
result.MeterSealed = (MeterSealed)data[33];
result.CalibrationLNA = (UInt16)(data[34] + 256 * data[35]);
result.CheckSum = data[36];
return result;
}
/// <summary>
/// Update the calibration structure from an incomplete byte array
/// </summary>
/// <param name="data">Byte array data</param>
/// <param name="offset">Offset of byte array data in CalibrationStructV2</param>
/// <returns>true when successful, false when data are not appropriate</returns>
public bool Update(byte[] data, int offset)
{
if ((data.Length == 2) && (offset == 2))
{
/// Data containing iPerl calibration factor
Calibration = (UInt16)(data[0] + 256 * data[1]);
return true;
}
else if ((data.Length == 2) && (offset == 34))
{
/// Data containing iPerl calibration factor
CalibrationLNA = (UInt16)(data[0] + 256 * data[1]);
return true;
}
else if ((data.Length == Length) && (offset == 0))
{
/// Data containing a complete CalibrationStruct
Version = data[0];
MeterType = (MeterType)data[1];
Calibration = (UInt16)(data[2] + 256 * data[3]);
VolumeUnits = (VolumeUnits)data[4];
FlowArrow = (FlowArrow)data[5];
FWVersion = (UInt16)(data[6] + 256 * data[7]);
TargetField[0] = (UInt16)(data[8] + 256 * data[9]);
TargetField[1] = (UInt16)(data[10] + 256 * data[11]);
TargetField[2] = (UInt16)(data[12] + 256 * data[13]);
RecipMeanCurrent = (UInt16)(data[14] + 256 * data[15]);
ThresholdVolume = (UInt16)(data[16] + 256 * data[17]);
ThresholdTime = (UInt16)(data[18] + 256 * data[19]);
FlowActivationThr = (UInt16)(data[20] + 256 * data[21]);
VolumeArrowThr = (UInt16)(data[22] + 256 * data[23]);
CalibrationTime = (((UInt32)data[27] * 256 + data[26]) * 256 + data[25]) * 256 + data[24];
SerialNumber = ((((UInt64)data[32] * 256 + data[31]) * 256 + data[30]) * 256 + data[29]) * 256 + data[28];
MeterSealed = (MeterSealed)data[33];
CalibrationLNA = (UInt16)(data[34] + 256 * data[35]);
CheckSum = data[36];
return true;
}
else
return false;
}
public string FWVersionStr()
{
int d1 = (FWVersion >> 8) & 0x000F;
int d2 = (FWVersion >> 12) & 0x000F;
int d3 = (FWVersion >> 4) & 0x000F;
int d4 = FWVersion & 0x000F;
return string.Format("{0}.{1}{2}{3}", d1, d2, d3, d4);
}
public override string ToString()
{
return string.Format("Calibration: V{0} Type={1} Cal={2} Units={3} FlowArrow.{4} FW={5} Hi={6} Norm={7} Low={8} RMC={9} ThrVol={10} ThrTime={11} FlActThr={12} VolArrThr={13} CalTm={14} SN={15} MeterSealed={16} CalLNA={17} Chksum={18}",
Version,
MeterType,
Calibration,
VolumeUnits,
FlowArrow,
FWVersion,
TargetField[0],
TargetField[1],
TargetField[2],
RecipMeanCurrent,
ThresholdVolume,
ThresholdTime,
FlowActivationThr,
VolumeArrowThr,
CalibrationTime,
SerialNumber,
MeterSealed,
CalibrationLNA,
CheckSum.ToString("X2"));
}
public virtual void WriteBinary(BinaryWriter writer)
{
writer.Write(Version);
writer.Write((byte)MeterType);
writer.Write(Calibration);
writer.Write((byte)VolumeUnits);
writer.Write((byte)FlowArrow);
writer.Write(FWVersion);
writer.Write(TargetField[0]);
writer.Write(TargetField[1]);
writer.Write(TargetField[2]);
writer.Write(RecipMeanCurrent);
writer.Write(ThresholdVolume);
writer.Write(ThresholdTime);
writer.Write(FlowActivationThr);
writer.Write(VolumeArrowThr);
writer.Write(CalibrationTime);
writer.Write(SerialNumber);
writer.Write((byte)MeterSealed);
writer.Write(CalibrationLNA);
writer.Write(CheckSum);
}
public virtual void ReadBinary(BinaryReader reader)
{
Version = reader.ReadByte();
MeterType = (MeterType)reader.ReadByte();
Calibration = reader.ReadUInt16();
VolumeUnits = (VolumeUnits)reader.ReadByte();
FlowArrow = (FlowArrow)reader.ReadByte();
FWVersion = reader.ReadUInt16();
TargetField[0] = reader.ReadUInt16();
TargetField[1] = reader.ReadUInt16();
TargetField[2] = reader.ReadUInt16();
RecipMeanCurrent = reader.ReadUInt16();
ThresholdVolume = reader.ReadUInt16();
ThresholdTime = reader.ReadUInt16();
FlowActivationThr = reader.ReadUInt16();
VolumeArrowThr = reader.ReadUInt16();
CalibrationTime = reader.ReadUInt32();
SerialNumber = reader.ReadUInt64();
MeterSealed = (MeterSealed)reader.ReadByte();
CalibrationLNA = reader.ReadUInt16();
CheckSum = reader.ReadByte();
}
}
}
@@ -0,0 +1,266 @@
///
/// Copyright (c) 2015-2020 Sensus Metering Systems
///
using System;
using System.IO;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class ConfigStruct
{
public const int Length = 32;
public Byte Version; /// 0: 1 byte
public MeterState MeterState; /// 1: 1 byte
public UInt32 TargetTimeVeryLowBatt; /// 2: 4 bytes in seconds
public UInt32 TargetTimeLowBatt; /// 6: 4 bytes, in seconds
public UInt32 TestModeTime; /// 10: 4 bytes, Max. test mode time in seconds
public UInt16 EmptyPipeThreshold; /// 14: 2 bytes
public byte[] PCBNumber; /// 16: 5 bytes
public byte TestModeConfig; /// 21: 1 byte
public UInt32 RadioAddress; /// 22: 4 bytes
public UInt16 TempCalibration; /// 26: 2 bytes
public UInt16 AlarmMask; /// 28: 2 bytes, Default 0xA3F7
public UInt16 ConfigCheckSum; /// 30: 2 bytes
public ConfigStruct()
{
PCBNumber = new byte[5];
}
public byte[] ToByteArray()
{
byte[] result = new byte[Length];
result[0] = Version;
result[1] = (byte)MeterState;
result[2] = (byte)(TargetTimeVeryLowBatt & 0x000000FF);
result[3] = (byte)((TargetTimeVeryLowBatt >> 8) & 0x000000FF);
result[4] = (byte)((TargetTimeVeryLowBatt >> 16) & 0x000000FF);
result[5] = (byte)((TargetTimeVeryLowBatt >> 24) & 0x000000FF);
result[6] = (byte)(TargetTimeLowBatt & 0x000000FF);
result[7] = (byte)((TargetTimeLowBatt >> 8) & 0x000000FF);
result[8] = (byte)((TargetTimeLowBatt >> 16) & 0x000000FF);
result[9] = (byte)((TargetTimeLowBatt >> 24) & 0x000000FF);
result[10] = (byte)(TestModeTime & 0x000000FF);
result[11] = (byte)((TestModeTime >> 8) & 0x000000FF);
result[12] = (byte)((TestModeTime >> 16) & 0x000000FF);
result[13] = (byte)((TestModeTime >> 24) & 0x000000FF);
result[14] = (byte)(EmptyPipeThreshold & 0x00FF);
result[15] = (byte)((EmptyPipeThreshold >> 8) & 0x00FF);
result[16] = PCBNumber[0];
result[17] = PCBNumber[1];
result[18] = PCBNumber[2];
result[19] = PCBNumber[3];
result[20] = PCBNumber[4];
result[21] = TestModeConfig;
result[22] = (byte)(RadioAddress & 0x000000FF);
result[23] = (byte)((RadioAddress >> 8) & 0x000000FF);
result[24] = (byte)((RadioAddress >> 16) & 0x000000FF);
result[25] = (byte)((RadioAddress >> 24) & 0x000000FF);
result[26] = (byte)(TempCalibration & 0x00FF);
result[27] = (byte)((TempCalibration >> 8) & 0x00FF);
result[28] = (byte)(AlarmMask & 0x00FF);
result[29] = (byte)((AlarmMask >> 8) & 0x00FF);
result[30] = (byte)(ConfigCheckSum & 0x00FF);
result[31] = (byte)((ConfigCheckSum >> 8) & 0x00FF);
return result;
}
/// <summary>
/// Create a configuration structure from a complete byte array
/// </summary>
/// <param name="data">A complete byte array data</param>
/// <returns>ConfigStruct or null when byte array was not complete</returns>
public static ConfigStruct FromByteArray(byte[] data)
{
if (data.Length != Length) return null;
ConfigStruct result = new ConfigStruct();
result.Version = data[0];
result.MeterState = (MeterState)data[1];
result.TargetTimeVeryLowBatt = (((UInt32)data[5] * 256 + data[4]) * 256 + data[3]) * 256 + data[2];
result.TargetTimeLowBatt = (((UInt32)data[9] * 256 + data[8]) * 256 + data[7]) * 256 + data[6];
result.TestModeTime = (((UInt32)data[13] * 256 + data[12]) * 256 + data[11]) * 256 + data[10];
result.EmptyPipeThreshold = (UInt16)(data[15] * 256 + data[14]);
result.PCBNumber[0] = data[16];
result.PCBNumber[1] = data[17];
result.PCBNumber[2] = data[18];
result.PCBNumber[3] = data[19];
result.PCBNumber[4] = data[20];
result.TestModeConfig = data[21];
result.RadioAddress = (((UInt32)data[25] * 256 + data[24]) * 256 + data[23]) * 256 + data[22];
result.TempCalibration = (UInt16)(data[27] * 256 + data[26]);
result.AlarmMask = (UInt16)(data[29] * 256 + data[28]);
result.ConfigCheckSum = (UInt16)(data[31] * 256 + data[30]);
return result;
}
/// <summary>
/// Update the configuration structure from an incomplete byte array
/// </summary>
/// <param name="offset">Offset of byte array data in ConfigStruct</param>
/// <param name="data">Byte array data</param>
/// <returns>true when successful, false when data are not appropriate</returns>
public bool Update(int offset, byte[] data)
{
if (offset == 0 && data.Length == 2)
{
/// iPerl mode of function
Version = data[0];
MeterState = (MeterState)data[1];
return true;
}
else if (offset == 0 && data.Length == 4)
{
/// iPerl mode of function and extra 2 bytes
Version = data[0];
MeterState = (MeterState)data[1];
return true;
}
else if (offset == 21 && data.Length == 1)
{
/// TestModeConfig value
TestModeConfig = data[21 - offset];
return true;
}
else if (offset == 0 && data.Length == Length)
{
/// Complete ConfigStruct
Version = data[0];
MeterState = (MeterState)data[1];
TargetTimeVeryLowBatt = (((UInt32)data[5] * 256 + data[4]) * 256 + data[3]) * 256 + data[2];
TargetTimeLowBatt = (((UInt32)data[9] * 256 + data[8]) * 256 + data[7]) * 256 + data[6];
TestModeTime = (((UInt32)data[13] * 256 + data[12]) * 256 + data[11]) * 256 + data[10];
EmptyPipeThreshold = (UInt16)(data[15] * 256 + data[14]);
PCBNumber[0] = data[16];
PCBNumber[1] = data[17];
PCBNumber[2] = data[18];
PCBNumber[3] = data[19];
PCBNumber[4] = data[20];
TestModeConfig = data[21];
RadioAddress = (((UInt32)data[25] * 256 + data[24]) * 256 + data[23]) * 256 + data[22];
TempCalibration = (UInt16)(data[27] * 256 + data[26]);
AlarmMask = (UInt16)(data[29] * 256 + data[28]);
ConfigCheckSum = (UInt16)(data[31] * 256 + data[30]);
return true;
}
else
return false;
}
/// <summary>
/// Returns PCB number string (12 characters, 12 decimal digits)
/// </summary>
/// <returns>PCB number STRING</returns>
public string GetPcbNrString()
{
return PCBNumber2String(this.PCBNumber);
}
/// <summary>
/// Converts PCBNumber to string (12 characters, 12 decimal digits)
/// </summary>
/// <param name="pcbNumber"></param>
/// <returns>PCB number string</returns>
public static string PCBNumber2String(byte[] pcbNumber)
{
if (pcbNumber.Length != 5) return string.Empty;
Int64 number = 0;
for (int i = 4; i >= 0; i--)
{
number = 256 * number + (Int64)pcbNumber[i];
}
return number.ToString();
}
public override string ToString()
{
return string.Format("Config: V{0} State={1} VLoBattT={2}s LoBattT={3}s TestModeT={4}s EPThld={5} PCB#={6} TMCfg={7} RadioAddr={8} TempCalib={9} AlarmMask={10} CfgCheckSum={11}",
Version,
MeterState,
TargetTimeVeryLowBatt,
TargetTimeLowBatt,
TestModeTime,
EmptyPipeThreshold,
GetPcbNrString(),
TestModeConfig.ToString("X2"),
RadioAddress,
TempCalibration,
AlarmMask.ToString("X4"),
ConfigCheckSum.ToString("X4"));
}
public string ToString(int sel)
{
return string.Format("{1} PCB#={6} TMCfg={7}",
Version,
MeterState,
TargetTimeVeryLowBatt,
TargetTimeLowBatt,
TestModeTime,
EmptyPipeThreshold,
GetPcbNrString(),
TestModeConfig.ToString("X2"),
RadioAddress,
TempCalibration,
AlarmMask.ToString("X4"),
ConfigCheckSum.ToString("X4"));
}
public virtual void WriteBinary(BinaryWriter writer)
{
writer.Write(Version);
writer.Write((byte)MeterState);
writer.Write(TargetTimeVeryLowBatt);
writer.Write(TargetTimeLowBatt);
writer.Write(TestModeTime);
writer.Write(EmptyPipeThreshold);
writer.Write(PCBNumber[0]);
writer.Write(PCBNumber[1]);
writer.Write(PCBNumber[2]);
writer.Write(PCBNumber[3]);
writer.Write(PCBNumber[4]);
writer.Write(TestModeConfig);
writer.Write(RadioAddress);
writer.Write(TempCalibration);
writer.Write(AlarmMask);
writer.Write(ConfigCheckSum);
}
public virtual void ReadBinary(BinaryReader reader)
{
Version = reader.ReadByte();
MeterState = (MeterState)reader.ReadByte();
TargetTimeVeryLowBatt = reader.ReadUInt32();
TargetTimeLowBatt = reader.ReadUInt32();
TestModeTime = reader.ReadUInt32();
EmptyPipeThreshold = reader.ReadUInt16();
PCBNumber[0] = reader.ReadByte();
PCBNumber[1] = reader.ReadByte();
PCBNumber[2] = reader.ReadByte();
PCBNumber[3] = reader.ReadByte();
PCBNumber[4] = reader.ReadByte();
TestModeConfig = reader.ReadByte();
RadioAddress = reader.ReadUInt32();
TempCalibration = reader.ReadUInt16();
AlarmMask = reader.ReadUInt16();
ConfigCheckSum = reader.ReadUInt16();
}
}
}
@@ -0,0 +1,116 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public enum MessageID
{
Calibration = 0x00, /// Access to stCalibration
Configuration = 0x01, /// Access to stConfig
Status = 0x02, /// Access to stStaus, read only
Power = 0x03, /// Access to stPower, containing power info from both processors
LCD = 0x04, /// Access to stLCD
EventData = 0x05, /// NOT USED
IntervalData = 0x06, /// NOT USED
Diagnostics = 0x07, /// Access tostMetroDiagArray, read onl
MetrologyMemory = 0x08, /// Memory block access, read only
Error_LongAck = 0x09, /// Error message
Command = 0x0A, /// Command to execute, with no arguments
Parameterizing = 0x0B, /// Parameterizing message is used in MCI-SPI interface only
Error_ShortAck = 0x0C, /// Short acknowledge message is used in MCI-SPI interface only
ChanelAlive = 0x0D, /// Channel alive message is used in MCI-SPI interface only
RadioPassthrough = 0x0E, /// RFID <-> Metrology <-> Radio passthrough message
ASICRegisterReadTest = 0x0F, /// ASIC Register read test
IMIDebugMessagesAccess = 0x10, /// IMI debug messages read test
ProductionChecksumsRead = 0x11, /// Production checksum read: Calibration (1 byte), Configuration (2 bytes), spare (4 bytes)
HardwareParametersTest = 0x12, /// Hardware Parameters Testing: User configurable fixed field drive time (1 byte)
/// <summary>
/// Notes:
/// 1. Short Ack message contains 1-byte error code and all the fields (Offset, Payload length, payload and password) will not be present.
/// 2. Channel Alive message does not contain the fields (Offset, Payload length, payload and password).
/// 3. Except the above two special messages, rest all the messages in the above table will follow the message format mentioned in sections 3.1 and 3.2.
/// </summary>
Count /// Number of MessageID-s
}
public enum MeterType : byte
{
DN15 = 0,
CoaxManifold = 1,
DN20 = 2,
DN25 = 3,
DN26 = 4, /// DN25*
DN32 = 5,
DN40 = 6,
AutoDetect,
Count /// Number of meter types
}
public enum VolumeUnits : byte
{
m3 = 0,
UK_gallon = 1,
US_gallon = 2,
Count /// Number of volume units
}
public enum FlowArrow : byte
{
No = 0,
Right = 1,
Left = 2,
Count /// Number of flow arrows
}
public enum MeterSealed : byte
{
InProduction = 0x00,
OutOfProduction = 0xA5,
Sealed = 0x5A,
}
public enum MeterState : byte
{
None = 0,
Idle = 1,
Active = 2,
Test = 3,
EndOfLife = 4,
Count /// Number of meter states
}
public enum Command : byte
{
SetActiveMode = 6,
SetTestMode = 7,
}
public enum FlowState : byte
{
No = 0,
Reverse = 1,
Forward = 2,
EmptyPipe = 3,
Count /// Number of flow states
}
public enum OptoTelegramFlags : byte
{
OK = 0,
OK_TestStart,
OK_TestEnd,
InvalidTelegram, /// Wrong telegram format of checksum error
SyncError,
}
public enum OptoState
{
Read,
Flush,
}
}
@@ -0,0 +1,27 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System.Collections.Generic;
using TBF.Rig.Generic;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class Factory : IComponentFactory
{
public string ClassName { get { return "RegisterReader for iPerl"; } }
public override string ToString() { return ClassName; }
public void ResetStaticProperties() { IperlHead.ResetStaticProperties(); }
public IComponent DummyComponent() { return new IperlHead(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new IperlHead(cfg); }
public IComponentCfg DefaultConfig() { return new IperlHeadCfg(this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(IperlHeadCfg.Serializer, component, this);
}
}
}
@@ -0,0 +1,149 @@
///
/// Copyright (c) 2018-2019 Sensus Slovensko a.s.
///
using System;
using log4net;
using Config.Entities;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class FlowDirectionDetection
{
private static readonly ILog log = LogManager.GetLogger(typeof(IperlHead));
const int FIFO_SIZE = 64; /// 8 sec. @ 8Hz
const double MAX_OPTO_DROPOUT = 4.5; /// sec.
Int64[] volumeRawFifo; /// Volume FIFO buffer
Int64[] timestampFifo; /// Timestamp FIFO buffer
int fifoCount; /// Number of valid FIFO items
int fifoIx; /// Index of the next FIFO item
DateTime lastFifoWriteTime; /// Time of the last write to FIFO
///
/// Sums for linear regression calculation
///
decimal sumXX;
decimal sumX;
decimal sumXY;
decimal sumY;
decimal N;
double minSlope; /// max. slope of the regressed line, always positive or 0
double maxSlope; /// min. slope of the regressed line, always negative or 0
public FlowDirectionDetection()
{
volumeRawFifo = new Int64[FIFO_SIZE];
timestampFifo = new Int64[FIFO_SIZE];
ClearFifo();
}
/// <summary>
/// Clear FIFO data
/// </summary>
public void ClearFifo()
{
fifoCount = 0;
fifoIx = 0;
lastFifoWriteTime = DateTime.MinValue;
sumXX = 0;
sumX = 0;
sumXY = 0;
sumY = 0;
N = 0;
minSlope = 0;
maxSlope = 0;
}
/// <summary>
/// Write data to FIFO
/// </summary>
/// <param name="volumeRaw">Volume</param>
/// <param name="timestamp">Time stamp</param>
public void WriteToFifo(Int64 volumeRaw, Int64 timestamp)
{
///
/// Update sums for linear regression calculation
///
if (fifoCount == FIFO_SIZE)
{
/// Buffer is already full, the oldest item will be re-written
sumXX -= timestampFifo[fifoIx] * timestampFifo[fifoIx];
sumX -= timestampFifo[fifoIx];
sumXY -= timestampFifo[fifoIx] * volumeRawFifo[fifoIx];
sumY -= volumeRawFifo[fifoIx];
N--;
}
sumXX += timestamp * timestamp;
sumX += timestamp;
sumXY += timestamp * volumeRaw;
sumY += volumeRaw;
N++;
///
/// Save new values to FIFO
///
volumeRawFifo[fifoIx] = volumeRaw;
timestampFifo[fifoIx] = timestamp;
fifoIx = (fifoIx + 1) % FIFO_SIZE;
fifoCount = Math.Min(fifoCount + 1, FIFO_SIZE);
lastFifoWriteTime = DateTime.Now;
}
/// <summary>
/// Determine whether there are enough recent FIFO data
/// </summary>
/// <returns>true when data valid</returns>
public bool AreFifoDataValid()
{
return (DateTime.Now.Subtract(lastFifoWriteTime).TotalSeconds <= MAX_OPTO_DROPOUT) && (fifoCount == FIFO_SIZE);
}
/// <summary>
/// Verify whether the flow direction is correct
/// </summary>
/// <returns>OptoHeadState.OptoAndDirOK, OptoHeadState.OptoNok or OptoHeadState.DirNok</returns>
public OptoHeadState CheckFlowDirection(Counting counting, string iPerlHeadName)
{
if (!AreFifoDataValid()) return OptoHeadState.OptoNok;
try
{
decimal numer = N * sumXY - sumX * sumY;
decimal denom = N * sumXX - sumX * sumX;
if (denom == 0) return OptoHeadState.DirNok;
/// Calculate the slope of the regressed line, determine min. and max.
double slope = (double)(numer / denom);
if (slope > maxSlope) maxSlope = slope;
if (slope < minSlope) minSlope = slope;
if ( counting == Counting.Arbitrary ||
(counting == Counting.Positive && maxSlope > Math.Abs(2 * minSlope)) ||
(counting == Counting.Negative && minSlope < -Math.Abs(2 * maxSlope)))
{
return OptoHeadState.OptoAndDirOK;
}
else
{
return OptoHeadState.DirNok;
}
}
catch (Exception)
{
log.ErrorFormat("{0} : CheckFlowDirection() failed", iPerlHeadName);
return OptoHeadState.DirNok; /// ???
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Xml.Serialization;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.Resources;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class IperlHeadCfg : ComponentCfgBase, Generic.IComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(IperlHeadCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) { return new IperlHeadCfgCtrl(); }
///
/// Serialized parameters
///
public bool UseTcpIP;
public string OptoIPAddress;
public ushort OptoTcpipPortNr;
public int OptoComPortNr;
public int RfidComPortNr; /// 0 = use MuxBoardNr
public int MuxBoardNr; /// 0 = use RfidComPort(Nr), otherwise mux. board nr. 1 .. 4
public int Group; /// Number written to QuidoRS to connct the watermeter to RfidComPort, 1 .. 10
/// <summary> Procedure parameters </summary>
[XmlIgnore]
public ProcParams ProcParams;
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
[XmlIgnore]
public MeterType MeterType { get { return (ProcParams != null) ? ProcParams.MeterType : MeterType.AutoDetect; } }
/// Private parameterless constructor invoked by all other (public) constructors
IperlHeadCfg()
{
Name = "iPerl";
ParentName = string.Empty;
OptoComPortNr = 10;
RfidComPortNr = 0; /// = use mux. board
MuxBoardNr = 1;
ProcParams = CreateProcParamsProvider() as ProcParams;
}
public IperlHeadCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("{0} Group1 (mux#)={1}, Group2={2}, Opto=Com{3}, RFID=Com{4}",
Name,
MuxBoardNr,
Group,
OptoComPortNr,
RfidComPortNr);
}
}
}
@@ -0,0 +1,150 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.Resources;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public partial class IperlHeadCfgCtrl : UserControl, IComponentCfgCtrl
{
public bool ShowMore { get { return false; } }
IperlHeadCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as IperlHeadCfg;
Redraw();
}
}
public IperlHeadCfgCtrl()
{
InitializeComponent();
}
private void WaterMeterCfgCtrl_Load(object sender, EventArgs e)
{
nameLabel.Text = Strings.Name;
classNameLabel.Text = config.Factory.ClassName;
Redraw();
}
public void Closing()
{
}
void Redraw()
{
if (config == null) return; /// Control was not loaded, settings were not changed
nameTextBox.Text = config.Name;
radioButton1.Checked = config.UseTcpIP;
radioButton2.Checked = !config.UseTcpIP;
ipAddressTextBox.Text = (config.OptoIPAddress != null) ? config.OptoIPAddress : "0.0.0.0";
tcpipPortTextBox.Text = config.OptoTcpipPortNr.ToString();
optoSerialPortTextBox.Text = config.OptoComPortNr.ToString();
rfidPortNrTextBox.Text = config.RfidComPortNr.ToString();
muxBoardNrTextBox.Text = config.MuxBoardNr.ToString();
groupTextBox.Text = config.Group.ToString();
}
public void Unlock()
{
nameTextBox.Enabled = true;
radioButton1.Enabled = true;
radioButton2.Enabled = true;
ipAddressTextBox.Enabled = true;
tcpipPortTextBox.Enabled = true;
optoSerialPortTextBox.Enabled = true;
rfidPortNrTextBox.Enabled = true;
muxBoardNrTextBox.Enabled = true;
groupTextBox.Enabled = true;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (radioButton1.Checked)
{
IPAddress dummyIPAddress;
if (!IPAddress.TryParse(ipAddressTextBox.Text, out dummyIPAddress))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'IP address' is not valid";
}
ushort sdummy;
if (!ushort.TryParse(tcpipPortTextBox.Text, out sdummy))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'TCP/IP port nr.' is not valid";
}
}
else
{
if (!int.TryParse(optoSerialPortTextBox.Text, out dummy) || dummy < 1 || dummy > 999)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Opto serial port nr.' is not valid";
}
}
if (!int.TryParse(rfidPortNrTextBox.Text, out dummy) || dummy < 0 || dummy > 999)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'RFID serial port nr.' is not valid";
}
if (!int.TryParse(muxBoardNrTextBox.Text, out dummy) || dummy < 1 || dummy > 4)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + string.Format(Strings.Invalid_0, muxBoardNrLabel.Text);
}
if (!int.TryParse(groupTextBox.Text, out dummy) || dummy < 1 || dummy > 10)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + string.Format(Strings.Invalid_0, groupLabel.Text);
}
return flags;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
config.Name = nameTextBox.Text;
if (radioButton1.Checked)
{
config.UseTcpIP = true;
config.OptoIPAddress = ipAddressTextBox.Text;
config.OptoTcpipPortNr = ushort.Parse(tcpipPortTextBox.Text);
}
else
{
config.UseTcpIP = false;
config.OptoComPortNr = int.Parse(optoSerialPortTextBox.Text);
}
config.RfidComPortNr = int.Parse(rfidPortNrTextBox.Text);
config.MuxBoardNr = int.Parse(muxBoardNrTextBox.Text);
config.Group = int.Parse(groupTextBox.Text);
return flags;
}
}
}
@@ -0,0 +1,308 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
partial class IperlHeadCfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.optoSerialPortTextBox = new System.Windows.Forms.TextBox();
this.optoSerialPortLabel = new System.Windows.Forms.Label();
this.muxBoardNrTextBox = new System.Windows.Forms.TextBox();
this.muxBoardNrLabel = new System.Windows.Forms.Label();
this.groupTextBox = new System.Windows.Forms.TextBox();
this.groupLabel = new System.Windows.Forms.Label();
this.rfidPortNrTextBox = new System.Windows.Forms.TextBox();
this.rfidSerialPortNrLabel = new System.Windows.Forms.Label();
this.radioButton1 = new System.Windows.Forms.RadioButton();
this.radioButton2 = new System.Windows.Forms.RadioButton();
this.optoDataGroupBox = new System.Windows.Forms.GroupBox();
this.tcpipPortLabel = new System.Windows.Forms.Label();
this.tcpipPortTextBox = new System.Windows.Forms.TextBox();
this.ipAddressLabel = new System.Windows.Forms.Label();
this.ipAddressTextBox = new System.Windows.Forms.TextBox();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.optoDataGroupBox.SuspendLayout();
this.groupBox1.SuspendLayout();
this.SuspendLayout();
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(135, 40);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(121, 20);
this.nameTextBox.TabIndex = 2;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(25, 43);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 1;
this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(132, 16);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(60, 13);
this.classNameLabel.TabIndex = 0;
this.classNameLabel.Text = "ClassName";
//
// optoSerialPortTextBox
//
this.optoSerialPortTextBox.Enabled = false;
this.optoSerialPortTextBox.Location = new System.Drawing.Point(329, 42);
this.optoSerialPortTextBox.Name = "optoSerialPortTextBox";
this.optoSerialPortTextBox.Size = new System.Drawing.Size(34, 20);
this.optoSerialPortTextBox.TabIndex = 7;
//
// optoSerialPortLabel
//
this.optoSerialPortLabel.AutoSize = true;
this.optoSerialPortLabel.Location = new System.Drawing.Point(241, 45);
this.optoSerialPortLabel.Name = "optoSerialPortLabel";
this.optoSerialPortLabel.Size = new System.Drawing.Size(72, 13);
this.optoSerialPortLabel.TabIndex = 6;
this.optoSerialPortLabel.Text = "Serial port nr.:";
//
// muxBoardNrTextBox
//
this.muxBoardNrTextBox.Enabled = false;
this.muxBoardNrTextBox.Location = new System.Drawing.Point(135, 63);
this.muxBoardNrTextBox.Name = "muxBoardNrTextBox";
this.muxBoardNrTextBox.Size = new System.Drawing.Size(34, 20);
this.muxBoardNrTextBox.TabIndex = 9;
//
// muxBoardNrLabel
//
this.muxBoardNrLabel.AutoSize = true;
this.muxBoardNrLabel.Location = new System.Drawing.Point(25, 66);
this.muxBoardNrLabel.Name = "muxBoardNrLabel";
this.muxBoardNrLabel.Size = new System.Drawing.Size(106, 13);
this.muxBoardNrLabel.TabIndex = 8;
this.muxBoardNrLabel.Text = "Group 1 (mux. board)";
//
// groupTextBox
//
this.groupTextBox.Enabled = false;
this.groupTextBox.Location = new System.Drawing.Point(135, 86);
this.groupTextBox.Name = "groupTextBox";
this.groupTextBox.Size = new System.Drawing.Size(34, 20);
this.groupTextBox.TabIndex = 11;
//
// groupLabel
//
this.groupLabel.AutoSize = true;
this.groupLabel.Location = new System.Drawing.Point(25, 89);
this.groupLabel.Name = "groupLabel";
this.groupLabel.Size = new System.Drawing.Size(45, 13);
this.groupLabel.TabIndex = 10;
this.groupLabel.Text = "Group 2";
//
// rfidPortNrTextBox
//
this.rfidPortNrTextBox.Enabled = false;
this.rfidPortNrTextBox.Location = new System.Drawing.Point(329, 21);
this.rfidPortNrTextBox.Name = "rfidPortNrTextBox";
this.rfidPortNrTextBox.Size = new System.Drawing.Size(34, 20);
this.rfidPortNrTextBox.TabIndex = 7;
//
// rfidSerialPortNrLabel
//
this.rfidSerialPortNrLabel.AutoSize = true;
this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(241, 24);
this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel";
this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(72, 13);
this.rfidSerialPortNrLabel.TabIndex = 6;
this.rfidSerialPortNrLabel.Text = "Serial port nr.:";
//
// radioButton1
//
this.radioButton1.AutoSize = true;
this.radioButton1.Enabled = false;
this.radioButton1.Location = new System.Drawing.Point(22, 19);
this.radioButton1.Name = "radioButton1";
this.radioButton1.Size = new System.Drawing.Size(83, 17);
this.radioButton1.TabIndex = 0;
this.radioButton1.TabStop = true;
this.radioButton1.Text = "Use TCP/IP";
this.radioButton1.UseVisualStyleBackColor = true;
//
// radioButton2
//
this.radioButton2.AutoSize = true;
this.radioButton2.Enabled = false;
this.radioButton2.Location = new System.Drawing.Point(234, 19);
this.radioButton2.Name = "radioButton2";
this.radioButton2.Size = new System.Drawing.Size(92, 17);
this.radioButton2.TabIndex = 1;
this.radioButton2.TabStop = true;
this.radioButton2.Text = "Use serial port";
this.radioButton2.UseVisualStyleBackColor = true;
//
// optoDataGroupBox
//
this.optoDataGroupBox.Controls.Add(this.tcpipPortLabel);
this.optoDataGroupBox.Controls.Add(this.tcpipPortTextBox);
this.optoDataGroupBox.Controls.Add(this.ipAddressLabel);
this.optoDataGroupBox.Controls.Add(this.ipAddressTextBox);
this.optoDataGroupBox.Controls.Add(this.radioButton1);
this.optoDataGroupBox.Controls.Add(this.radioButton2);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortLabel);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortTextBox);
this.optoDataGroupBox.Location = new System.Drawing.Point(28, 114);
this.optoDataGroupBox.Name = "optoDataGroupBox";
this.optoDataGroupBox.Size = new System.Drawing.Size(414, 97);
this.optoDataGroupBox.TabIndex = 5;
this.optoDataGroupBox.TabStop = false;
this.optoDataGroupBox.Text = "Opto-data";
//
// tcpipPortLabel
//
this.tcpipPortLabel.AutoSize = true;
this.tcpipPortLabel.Location = new System.Drawing.Point(31, 71);
this.tcpipPortLabel.Name = "tcpipPortLabel";
this.tcpipPortLabel.Size = new System.Drawing.Size(47, 13);
this.tcpipPortLabel.TabIndex = 4;
this.tcpipPortLabel.Text = "Port nr..:";
//
// tcpipPortTextBox
//
this.tcpipPortTextBox.Enabled = false;
this.tcpipPortTextBox.Location = new System.Drawing.Point(107, 68);
this.tcpipPortTextBox.Name = "tcpipPortTextBox";
this.tcpipPortTextBox.Size = new System.Drawing.Size(39, 20);
this.tcpipPortTextBox.TabIndex = 5;
//
// ipAddressLabel
//
this.ipAddressLabel.AutoSize = true;
this.ipAddressLabel.Location = new System.Drawing.Point(31, 48);
this.ipAddressLabel.Name = "ipAddressLabel";
this.ipAddressLabel.Size = new System.Drawing.Size(63, 13);
this.ipAddressLabel.TabIndex = 2;
this.ipAddressLabel.Text = "IP address.:";
//
// ipAddressTextBox
//
this.ipAddressTextBox.Enabled = false;
this.ipAddressTextBox.Location = new System.Drawing.Point(107, 45);
this.ipAddressTextBox.Name = "ipAddressTextBox";
this.ipAddressTextBox.Size = new System.Drawing.Size(98, 20);
this.ipAddressTextBox.TabIndex = 3;
//
// groupBox1
//
this.groupBox1.Controls.Add(this.rfidPortNrTextBox);
this.groupBox1.Controls.Add(this.rfidSerialPortNrLabel);
this.groupBox1.Location = new System.Drawing.Point(28, 218);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(414, 55);
this.groupBox1.TabIndex = 12;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "RFID communication (in case mux. board is not used)";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(176, 66);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(31, 13);
this.label3.TabIndex = 13;
this.label3.Text = "1 .. 4";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(176, 89);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(37, 13);
this.label4.TabIndex = 14;
this.label4.Text = "1 .. 10";
//
// WaterMeterCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.optoDataGroupBox);
this.Controls.Add(this.groupTextBox);
this.Controls.Add(this.groupLabel);
this.Controls.Add(this.muxBoardNrTextBox);
this.Controls.Add(this.muxBoardNrLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Name = "WaterMeterCfgCtrl";
this.Size = new System.Drawing.Size(500, 300);
this.Load += new System.EventHandler(this.WaterMeterCfgCtrl_Load);
this.optoDataGroupBox.ResumeLayout(false);
this.optoDataGroupBox.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.TextBox optoSerialPortTextBox;
private System.Windows.Forms.Label optoSerialPortLabel;
private System.Windows.Forms.TextBox muxBoardNrTextBox;
private System.Windows.Forms.Label muxBoardNrLabel;
private System.Windows.Forms.TextBox groupTextBox;
private System.Windows.Forms.Label groupLabel;
private System.Windows.Forms.TextBox rfidPortNrTextBox;
private System.Windows.Forms.Label rfidSerialPortNrLabel;
private System.Windows.Forms.RadioButton radioButton1;
private System.Windows.Forms.RadioButton radioButton2;
private System.Windows.Forms.GroupBox optoDataGroupBox;
private System.Windows.Forms.Label tcpipPortLabel;
private System.Windows.Forms.TextBox tcpipPortTextBox;
private System.Windows.Forms.Label ipAddressLabel;
private System.Windows.Forms.TextBox ipAddressTextBox;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,20 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class OptoReceivedEventArgs : EventArgs
{
public string Data;
public OptoReceivedEventArgs(string data)
{
this.Data = data;
}
}
}
@@ -0,0 +1,320 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Globalization;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class OptoTelegramRaw
{
public static readonly int Length = 42;
private static CultureInfo culture;
///
/// Strobed value
///
public static decimal TestStartTimestampDec;
///
/// Stored values
///
public OptoTelegramFlags Flags;
public DateTime DateTime; /// From PC
public float RefFlow; /// [m3/h]
public int Counter;
public UInt32 EmfRaw; /// From iPerl opto data
public Int16 MagneticFieldRaw;
public Int16 FlowRaw;
public UInt32 VolumeRaw;
public Int64 VolumeRawExt;
public Int16 Impedance;
public UInt32 Timestamp;
public Int64 TimestampExt;
public byte CheckSum;
///
/// Calculated values
///
public double EMF()
{
Int32 signedEmf = (EmfRaw > 0x7FFFFF) ? ((int)EmfRaw - 0x1000000) : (int)EmfRaw;
return 0.000000333 * (double)signedEmf;
}
public double MagneticField() { return (double)MagneticFieldRaw; }
public double Flow(double scalingFactor) { return 0.225 * scalingFactor * (double)FlowRaw; }
public double Volume(double scalingFactor) { return 0.0000625 * scalingFactor * (double)VolumeRawExt; }
public Int32 FlipTime() { return Impedance; }
public decimal TimestampDec() { return (decimal)TimestampExt / (decimal)8192; }
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous) { return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor); }
public decimal TimeDelta() { return TimestampDec() - TestStartTimestampDec; }
public string Label()
{
if (Flags == OptoTelegramFlags.OK_TestStart) return "#### start test ####";
else if (Flags == OptoTelegramFlags.OK_TestEnd) return "#### end of test ####";
else return string.Empty;
}
static OptoTelegramRaw()
{
culture = CultureInfo.CreateSpecificCulture("DE"); /// This is to use comma as decimal number separator
}
public OptoTelegramRaw()
{
}
/// <summary>
/// Parses optical telegram and returns OptoTelegramRaw object
/// </summary>
/// <description>
/// Create a configuration structure from a complete byte array
///
/// Telegram description:
///
/// AAAAAA[tab]BBBB[tab]CCCC[tab]DDDDDD[tab]EEEE[tab]FFFFFFFF[tab]GG[cr][lf] (42 bytes)
///
/// Data Comment Type Calculate to decimal
/// ----------------------------------------------------------------
/// AAAAAA EMF Int24 Value * 0.000000333
/// BBBB Magnetic field Int16 Value
/// CCCC Flow Int16 Value * 0.225 * Scalig factor
/// DDDDDD Volume Int24 Value / 16000 * Scaling factor
/// EEEE Impedance Int16 Value
/// FFFFFFFF Timestamp Uint32 Value / 8192
/// GG Checksum Byte
/// ----------------------------------------------------------------
///
/// Example:
/// FFFFFE 51EA 0000 65324E 0087 F6319DFF 86
/// FFDD3A 51F9 0000 65324E 0088 F631A60B 45
/// ...
/// </description>
/// <param name="data">A complete byte array data</param>
/// <returns>true = telegram OK, false = telegram NOK</returns>
public bool UpdateFromString(string telegram, int counter, ref Int64 volumeRawExtLast, ref Int64 timestampExtLast)
{
DateTime = DateTime.Now;
Counter = counter;
RefFlow = (float)Sequences.ProcessData.RefFlow.Val;
if ((telegram == null) || (telegram.Length < Length) ||
(telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') ||
(telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') ||
(telegram[40] != '\r') || (telegram[41] != '\n'))
{
Flags = OptoTelegramFlags.InvalidTelegram;
return false;
}
bool f1 = UInt32.TryParse(telegram.Substring(0, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out EmfRaw);
bool f2 = Int16.TryParse(telegram.Substring(7, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out MagneticFieldRaw);
bool f3 = Int16.TryParse(telegram.Substring(12, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out FlowRaw);
bool f4 = UInt32.TryParse(telegram.Substring(17, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw);
bool f5 = Int16.TryParse(telegram.Substring(24, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Impedance);
bool f6 = UInt32.TryParse(telegram.Substring(29, 8), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp);
bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum);
bool allOk = f1 && f2 && f3 && f4 && f5 && f6 && f7;
Flags = allOk ? OptoTelegramFlags.OK : OptoTelegramFlags.InvalidTelegram;
///
/// Cope with 'VolumeRaw' overflow
///
Int64 uncorrected = (Int64)(((UInt64)volumeRawExtLast & 0xFFFFFFFFFF000000UL) | VolumeRaw);
if (Math.Abs(uncorrected - volumeRawExtLast) <= 0x800000L)
{
VolumeRawExt = volumeRawExtLast = uncorrected;
}
else if (Math.Abs(uncorrected + 0x1000000L - volumeRawExtLast) <= 0x800000L)
{
VolumeRawExt = volumeRawExtLast = uncorrected + 0x1000000L;
}
else if (Math.Abs(uncorrected - 0x1000000L - volumeRawExtLast) <= 0x800000L)
{
VolumeRawExt = volumeRawExtLast = uncorrected - 0x1000000L;
}
else
{
VolumeRawExt = volumeRawExtLast = uncorrected;
}
///
/// Cope with 'Timestamp' overflow
///
uncorrected = (Int64)(((UInt64)timestampExtLast & 0xFFFFFFFF00000000UL) | Timestamp);
if (Math.Abs(uncorrected - timestampExtLast) <= 0x80000000L)
{
TimestampExt = timestampExtLast = uncorrected;
}
else if (Math.Abs(uncorrected + 0x100000000L - timestampExtLast) <= 0x80000000L)
{
TimestampExt = timestampExtLast = uncorrected + 0x100000000L;
}
else if (Math.Abs(uncorrected - 0x100000000L - timestampExtLast) <= 0x80000000L)
{
TimestampExt = timestampExtLast = uncorrected - 0x100000000L;
}
else
{
TimestampExt = timestampExtLast = uncorrected;
}
return allOk;
}
/// <summary>
/// Alternative to UpdateFromString(...) when data are flushed
/// </summary>
public bool UpdateFromStringDummy(string telegram)
{
DateTime = DateTime.Now;
RefFlow = (float)Sequences.ProcessData.RefFlow.Val;
if ((telegram == null) || (telegram.Length < Length) ||
(telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') ||
(telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') ||
(telegram[40] != '\r') || (telegram[41] != '\n'))
{
Flags = OptoTelegramFlags.InvalidTelegram;
return false;
}
//bool f1 = UInt32.TryParse(telegram.Substring(0, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out EmfRaw);
//bool f2 = Int16.TryParse(telegram.Substring(7, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out MagneticFieldRaw);
//bool f3 = Int16.TryParse(telegram.Substring(12, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out FlowRaw);
//bool f4 = UInt32.TryParse(telegram.Substring(17, 6), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out VolumeRaw);
//bool f5 = Int16.TryParse(telegram.Substring(24, 4), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Impedance);
//bool f6 = UInt32.TryParse(telegram.Substring(29, 8), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out Timestamp);
//bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture, out CheckSum);
//return f1 && f2 && f3 && f4 && f5 && f6 && f7;
return true;
}
public void SetFlags(OptoTelegramFlags flags)
{
this.Flags = flags;
}
public string ToString(double scalingFactor, OptoTelegramRaw previous)
{
if (Flags == OptoTelegramFlags.SyncError)
{
return "Sychronization error";
}
else if (Flags == OptoTelegramFlags.InvalidTelegram)
{
return "Invalid telegram";
}
else /// if (flags == OptoTelegramFlags.OK / OptoTelegramFlags.OK_TestStart / OptoTelegramFlags.OK_TestEnd)
{
return string.Format("{0}:{1}:{2}.{3}\t{4} :\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\t{13}\t{14}\t{15}\t{16}\t{17}\t{18}\t{19}\t{20}\t{21}\t{22}",
/* 0 */ DateTime.Hour.ToString("D2"),
/* 1 */ DateTime.Minute.ToString("D2"),
/* 2 */ DateTime.Second.ToString("D2"),
/* 3 */ DateTime.Millisecond.ToString("D4"),
/* 4 */ Counter,
/* 5 */ EmfRaw.ToString("X6"),
/* 6 */ MagneticFieldRaw.ToString("X4"),
/* 7 */ FlowRaw.ToString("X4"),
/* 8 */ VolumeRaw.ToString("X6"),
/* 9 */ Impedance.ToString("X4"),
/* 10 */ Timestamp.ToString("X8"),
/* 11 */ CheckSum.ToString("X2"),
/* 12 */ EMF().ToString("F4", culture),
/* 13 */ MagneticField().ToString("F0", culture),
/* 14 */ Flow(scalingFactor).ToString("F2", culture),
/* 15 */ Volume(scalingFactor).ToString("F4", culture),
/* 16 */ FlipTime().ToString("F0", culture),
/* 17 */ TimestampDec().ToString("F4", culture),
/* 18 */ (RefFlow * 1000).ToString("F2", culture),
/* 19 */ VolumeDelta(scalingFactor, previous).ToString("F4", culture),
/* 20 */ TimeDelta().ToString("F3", culture),
/* 21 */ scalingFactor.ToString("F1", culture),
/* 22 */ Label());
}
}
/// <summary>
/// Filter RefFlow data in an array of OptoTelegramRaw objects by a FIR filter:
///
/// kSize = 5, kSize2 = 2
///
/// i k
/// ---------------------------------------------------------------------------
/// 0 -5 filtered[0] = data[0]
/// 1 -4 filtered[1] = data[1]
/// 2 -3 filtered[2] = data[0]*k[0] + ... + data[4]*k[4]
/// 3 -2 filtered[3] = data[1]*k[0] + ... + data[5]*k[4]
/// 4 -1 filtered[4] = data[2]*k[0] + ... + data[6]*k[4]
/// 5 0 data[0] = filtered[0], filtered[0] = data[3]*k[0] + ... + data[7]*k[4]
/// 6 1 data[1] = filtered[1], filtered[1] = data[4]*k[0] + ... + data[8]*k[4]
/// 7 ...
/// </summary>
/// <param name="optoData">array of OptoTelegramRaw objects</param>
/// <param name="from">Index of the first optoData item to process</param>
/// <param name="to">Index of the last optoData item to process</param>
public static void FIRFilterFlow(OptoTelegramRaw[] optoData, int from, int to)
{
float[] kernel = new float[] { 0.1f, 0.2f, 0.4f, 0.2f, 0.1f };
int kSize = kernel.Length;
int kSize2 = kernel.Length / 2;
float[] filtered = new float[kSize];
for (int i = from; i <= to; i++)
{
if ((i < from + kSize2) || (i > to - kSize2))
{
/// Beginning or end of optoData buffer => Just copy data (=do not filter)
filtered[i % kSize] = optoData[BufferIdx(i)].RefFlow;
}
else
{
/// Make a convolution of optoData and the kernel
float weightedSum = 0;
for (int j = -kSize2; j <= kSize2; j++)
weightedSum += optoData[BufferIdx(i + j)].RefFlow * kernel[j + kSize2];
filtered[i % kSize] = weightedSum;
}
if (i >= from + kSize)
{
/// filtered[] buffer full => copy filtered data to optoData
optoData[BufferIdx(i - kSize)].RefFlow = filtered[i % kSize];
}
}
for (int i = to - kSize + 1; i <= to; i++)
{
if (i >= 0) optoData[BufferIdx(i)].RefFlow = filtered[i % kSize];
}
}
/// <summary>
/// Get index to optoData buffer
/// </summary>
/// <param name="index">Original unwrapped index</param>
/// <returns>Index to the buffer</returns>
public static int BufferIdx(int index)
{
if (index < IperlHead.MaxOptoDataCount)
{
return index;
}
else
{
return IperlHead.StartOptoDataCount + (index - IperlHead.MaxOptoDataCount) % IperlHead.EndOptoDataCount;
}
}
}
}
@@ -0,0 +1,214 @@
///
/// Copyright (c) 2015-2019 Sensus Slovensko a.s.
///
using System;
using System.IO;
using System.Xml.Serialization;
using Config.Entities;
using TBF.Rig.Generic;
using System.Collections.Generic;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class ProcParams : ProcedureParamsBase, IParamsProvider, IProcedureParams
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(ProcParams) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public MeterType MeterType;
public float CalibTarget; /// Target error after calibration in [%]
public float CalibTargetQ2; /// Target error at Q2 after Q2 correction in [%]
public int FactorLimitLo; /// Lower limit for the calibration factor
public int FactorLimitHi; /// Upper limit for the calibration factor
public Counting Counting; /// Initial iPerl counting (Artbitrary, Positive or Negative)
public int WMType_ID; /// Required for Oracle DB: ID_WZTyp in table VT_PRUEFPUNKT_SOLL_SD
public int InitQ2CorrLR;
public int InitQ2CorrRL;
public override void InitializeAll()
{
MeterType = MeterType.AutoDetect;
CalibTarget = 0;
CalibTargetQ2 = 0;
FactorLimitLo = 1000;
FactorLimitHi = 8000;
Counting = Counting.Arbitrary;
WMType_ID = 2; /// Value for iPerl DN15
InitQ2CorrLR = 0;
InitQ2CorrRL = 0;
}
string[] paramNames = new string[]
{
"iPerl type",
"Calib. target [%]",
"Calib. target at Q2 [%]",
"Calib. factor Lo",
"Calib. factor Hi",
"Counting",
"WM type ID",
"Init. Q2 corr. LR",
"Init. Q2 corr. RL",
};
public override string ParamName(int i) { return paramNames[i]; }
public override int ParamsCount() { return paramNames.Length; }
public override ICollection<string> ParamValues(int i)
{
if (i == 0)
{
List<string> retVal = new List<string>();
for (MeterType mt = 0; mt < MeterType.Count; mt++) retVal.Add(mt.ToString());
return retVal;
}
else if (i == 5)
{
List<string> retVal = new List<string>();
for (Counting c = 0; c < Counting.Count; c++) retVal.Add(c.ToString());
return retVal;
}
return null;
}
public override string ToString(int i)
{
switch (i)
{
case 0: return MeterType.ToString();
case 1: return CalibTarget.ToString();
case 2: return CalibTargetQ2.ToString();
case 3: return FactorLimitLo.ToString();
case 4: return FactorLimitHi.ToString();
case 5: return Counting.ToString();
case 6: return WMType_ID.ToString();
case 7: return InitQ2CorrLR.ToString();
case 8: return InitQ2CorrRL.ToString();
default: return string.Empty;
}
}
public CfgUpdateFlags UpdateParam(int i, string strValue)
{
switch (i)
{
case 0:
for (MeterType mt = 0; mt < MeterType.Count; mt++)
{
if (mt.ToString().Equals(strValue)) { MeterType = mt; return CfgUpdateFlags.None; }
}
break;
case 1: CalibTarget = Utils.ParseSFloat(strValue); return CfgUpdateFlags.None;
case 2: CalibTargetQ2 = Utils.ParseSFloat(strValue); return CfgUpdateFlags.None;
case 3: FactorLimitLo = int.Parse(strValue); return CfgUpdateFlags.None;
case 4: FactorLimitHi = int.Parse(strValue); return CfgUpdateFlags.None;
case 5:
for (Counting c = 0; c < Counting.Count; c++)
{
if (c.ToString().Equals(strValue)) { Counting = c; return CfgUpdateFlags.None; }
}
break;
case 6: WMType_ID = int.Parse(strValue); return CfgUpdateFlags.None;
case 7: InitQ2CorrLR = int.Parse(strValue); return CfgUpdateFlags.None;
case 8: InitQ2CorrRL = int.Parse(strValue); return CfgUpdateFlags.None;
default: return CfgUpdateFlags.None;
}
return CfgUpdateFlags.None;
}
public bool ValidateParam(int i, string strValue, out string message)
{
message = string.Empty;
int iDummy;
float fDummy;
switch (i)
{
case 0:
for (MeterType mt = 0; mt < MeterType.Count; mt++) if (mt.ToString().Equals(strValue)) return true;
break;
case 1:
case 2:
if (Utils.TryParseSFloat(strValue, out fDummy) && fDummy >= -10.0f && fDummy <= 10.0f) return true;
break;
case 3:
case 4:
if (int.TryParse(strValue, out iDummy) && iDummy >= 1000 && iDummy <= 8000) return true;
break;
case 5:
for (Counting c = 0; c < Counting.Count; c++) if (c.ToString().Equals(strValue)) return true;
break;
case 6: /// WMType_ID
if (int.TryParse(strValue, out iDummy)) return true;
break;
case 7: /// InitQ2CorrLR
case 8: /// InitQ2CorrRL
if (int.TryParse(strValue, out iDummy) || (iDummy < -127) || (iDummy > 127)) return true;
break;
default:
message = "Invalid index";
return false;
}
message = ParamName(i) + " is invalid";
return false;
}
void CopyContentTo(ProcParams prms)
{
prms.MeterType = this.MeterType;
prms.CalibTarget = this.CalibTarget;
prms.CalibTargetQ2 = this.CalibTargetQ2;
prms.FactorLimitLo = this.FactorLimitLo;
prms.FactorLimitHi = this.FactorLimitHi;
prms.Counting = this.Counting;
prms.WMType_ID = this.WMType_ID;
prms.InitQ2CorrLR = this.InitQ2CorrLR;
prms.InitQ2CorrRL = this.InitQ2CorrRL;
}
public IParamsProvider Clone()
{
ProcParams pars = new ProcParams();
CopyContentTo(pars);
return pars;
}
public override void UpdateFromDbEntity(ComponentProcedure dbEntity)
{
if (dbEntity == null) return;
try
{
ProcParams tmp = Serializer.Deserialize(new StringReader(dbEntity.Parameters)) as ProcParams;
procedureParamsEntity = dbEntity;
componentName = dbEntity.CmpntName;
procedure = dbEntity.Procedure;
if (tmp != null) tmp.CopyContentTo(this);
}
catch
{
}
}
public ProcParams()
{
}
public ProcParams(bool initialize)
{
if (initialize) InitializeAll();
}
public ProcParams(ComponentProcedure procParamsEntity, string componentName, Procedure procedure)
{
this.procedureParamsEntity = procParamsEntity;
this.componentName = componentName;
this.procedure = procedure;
}
}
}
@@ -0,0 +1,160 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
namespace TBF.Rig.TestMethods.iPerlCommunication.iPerlHead
{
public class StatusStruct
{
public static readonly int Length = 48;
public Byte Version;
public FlowState FlowState;
public UInt32 BillingVolume;
public UInt32 ForwardVolume;
public UInt32 ReverseVolume;
public UInt32 SecondsActive;
public UInt32 SecondsIdle;
public UInt32 SecondsUsed;
public UInt32 UTC;
public Int32 ScaledAvgFlowRate;
public UInt16 PeakFlowRate;
public UInt16 AlarmState;
public byte RebootCount;
public byte[] AlarmCount;
public UInt16 FlipTime;
public StatusStruct()
{
AlarmCount = new byte[7];
}
public byte[] ToByteArray()
{
byte[] result = new byte[Length];
result[0] = Version;
result[1] = (byte)FlowState;
result[2] = (byte)(BillingVolume & 0x000000FF);
result[3] = (byte)((BillingVolume >> 8) & 0x000000FF);
result[4] = (byte)((BillingVolume >> 16) & 0x000000FF);
result[5] = (byte)((BillingVolume >> 24) & 0x000000FF);
result[6] = (byte)(ForwardVolume & 0x000000FF);
result[7] = (byte)((ForwardVolume >> 8) & 0x000000FF);
result[8] = (byte)((ForwardVolume >> 16) & 0x000000FF);
result[9] = (byte)((ForwardVolume >> 24) & 0x000000FF);
result[10] = (byte)(ReverseVolume & 0x000000FF);
result[11] = (byte)((ReverseVolume >> 8) & 0x000000FF);
result[12] = (byte)((ReverseVolume >> 16) & 0x000000FF);
result[13] = (byte)((ReverseVolume >> 24) & 0x000000FF);
result[14] = (byte)(SecondsActive & 0x000000FF);
result[15] = (byte)((SecondsActive >> 8) & 0x000000FF);
result[16] = (byte)((SecondsActive >> 16) & 0x000000FF);
result[17] = (byte)((SecondsActive >> 24) & 0x000000FF);
result[18] = (byte)(SecondsIdle & 0x000000FF);
result[19] = (byte)((SecondsIdle >> 8) & 0x000000FF);
result[20] = (byte)((SecondsIdle >> 16) & 0x000000FF);
result[21] = (byte)((SecondsIdle >> 24) & 0x000000FF);
result[22] = (byte)(SecondsUsed & 0x000000FF);
result[23] = (byte)((SecondsUsed >> 8) & 0x000000FF);
result[24] = (byte)((SecondsUsed >> 16) & 0x000000FF);
result[25] = (byte)((SecondsUsed >> 24) & 0x000000FF);
result[26] = (byte)(UTC & 0x000000FF);
result[27] = (byte)((UTC >> 8) & 0x000000FF);
result[28] = (byte)((UTC >> 16) & 0x000000FF);
result[29] = (byte)((UTC >> 24) & 0x000000FF);
result[30] = (byte)(ScaledAvgFlowRate & 0x000000FF);
result[31] = (byte)((ScaledAvgFlowRate >> 8) & 0x000000FF);
result[32] = (byte)((ScaledAvgFlowRate >> 16) & 0x000000FF);
result[33] = (byte)((ScaledAvgFlowRate >> 24) & 0x000000FF); /// TODO: test with negative value
result[34] = (byte)(PeakFlowRate & 0x00FF);
result[35] = (byte)((PeakFlowRate >> 8) & 0x00FF);
result[36] = (byte)(AlarmState & 0x00FF);
result[37] = (byte)((AlarmState >> 8) & 0x00FF);
result[38] = RebootCount;
result[39] = AlarmCount[0];
result[40] = AlarmCount[1];
result[41] = AlarmCount[2];
result[42] = AlarmCount[3];
result[43] = AlarmCount[4];
result[44] = AlarmCount[5];
result[45] = AlarmCount[6];
result[46] = (byte)(FlipTime & 0x00FF);
result[47] = (byte)((FlipTime >> 8) & 0x00FF);
return result;
}
public static StatusStruct FromByteArray(byte[] data)
{
if (data.Length != Length) return null;
StatusStruct result = new StatusStruct();
result.Version = data[0];
result.FlowState = (FlowState)data[1];
result.BillingVolume = (((UInt32)data[5] * 256 + data[4]) * 256 + data[3]) * 256 + data[2];
result.ForwardVolume = (((UInt32)data[9] * 256 + data[8]) * 256 + data[7]) * 256 + data[6];
result.ReverseVolume = (((UInt32)data[13] * 256 + data[12]) * 256 + data[11]) * 256 + data[10];
result.SecondsActive = (((UInt32)data[17] * 256 + data[16]) * 256 + data[15]) * 256 + data[14];
result.SecondsIdle = (((UInt32)data[21] * 256 + data[20]) * 256 + data[19]) * 256 + data[18];
result.SecondsUsed = (((UInt32)data[25] * 256 + data[24]) * 256 + data[23]) * 256 + data[22];
result.UTC = (((UInt32)data[29] * 256 + data[28]) * 256 + data[27]) * 256 + data[26];
result.ScaledAvgFlowRate = (((Int32)data[33] * 256 + data[32]) * 256 + data[31]) * 256 + data[30]; /// TODO: test with negative value
result.PeakFlowRate = (UInt16)(data[34] + 256 * data[35]);
result.AlarmState = (UInt16)(data[36] + 256 * data[37]);
result.RebootCount = data[38];
result.AlarmCount[0] = data[39];
result.AlarmCount[1] = data[40];
result.AlarmCount[2] = data[41];
result.AlarmCount[3] = data[42];
result.AlarmCount[4] = data[43];
result.AlarmCount[5] = data[44];
result.AlarmCount[6] = data[45];
result.FlipTime = (UInt16)(data[46] * 256 + data[47]);
return result;
}
public override string ToString()
{
return string.Format("Status: V{0} Flow={1} BillVol={2} ForwVol={3} RevVol={4} SecActive={5}s SecIdle={6}s SecUsed={7}s UTC={8} AvgFlowRate={9} PeekFlowRate={10} AlarmState={11} RebootCount={12} A0={13} A1={14} A2={15} A3={16} A4={17} A5={18} A6={19} FlipTime={20}",
Version,
FlowState,
BillingVolume,
ForwardVolume,
ReverseVolume,
SecondsActive,
SecondsIdle,
SecondsUsed,
UTC,
ScaledAvgFlowRate,
PeakFlowRate,
AlarmState,
RebootCount,
AlarmCount[0],
AlarmCount[1],
AlarmCount[2],
AlarmCount[3],
AlarmCount[4],
AlarmCount[5],
AlarmCount[6],
FlipTime);
}
}
}