Genesis: - data types UInt48/72/88 supported,

manual merge from Dev_Shipping - part 1
This commit is contained in:
Thomas Wiedebusch 2025-03-17 18:17:28 +01:00
parent 8d5bf62e2e
commit 0f784bfb03
26 changed files with 8422 additions and 734 deletions

View File

@ -6,17 +6,25 @@
public enum StatusReturn
{
/// <summary>
/// undefined status
/// undefined status does not fit in any other status defined below
/// </summary>
Unknown,
/// <summary>
/// skipped test and therefore the return identifies this
/// </summary>
Skipped,
/// <summary>
/// successfully executed without any restrictions
/// </summary>
Okay,
/// <summary>
/// execution failed
/// </summary>
Failed,
/// <summary>
/// inspection needed as warning returns
/// </summary>

View File

@ -0,0 +1,11 @@
<ProjectConfiguration>
<Settings>
<CopyReferencedAssembliesToWorkspace>False</CopyReferencedAssembliesToWorkspace>
<HiddenComponentWarnings />
<IgnoredTests>
<AllTestsSelector />
</IgnoredTests>
<IncludeStaticReferencesInWorkspace>False</IncludeStaticReferencesInWorkspace>
<PreventSigningOfAssembly>True</PreventSigningOfAssembly>
</Settings>
</ProjectConfiguration>

View File

@ -0,0 +1,33 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
/// <summary>
/// abstract for set structure for BaseDataEventArgs
/// </summary>
public abstract class BaseDataEventArgs : EventArgs
{
/// <summary>
/// 'Base' get event record form real child.
/// </summary>
/// <returns></returns>
public Object GetData()
{
return GetEventData();
}
/// <summary>
/// get real Event record
/// </summary>
/// <returns></returns>
public abstract Object GetEventData();
/// <summary>
/// Holds the record before decoding, for logging
/// </summary>
public String RawData;
}
}

View File

@ -0,0 +1,31 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts
{
/// <summary>
/// Alarm messages
/// </summary>
[Flags]
public enum Alarm
{
// ReSharper disable InconsistentNaming
#pragma warning disable CS1591
REBOOT = 1 << 0,
LOW_BATTERY = 1 << 1,
EMPTY_PIPE = 1 << 4,
REVERSE_FLOW = 1 << 6,
SUSPECT_LEAK = 1 << 7,
BROKEN_PIPE = 1 << 8,
LOW_PRESSURE = 1 << 9,
HIGH_PRESSURE = 1 << 10,
LOW_TEMPERATURE = 1 << 11,
HIGH_TEMPERATURE = 1 << 12,
RADIO_ERROR = 1 << 13,
METROLOGY_PARAMS = 1 << 14,
METROLOGY_MEASURE = 1 << 15,
ALL = 0xFFFFFF,
#pragma warning restore CS1591
// ReSharper restore InconsistentNaming
}
}

View File

@ -1,15 +1,17 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
using Applications;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Registers;
using Registers.DataTypes;
using Registers.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Applications;
using Registers;
using Registers.DataTypes;
using Registers.Json;
using WaterMeterRegisters;
/// <summary>
@ -32,7 +34,7 @@
/// <summary>
/// Ctor
/// </summary>
public GenesisConfigurationReader() { }
public GenesisConfigurationReader() {}
/// <summary>
/// Ctor
@ -68,7 +70,7 @@
.DeserializeObject<IDictionary<String, AppSection>>(configurationJson, SerializerSettings)
?? new Dictionary<String, AppSection>();
}
catch (Exception)
catch (Exception )
{
sections = new Dictionary<String, AppSection>();
}
@ -151,35 +153,29 @@
private static Type GetType(String value)
{
try
switch (value.ToLower())
{
switch (value.ToLower())
{
case "bool_t": return typeof(Boolean);
case "rpc": return typeof(Rpc);
case "string": return typeof(String);
case "uint32_t": return typeof(UInt32);
case "time_t": return typeof(TimeT);
case "status_t": return typeof(StatusT);
case "uint16_t": return typeof(UInt16);
case "uint8_t": return typeof(Byte);
case "enum8": return typeof(Enum8);
case "uint64_t": return typeof(UInt64);
case "uint96_t": return typeof(UInt96);
case "uint128_t": return typeof(UInt128);
case "int8_t": return typeof(SByte);
case "int32_t": return typeof(Int32);
case "int64_t": return typeof(Int64);
case "int16_t": return typeof(Int16);
default: throw new Exception($"Type {value} not recognized!");
}
case "bool_t": return typeof(Boolean);
case "rpc": return typeof(Rpc);
case "string": return typeof(String);
case "uint32_t": return typeof(UInt32);
case "time_t": return typeof(TimeT);
case "status_t": return typeof(StatusT);
case "uint16_t": return typeof(UInt16);
case "uint8_t": return typeof(Byte);
case "enum8": return typeof(Enum8);
case "uint64_t": return typeof(UInt64);
case "uint96_t": return typeof(UInt96);
case "uint128_t": return typeof(UInt128);
case "int8_t": return typeof(SByte);
case "int32_t": return typeof(Int32);
case "int64_t": return typeof(Int64);
case "int16_t": return typeof(Int16);
case "uint72_t": return typeof(UInt72);
case "uint48_t": return typeof(UInt48);
case "uint88_t": return typeof(UInt88);
default: throw new Exception($"Type {value} not recognized!");
}
catch (Exception ex)
{
//throw ex;
return typeof(Boolean);
}
}
}

View File

@ -0,0 +1,241 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
using Applications;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Registers;
using Registers.DataTypes;
using Registers.Json;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using WaterMeterRegisters;
/// <summary>
/// JSON filer reader for generation of register lists
/// </summary>
public class GenesisConfigurationReader
{
/// <summary>
/// Registers defined in configuration.json file
/// </summary>
public List<IRegister> ConfigRegistersDefinitions { get; }
= new List<IRegister>();
/// <summary>
/// Applications defined in configuration.json file
/// </summary>
public List<ApplicationDefinition> ConfigApplicationDefinitions { get; }
= new List<ApplicationDefinition>();
/// <summary>
/// Ctor
/// </summary>
public GenesisConfigurationReader() { }
/// <summary>
/// Ctor
/// </summary>
/// <param name="configFilePath">the file path for configuration.json as interface description to the meter</param>
public GenesisConfigurationReader(String configFilePath)
{
if (string.IsNullOrEmpty(configFilePath))
{
throw new ApplicationException("JsonFileRegisterReader needs at least one file path");
}
if (!File.Exists(configFilePath))
{
throw new ApplicationException($" {configFilePath} doesn't exists");
}
BuildRegisterList(File.ReadAllText(configFilePath));
}
/// <summary>
/// Convert file content to register list
/// </summary>
/// <param name="configurationJson"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public void BuildRegisterList(String configurationJson)
{
IDictionary<String, AppSection> sections;
try
{
sections = JsonConvert
.DeserializeObject<IDictionary<String, AppSection>>(configurationJson, SerializerSettings)
?? new Dictionary<String, AppSection>();
}
catch (Exception)
{
sections = new Dictionary<String, AppSection>();
}
foreach (var sectionKVP in sections)
{
var appId = sectionKVP.Value.Id;
var appName = sectionKVP.Key;
ConfigApplicationDefinitions.Add(new ApplicationDefinition
{
AppId = appId,
AppName = appName
});
var section = sectionKVP.Value;
var registers = section.Registers;
if (registers is null)
{
continue;
}
foreach (var registerKVP in registers)
{
var register = registerKVP.Value;
if (register is null)
{
continue;
}
var details = register.Details;
if (details is null)
{
continue;
}
var regId = register.Id;
foreach (var detail in details)
{
var values = detail.Values;
ConfigRegistersDefinitions.Add(new RegisterDefinition
{
AppAddress = appId,
AppName = appName,
DataType = GetType(detail.Type),
Default = values?.Default is Int64 d ? d : default(Int64?),
IsAvailable = default(Boolean),
Maximum = values?.Maximum is Int64 max ? max : default(Int64?),
Minimum = values?.Minimum is Int64 min ? min : default(Int64?),
RegAddressInApp = regId,
// RegisterAddress = new byte[] { appId, regId },
RegisterDetail = detail,
RegisterName = registerKVP.Key,
RestoreCapability = new StaticType(detail.StaticType)
});
}
}
}
}
/// <summary>
/// Settings
/// </summary>
private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
AccessConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
private static Type GetType(String value)
{
try
{
switch (value.ToLower())
{
case "bool_t": return typeof(Boolean);
case "rpc": return typeof(Rpc);
case "string": return typeof(String);
case "uint32_t": return typeof(UInt32);
case "time_t": return typeof(TimeT);
case "status_t": return typeof(StatusT);
case "uint16_t": return typeof(UInt16);
case "uint8_t": return typeof(Byte);
case "enum8": return typeof(Enum8);
case "uint64_t": return typeof(UInt64);
case "uint96_t": return typeof(UInt96);
case "uint128_t": return typeof(UInt128);
case "int8_t": return typeof(SByte);
case "int32_t": return typeof(Int32);
case "int64_t": return typeof(Int64);
case "int16_t": return typeof(Int16);
default: throw new Exception($"Type {value} not recognized!");
}
}
catch (Exception ex)
{
//throw ex;
return typeof(Boolean);
}
}
}
/// <summary>
///
/// </summary>
public class AccessConverter : JsonConverter
{
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public override Boolean CanConvert(Type t) => t == typeof(Access) || t == typeof(Access?);
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="t"></param>
/// <param name="existingValue"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public override Object ReadJson(JsonReader reader, Type t, Object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var value = serializer.Deserialize<String>(reader);
return Enum.TryParse(value, true, out Access access) ? access : default(Access);
}
/// <summary>
///
/// </summary>
/// <param name="writer"></param>
/// <param name="untypedValue"></param>
/// <param name="serializer"></param>
public override void WriteJson(JsonWriter writer, Object untypedValue, JsonSerializer serializer)
{
if (untypedValue is Access access)
{
writer.WriteValue(access.ToString());
}
else
{
throw new Exception("Cannot marshal type Lvl");
}
}
/// <summary>
///
/// </summary>
public static readonly AccessConverter Singleton = new AccessConverter();
}
}

View File

@ -60,6 +60,33 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Could not find a CSD parameter in register list!.
/// </summary>
internal static string StrErrorMsgCsdMissing {
get {
return ResourceManager.GetString("StrErrorMsgCsdMissing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Could not find register in register list! Check configuration.json for latest version!.
/// </summary>
internal static string StrErrorMsgRegisterNotFound {
get {
return ResourceManager.GetString("StrErrorMsgRegisterNotFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Could not write register!.
/// </summary>
internal static string StrErrorMsgRegisterWrite {
get {
return ResourceManager.GetString("StrErrorMsgRegisterWrite", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Read back.
/// </summary>
@ -169,7 +196,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
}
/// <summary>
/// Looks up a localized string similar to Recovery configuration.....
/// Looks up a localized string similar to Execute configuration sequence.....
/// </summary>
internal static string StrRegisterRecoveryExec {
get {
@ -178,7 +205,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
}
/// <summary>
/// Looks up a localized string similar to Finalize configuration recovery sequence.....
/// Looks up a localized string similar to Finalize configuration sequence.....
/// </summary>
internal static string StrRegisterRecoveryFinalization {
get {
@ -187,7 +214,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
}
/// <summary>
/// Looks up a localized string similar to Prepare configuration recovery sequence.....
/// Looks up a localized string similar to Prepare configuration sequence.....
/// </summary>
internal static string StrRegisterRecoveryPreparation {
get {
@ -196,7 +223,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
}
/// <summary>
/// Looks up a localized string similar to ERROR: Register value is out of range.
/// Looks up a localized string similar to ERROR: Register value is out of range!.
/// </summary>
internal static string StrRegisterValueOutOfRange {
get {
@ -223,7 +250,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
}
/// <summary>
/// Looks up a localized string similar to Set register value to default.
/// Looks up a localized string similar to WARNING: Register value set to default.
/// </summary>
internal static string StrSetRegisterToDefault {
get {

View File

@ -154,13 +154,13 @@
<value>Lese Register nach der Wartung</value>
</data>
<data name="StrRegisterRecoveryExec" xml:space="preserve">
<value>Ausführung Konfigurationswiederherstellungssequenz....</value>
<value>Ausführung der Konfigurationsequenz....</value>
</data>
<data name="StrRegisterRecoveryFinalization" xml:space="preserve">
<value>Abschließen Konfigurationswiederherstellungssequenz....</value>
<value>Abschließen der Konfigurationsequenz....</value>
</data>
<data name="StrRegisterRecoveryPreparation" xml:space="preserve">
<value>Vorbereitung Konfigurationswiederherstellungssequenz....</value>
<value>Vorbereitung der Konfigurationsequenz....</value>
</data>
<data name="StrRegisterCompareFailed" xml:space="preserve">
<value>FEHLER: Vergleich fehlgeschlagen!</value>
@ -172,9 +172,18 @@
<value>Zurückgelesen</value>
</data>
<data name="StrSetRegisterToDefault" xml:space="preserve">
<value>Registerwert auf Standardwert gesetzt</value>
<value>WARNUNG: Registerwert auf Standardwert gesetzt</value>
</data>
<data name="StrRegisterValueOutOfRange" xml:space="preserve">
<value>FEHLER: Registerwert ungültig</value>
<value>FEHLER: Registerwert außerhalb des zulässigen Bereiches!</value>
</data>
<data name="StrErrorMsgCsdMissing" xml:space="preserve">
<value>FEHLER: Kein CSD Parameter in Parameterliste gefunden!</value>
</data>
<data name="StrErrorMsgRegisterNotFound" xml:space="preserve">
<value>FEHLER: Register nicht in Registerliste gefunden! Version der Configuration.json prüfen!</value>
</data>
<data name="StrErrorMsgRegisterWrite" xml:space="preserve">
<value>FEHLER: Register konnte nicht geschrieben werden!</value>
</data>
</root>

View File

@ -154,13 +154,13 @@
<value>Write register</value>
</data>
<data name="StrRegisterRecoveryExec" xml:space="preserve">
<value>Recovery configuration....</value>
<value>Execute configuration sequence....</value>
</data>
<data name="StrRegisterRecoveryFinalization" xml:space="preserve">
<value>Finalize configuration recovery sequence....</value>
<value>Finalize configuration sequence....</value>
</data>
<data name="StrRegisterRecoveryPreparation" xml:space="preserve">
<value>Prepare configuration recovery sequence....</value>
<value>Prepare configuration sequence....</value>
</data>
<data name="StrRegisterCompareFailed" xml:space="preserve">
<value>ERROR: Comparison failed!</value>
@ -172,9 +172,18 @@
<value>Read back</value>
</data>
<data name="StrSetRegisterToDefault" xml:space="preserve">
<value>Set register value to default</value>
<value>WARNING: Register value set to default</value>
</data>
<data name="StrRegisterValueOutOfRange" xml:space="preserve">
<value>ERROR: Register value is out of range</value>
<value>ERROR: Register value is out of range!</value>
</data>
<data name="StrErrorMsgCsdMissing" xml:space="preserve">
<value>ERROR: Could not find a CSD parameter in register list!</value>
</data>
<data name="StrErrorMsgRegisterNotFound" xml:space="preserve">
<value>ERROR: Could not find register in register list! Check configuration.json for latest version!</value>
</data>
<data name="StrErrorMsgRegisterWrite" xml:space="preserve">
<value>ERROR: Could not write register!</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,8 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes
{
public struct UInt48
{
}
}

View File

@ -0,0 +1,8 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes
{
public struct UInt72
{
}
}

View File

@ -0,0 +1,8 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes
{
public struct UInt88
{
}
}

View File

@ -122,6 +122,11 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
}
}
/// <summary>
/// Unimplemented
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();

View File

@ -0,0 +1,90 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Register data type unknown.
/// </summary>
internal static string StrRegisterDataTypeUnknown {
get {
return ResourceManager.GetString("StrRegisterDataTypeUnknown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Register value is out of range.
/// </summary>
internal static string StrRegisterValueOutOfRange {
get {
return ResourceManager.GetString("StrRegisterValueOutOfRange", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to WARNING: Register value is set to default.
/// </summary>
internal static string StrSetRegisterToDefault {
get {
return ResourceManager.GetString("StrSetRegisterToDefault", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 1.3
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">1.3</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1">this is my long string</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
[base64 mime encoded serialized .NET Framework object]
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
[base64 mime encoded string representing a byte array form of the .NET Framework object]
</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.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:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<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" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</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>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="StrSetRegisterToDefault" xml:space="preserve">
<value>WARNUNG: Registerwert auf Standardwert gesetzt</value>
</data>
<data name="StrRegisterValueOutOfRange" xml:space="preserve">
<value>FEHLER: Registerwert außerhalb des zulässigen Bereiches</value>
</data>
<data name="StrRegisterDataTypeUnknown" xml:space="preserve">
<value>FEHLER: Registerdatentyp ist unbekannt!</value>
</data>
</root>

View File

@ -0,0 +1,129 @@
<?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>
<data name="StrSetRegisterToDefault" xml:space="preserve">
<value>WARNING: Register value is set to default</value>
</data>
<data name="StrRegisterValueOutOfRange" xml:space="preserve">
<value>ERROR: Register value is out of range</value>
</data>
<data name="StrRegisterDataTypeUnknown" xml:space="preserve">
<value>ERROR: Register data type unknown</value>
</data>
</root>

View File

@ -1,142 +1,264 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.Properties;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
{
/// <summary>
/// Check the register value for Out Of Boundaries (min, max) and optional if set to Default .
/// </summary>
public static class RegisterCheck
{
private static String OutOfRangeMsg(Boolean isMin, String value, String range, String name)
private enum RangeCheck
{
return isMin ? $" Register {name} value ({value}) is to small - minimum is ({range})" :
$" Register {name} value ({value}) is to large - maximum is ({range})";
MinimumExceeded,
MaximumExceeded,
DefaultValue,
NotConvertible
};
private static String OutOfRangeMsg(RangeCheck rangeCheck, String value, String range, String name)
{
var msg = "";
switch (rangeCheck)
{
case RangeCheck.DefaultValue:
msg = $"{Resources.StrSetRegisterToDefault} {name}: ({value})";
break;
case RangeCheck.MaximumExceeded:
msg = $"{Resources.StrRegisterValueOutOfRange} {name}: ({value}) - Maximum ({range})";
break;
case RangeCheck.MinimumExceeded:
msg = $"{Resources.StrRegisterValueOutOfRange} {name}: ({value}) - Minimum ({range})";
break;
case RangeCheck.NotConvertible:
msg = $"{Resources.StrRegisterDataTypeUnknown} {name}";
break;
}
return msg;
}
/// <summary>
/// Check the registers for ot of range (below minimum or above maximum) and return empty string
/// if impossible to check (unset limits) or in range.
/// </summary>
/// <param name="reg"></param>
/// <param name="regDef"></param>
/// <param name="value"></param>
/// <returns>empty string for in range or unset limits or out of range message</returns>
/// <param name="msg">feedback message</param>
/// <param name="checkIfValueIsDefault">forces a warning if value is set to default</param>
/// <returns>
/// <see cref="StatusReturn.Warning"/> if default and check for this is required
/// <see cref="StatusReturn.Okay"/>if the value is in range or has no range defined
/// <see cref="StatusReturn.Failed"/>if value exceeds the limits
/// </returns>
/// <remarks date="????" author="Roland Drabesch">
/// - Init.
/// </remarks>
/// <remarks date="2024-Mar-21" author="Thomas Wiedebusch">
/// - Modified with try catch block.
/// </remarks>
public static String CheckRange(RegisterDefinition reg, Byte[] value)
/// <remarks date="2024-Sep-25" author="Thomas Wiedebusch">
/// - Returns warning on default, else error or okay.
/// </remarks>
/// <remarks date="2025-Feb-13" author="Thomas Wiedebusch">
/// - Ignore TimeT.
/// </remarks>
public static StatusReturn CheckRange(RegisterDefinition regDef, Byte[] value, out String msg,
Boolean checkIfValueIsDefault = false)
{
// for all registers without min and max a
if (!reg.Minimum.HasValue && !reg.Maximum.HasValue)
msg = "";
// for all registers without min and max or if type is of TimeT
if ((!regDef.Minimum.HasValue && !regDef.Maximum.HasValue) || regDef.DataType == typeof(TimeT))
{
return string.Empty;
return StatusReturn.Okay;
}
try
{
var type = reg.DataType;
var type = regDef.DataType;
if (type == typeof(UInt16))
{
var checkValue = RegisterConverter.ByteArrayToValue<UInt16>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(UInt32))
{
var checkValue = RegisterConverter.ByteArrayToValue<UInt32>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(UInt64))
{
var checkValue = RegisterConverter.ByteArrayToValue<UInt64>(value);
if (reg.Minimum.HasValue && checkValue < (UInt64)reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < (UInt64)regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > (UInt64)reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > (UInt64)regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && 0 == checkValue.CompareTo(regDef.Default.Value))
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(Int16))
{
var checkValue = RegisterConverter.ByteArrayToValue<Int16>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(Int32))
{
var checkValue = RegisterConverter.ByteArrayToValue<Int32>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(Int64))
{
var checkValue = RegisterConverter.ByteArrayToValue<Int64>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(Byte) || type == typeof(Enum8))
{
var checkValue = RegisterConverter.ByteArrayToValue<Byte>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(SByte))
{
var checkValue = RegisterConverter.ByteArrayToValue<SByte>(value);
if (reg.Minimum.HasValue && checkValue < reg.Minimum.Value)
if (regDef.Minimum.HasValue && checkValue < regDef.Minimum.Value)
{
return OutOfRangeMsg(true, checkValue.ToString(), reg.Minimum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MinimumExceeded, checkValue.ToString(),
regDef.Minimum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (reg.Maximum.HasValue && checkValue > reg.Maximum.Value)
if (regDef.Maximum.HasValue && checkValue > regDef.Maximum.Value)
{
return OutOfRangeMsg(false, checkValue.ToString(), reg.Maximum.Value.ToString(), reg.GetIdent());
msg = OutOfRangeMsg(RangeCheck.MaximumExceeded, checkValue.ToString(),
regDef.Maximum.Value.ToString(), regDef.GetIdent());
return StatusReturn.Failed;
}
if (checkIfValueIsDefault && regDef.Default.HasValue && checkValue == regDef.Default.Value)
{
msg = OutOfRangeMsg(RangeCheck.DefaultValue, checkValue.ToString(),
regDef.Default.Value.ToString(), regDef.GetIdent());
return StatusReturn.Warning;
}
}
else if (type == typeof(Boolean))
@ -146,17 +268,18 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
}
else
{
return ($"Register {reg.RegisterName} has min or max value but the data type {reg.DataType} " +
"doesn't support auto conversion");
msg = OutOfRangeMsg(RangeCheck.NotConvertible, "", "", regDef.GetIdent());
return StatusReturn.Failed;
}
}
catch (Exception e)
{
return e.Message;
msg = e.Message;
return StatusReturn.Failed;
}
// the value is in range
return string.Empty;
// the value is in range and not on default
return StatusReturn.Okay;
}
}
}

View File

@ -16,7 +16,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
/// Convert single register from Genesis and return a message of the result. The value input is a byte array
/// with the LSB at the index 0 and the MSB at the highest index. A string is sorted with the first character
/// at index 0, this does not need to be reversed as it is readable by default.
/// The raw value is swapped byte-wise to get a human readable value with MSB at leftmost and LSB at rightmost.
/// The raw value is swapped byte-wise to get a human-readable value with MSB at leftmost and LSB at rightmost.
/// The message contains the register name, the swapped raw value and on the converted value.
/// </summary>
/// <param name="registerDefinition"></param>
@ -38,32 +38,31 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
public static String GetRegisterContentText(RegisterDefinition registerDefinition, Byte[] regRawByteArray)
{
String msg;
var strValueResult = "?";
var strRawResult = "?";
var registerName = "?";
String registerName = "?";
try
{
registerName = registerDefinition.GetIdent();
// getting a result value as text of the data type e.g. UInt32
var strValue = ConvertToText(regRawByteArray, registerDefinition.DataType);
// remove non printable char of the converted result
strValueResult = Regex.Replace(strValue, @"\p{C}+", string.Empty);
// remove non-printable char of the converted result
var strValueResult = Regex.Replace(strValue, @"\p{C}+", string.Empty);
// use hashed output being able to compare written and read back values
String strRawResult;
if (registerName.Contains("EncryptionKey") || registerName.Contains("Password"))
{
strRawResult = GetRegisterRawText(registerDefinition, regRawByteArray, true);
msg = $"{registerName} ({strRawResult})";
strRawResult = GetRegisterRawText(registerDefinition, regRawByteArray, encryptData:true);
msg = $"{registerName}: ({strRawResult})h";
}
else
{
strRawResult = GetRegisterRawText(registerDefinition, regRawByteArray);
msg = $"{registerName} ({strRawResult}) ({strValueResult})";
msg = $"{registerName}: ({strRawResult})h ({strValueResult})";
}
}
catch (Exception)
{
msg = $"{registerName} ({strRawResult}) ({strValueResult})";
throw new ApplicationException($"Cannot convert register {registerName}");
}
return msg;
@ -73,7 +72,7 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
/// Convert single register from Genesis and return a message of the result. The value input is a byte array
/// with the LSB at the index 0 and the MSB at the highest index. A string is sorted with the first character
/// at index 0, this does not need to be reversed as it is readable by default.
/// The raw value is swapped byte-wise to get a human readable value with MSB at leftmost and LSB at rightmost.
/// The raw value is swapped byte-wise to get a human-readable value with MSB at leftmost and LSB at rightmost.
/// The message contains the swapped raw value.
/// </summary>
/// <param name="registerDefinition"></param>
@ -97,9 +96,12 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
// inverse the raw value to get a readable byte order MSB left and LSB last right position
var tmpList = regRawByteArray.ToList();
// reverse the list if it is not a string or the UInt96 or UInt128 which will be used as
// placeholder for a string
// reverse the list if it is not a string or the UInt48, UInt72, UInt88,UInt96 or UInt128 which
// will be used as placeholder for a string
if (registerDefinition.DataType != typeof(String) &&
registerDefinition.DataType != typeof(UInt48) &&
registerDefinition.DataType != typeof(UInt72) &&
registerDefinition.DataType != typeof(UInt88) &&
registerDefinition.DataType != typeof(UInt96) &&
registerDefinition.DataType != typeof(UInt128))
{
@ -288,18 +290,39 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
size = sizeof(Int64);
}
else if (registerDefinition.DataType == typeof(UInt48))
{
// 48 Bits / 8 Bits/Byte
size = 6;
}
else if (registerDefinition.DataType == typeof(UInt72))
{
// 72 Bits / 8 Bits/Byte
size = 9;
}
else if (registerDefinition.DataType == typeof(UInt88))
{
// 88 Bits / 8 Bits/Byte
size = 11;
}
else if (registerDefinition.DataType == typeof(UInt96))
{
// 96 Bits / 8 Bits/Byte
size = 3 * 4;
}
else if (registerDefinition.DataType == typeof(UInt128))
{
// 128 Bits / 8 Bits/Byte
size = 4 * 4;
}
else if (registerDefinition.DataType == typeof(String))
{
// just some value
size = 6 * 4;
}
@ -361,6 +384,18 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
{
return ByteArrayToValue<Byte>(rawByteArray).ToString();
}
if (type == typeof(UInt48))
{
return "";
}
if (type == typeof(UInt72))
{
return "";
}
if (type == typeof(UInt88))
{
return "";
}
if (type == typeof(UInt96))
{
return "";
@ -421,6 +456,18 @@ namespace Xylem.Common.Hardware.WaterMeter.Genesis.Registers
{
convertedObj = BitConverter.ToUInt64(rawByteArray, 0);
}
else if (type == typeof(UInt48))
{
return default(T);
}
else if (type == typeof(UInt72))
{
return default(T);
}
else if (type == typeof(UInt88))
{
return default(T);
}
else if (type == typeof(UInt96))
{
return default(T);

View File

@ -113,7 +113,10 @@
<Compile Include="DataTypes\st_radio_dewa.cs" />
<Compile Include="DataTypes\st_radio_tfx.cs" />
<Compile Include="DataTypes\TimeT.cs" />
<Compile Include="DataTypes\UInt48.cs" />
<Compile Include="DataTypes\UInt72.cs" />
<Compile Include="DataTypes\UInt128.cs" />
<Compile Include="DataTypes\UInt88.cs" />
<Compile Include="DataTypes\UInt96.cs" />
<Compile Include="Json\AppSection.cs" />
<Compile Include="Json\FwVersion.cs" />
@ -121,6 +124,16 @@
<Compile Include="Json\Register.cs" />
<Compile Include="Json\Status.cs" />
<Compile Include="Json\Value.cs" />
<Compile Include="Properties\Resources.de.Designer.cs">
<DependentUpon>Resources.de.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="RecoveryRegisterItem.cs" />
<Compile Include="RecoverySettings.cs" />
<Compile Include="RegisterCheck.cs" />
@ -136,6 +149,10 @@
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1B02C79E-0B19-43E2-8F6B-71EF0C786C97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\ProcessExec\ProcessExec.csproj">
<Project>{2FD60CF1-45CD-4060-B8E5-4F39FBA7F1CB}</Project>
<Name>ProcessExec</Name>
@ -156,6 +173,18 @@
<ItemGroup>
<WCFMetadata Include="Connected Services\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.de.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.de.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1827,7 +1827,7 @@ namespace Xylem.Common.Ui.GenesisToolBox
url += "&FileCategoryID=1";
var result = LocalWebRequest.GetRequest(url, 1000);
var result = LocalWebRequest.GetRequest(url, 30000);
var a = JsonConvert.DeserializeObject<List<ClusteredFile>>(result);
dgvFiles.DataSource = a?.OrderByDescending(o => o.FileDate).ToList();
@ -1849,7 +1849,7 @@ namespace Xylem.Common.Ui.GenesisToolBox
var url = $"{ServiceUrls.FileContentControllerUrl()}/GetFileParts?ParentFileId={parentFilId}&readContent=true";
var result = LocalWebRequest.GetRequest(url, 1000);
var result = LocalWebRequest.GetRequest(url, 30000);
var a = JsonConvert.DeserializeObject<List<FilePart>>(result);
lblFwNameSelected.Text = dgvFiles.Rows[e.RowIndex].Cells[1].Value.ToString();

View File

@ -1123,12 +1123,12 @@ namespace Xylem.Common.Ui.GenesisToolBox
fa.AddRange((new Byte[] { 0xAC, 0x21, 0x65, 0x35 }).ToArray());
_currentGenesis.WriteRegister("SENSUSRADIO_EncryptionKey", fa.ToArray());
return;
var before = _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds");
//var before = _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds");
var tsa = DateTimeOffset.UtcNow - new DateTimeOffset(2000, 1, 1, 0, 0, 0, new TimeSpan(0));
_currentGenesis.WriteRegister("SYSTEM_CalendarSeconds", Convert.ToInt32(tsa.TotalSeconds));
//var tsa = DateTimeOffset.UtcNow - new DateTimeOffset(2000, 1, 1, 0, 0, 0, new TimeSpan(0));
//_currentGenesis.WriteRegister("SYSTEM_CalendarSeconds", Convert.ToInt32(tsa.TotalSeconds));
var after = _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds");
//var after = _currentGenesis.ReadRegister("SYSTEM_CalendarSeconds");
@ -1464,8 +1464,8 @@ namespace Xylem.Common.Ui.GenesisToolBox
)
{
var a = _currentGenesis.ReadRegister(VARIABLE.GetIdent());
String result = RegisterCheck.CheckRange(VARIABLE, a);
if (!string.IsNullOrEmpty(result))
//String result = RegisterCheck.CheckRange(VARIABLE, a);
if (StatusReturn.Failed == RegisterCheck.CheckRange(VARIABLE, a, out var result))
{
MessageBox.Show(result);
allSucceed = false;

View File

@ -274,7 +274,7 @@ namespace Xylem.ServiceFwUpdate.Common.FwUpdateDb
/// </remarks>
public Boolean DownloadCordonelRecoveryRegistersFromDb(String pcbId, List<RecoveryRegisterItem> recoveryRegisters)
{
if (StatusReturn.Okay == RegisterRestorer.DownloadProgrammingParametersFromDb(pcbId, recoveryRegisters))
if (StatusReturn.Okay == RegisterRestorer.DownloadProgrammingParametersFromDb(pcbId, recoveryRegisters, out _))
{
DbIsConnected = true;
return true;