Initial commit

This commit is contained in:
Rowlander
2021-10-01 11:09:20 +02:00
commit fe54fceb1e
1301 changed files with 853973 additions and 0 deletions
@@ -0,0 +1,32 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications
{
/// <summary>
/// describe a genesis register.
/// need for read and write
/// all request protocols uses RegisterDefinition
/// </summary>
public class ApplicationDefinition
{
/// <summary>
/// name of the application
/// </summary>
public String AppName;
/// <summary>
/// address of application
/// </summary>
public Byte AppId;
/// <summary>
/// Get application name
/// </summary>
/// <returns></returns>
public String GetIdent()
{
return $"{AppName}";
}
}
}
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{315E94B8-B82F-4B25-B5BD-297DF3BC1D54}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.Applications</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.Applications</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>false</Deterministic>
<TargetFrameworkProfile />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="ApplicationDefinition.cs" />
<Compile Include="Const\MeterAppState.cs" />
<Compile Include="Const\MeterAppStateInfo.cs" />
<Compile Include="Const\MeterAppStateText.cs" />
<Compile Include="FileApplications.cs" />
<Compile Include="MeterApplications.cs" />
<Compile Include="Properties\AssemblyInfo.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">
<DependentUpon>Resources.resx</DependentUpon>
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.de.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.de.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,70 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const
{
/// <summary>
/// Genesis meter application state
/// </summary>
public enum MeterAppState
{
/// <summary>
/// This file application is not installed in the meter
/// </summary>
MeterAppNotInstalled,
/// <summary>
/// Meter version is equal to file version - it is up to date
/// </summary>
MeterAppUpToDate,
/// <summary>
/// Installation of application is required
/// </summary>
MeterAppInstallationRequired,
/// <summary>
/// Meter application is different from file application
/// </summary>
MeterAppVersionOutdated,
/// <summary>
/// CRC does not match even if meter and file application version are
/// shown as being identical!
/// </summary>
InvalidCrc,
/// <summary>
/// Meter applications has to be erased
/// </summary>
MeterAppErasureRequired,
/// <summary>
/// Application successfully downloaded to meter
/// </summary>
MeterAppDownloadSucceeded,
/// <summary>
/// Application download to meter suspicious
/// </summary>
MeterAppDownloadSuspicious,
/// <summary>
/// Application successfully erased from meter
/// </summary>
MeterAppSuccessfulErased,
/// <summary>
/// Application successfully updated in meter
/// </summary>
MeterAppSuccessfulUpdated,
/// <summary>
/// Application download to meter failed
/// </summary>
MeterAppDownloadFailed,
/// <summary>
/// Application download to meter active ongoing
/// </summary>
MeterAppDownloadActive,
/// <summary>
/// This file application is invalid
/// </summary>
FileAppInvalid,
/// <summary>
/// State is unknown because package file not loaded
/// </summary>
Unknown,
/// <summary>
/// Delimiter for list
/// </summary>
MeterAppStateListDelimiter
}
}
@@ -0,0 +1,86 @@
using System;
using System.Linq;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const
{
/// <summary>
/// Concatenation of state and string
/// </summary>
public class MeterAppStateInfo
{
/// <summary>
/// Build state information
/// </summary>
private readonly MeterAppStateText[] _stateInfos;
/// <summary>
/// Ctor
/// </summary>
public MeterAppStateInfo()
{
_stateInfos = new MeterAppStateText[(Int32)MeterAppState.MeterAppStateListDelimiter];
_stateInfos[0].State = MeterAppState.MeterAppNotInstalled;
_stateInfos[0].Message = Resources.StrMeterAppNotInstalled;
_stateInfos[1].State = MeterAppState.MeterAppUpToDate;
_stateInfos[1].Message = Resources.StrMeterAppUpToDate;
_stateInfos[2].State = MeterAppState.MeterAppInstallationRequired;
_stateInfos[2].Message = Resources.StrMeterAppInstallationRequired;
_stateInfos[3].State = MeterAppState.MeterAppVersionOutdated;
_stateInfos[3].Message = Resources.StrMeterAppVersionOutdated;
_stateInfos[4].State = MeterAppState.InvalidCrc;
_stateInfos[4].Message = Resources.StrInvalidCrc;
_stateInfos[5].State = MeterAppState.MeterAppErasureRequired;
_stateInfos[5].Message = Resources.StrMeterAppErasureRequired;
_stateInfos[6].State = MeterAppState.MeterAppDownloadSucceeded;
_stateInfos[6].Message = Resources.StrMeterAppDownloadSucceeded;
_stateInfos[7].State = MeterAppState.MeterAppDownloadSuspicious;
_stateInfos[7].Message = Resources.StrMeterAppDownloadSuspicious;
_stateInfos[8].State = MeterAppState.MeterAppSuccessfulErased;
_stateInfos[8].Message = Resources.StrMeterAppSuccessfulErased;
_stateInfos[9].State = MeterAppState.MeterAppSuccessfulUpdated;
_stateInfos[9].Message = Resources.StrMeterAppSuccessfulUpdated;
_stateInfos[10].State = MeterAppState.MeterAppDownloadFailed;
_stateInfos[10].Message = Resources.StrMeterAppDownloadFailed;
_stateInfos[11].State = MeterAppState.MeterAppDownloadActive;
_stateInfos[11].Message = Resources.StrMeterAppDownloadActive;
_stateInfos[12].State = MeterAppState.FileAppInvalid;
_stateInfos[12].Message = Resources.StrFileAppInvalid;
_stateInfos[13].State = MeterAppState.Unknown;
_stateInfos[13].Message = "";
}
/// <summary>
/// Get the text of this state
/// </summary>
/// <param name="state">state to search for information text</param>
public String GetTextFromState(MeterAppState state)
{
return (from stateInfo in _stateInfos
where stateInfo.State == state select stateInfo.Message).Last();
}
/// <summary>
/// Get the state of the text
/// </summary>
/// <param name="text">information text to search if state is assigned</param>
public MeterAppState GetStateFromText(String text)
{
return (from stateInfo in _stateInfos
where stateInfo.Message == text select stateInfo.State).Last();
}
}
}
@@ -0,0 +1,19 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const
{
/// <summary>
/// Concatenation between meter application state and text
/// </summary>
public struct MeterAppStateText
{
/// <summary>
/// The state of the firmware update
/// </summary>
public MeterAppState State;
/// <summary>
/// The message text of the meter application state
/// </summary>
public String Message;
}
}
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications
{
/// <summary>
/// File FW and application information
/// </summary>
public class FileApplications
{
/// <summary>
/// Name of application
/// </summary>
public String AppName;
/// <summary>
/// File name of the file system where the binaries are stored
/// </summary>
public String BinaryFilename;
/// <summary>
/// File name of the meter including the "1\\upg" string and the AppId as hexadecimal 2 digits
/// </summary>
public String MeterFilename;
/// <summary>
/// Ctor file applications
/// </summary>
/// <param name="binaryFilename"></param>
public FileApplications(String binaryFilename)
{
BinaryFilename = binaryFilename;
}
/// <summary>
/// Identification number of application
/// </summary>
public Byte AppId;
/// <summary>
/// Version of application
/// </summary>
public UInt32 Version;
/// <summary>
/// Version string of application because of FLEXNETVERSION
/// does return a different layout, instead of decimals it
/// used hexadecimals. Therefore the version has to be saved as
/// string at time of receiving for the FW-Update comparison
/// </summary>
public String StrVersion;
/// <summary>
/// Check of application for verification
/// </summary>
public UInt16 Crc;
/// <summary>
/// Validation of application
/// </summary>
public Boolean IsValid;
/// <summary>
/// Binary file content
/// </summary>
public List<Byte> BinData = new List<Byte>();
/// <summary>
/// Actual part if file is going to be partitioned
/// </summary>
public Int32 Part = 0;
/// <summary>
/// Number of parts for entire file if partitioned
/// </summary>
public Int32 Parts = 1;
/// <summary>
/// Meter file offset if file is partitioned
/// </summary>
public Int32 MeterFileOffset = 0;
}
}
@@ -0,0 +1,60 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Const;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications
{
/// <summary>
/// Meter FW and application information
/// </summary>
public class MeterApplications
{
/// <summary>
/// Name of application
/// </summary>
public String AppName;
/// <summary>
/// Identification number of application
/// </summary>
public Byte AppId;
/// <summary>
/// Version of application
/// </summary>
public UInt32 Version;
/// <summary>
/// Version string of application because of FLEXNETVERSION
/// does return a different layout, instead of decimals it
/// used hexadecimals. Therefore the version has to be safed as
/// string at time of receiving for the FW-Update comparison
/// </summary>
public String StrVersion;
/// <summary>
/// Check of application for verification
/// </summary>
public UInt16 Crc;
/// <summary>
/// Check if application is installed
/// </summary>
public Boolean IsInstalled;
/// <summary>
/// Update this version
/// </summary>
public Boolean Update;
/// <summary>
/// Erase this version
/// </summary>
public Boolean Erase;
/// <summary>
/// Status text
/// </summary>
public MeterAppState Status;
}
}
@@ -0,0 +1,34 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.Applications")]
[assembly: AssemblyDescription("")]
//[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Xylem")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("Copyright © Xylem 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("315e94b8-b82f-4b25-b5bd-297df3bc1d54")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
@@ -0,0 +1,180 @@
//------------------------------------------------------------------------------
// <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.Applications.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()]
public 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)]
public 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.Applications.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)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to INVALID UPDATE FILE.
/// </summary>
public static string StrFileAppInvalid {
get {
return ResourceManager.GetString("StrFileAppInvalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to CRC ERROR.
/// </summary>
public static string StrInvalidCrc {
get {
return ResourceManager.GetString("StrInvalidCrc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DOWNLOAD....
/// </summary>
public static string StrMeterAppDownloadActive {
get {
return ResourceManager.GetString("StrMeterAppDownloadActive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Download failed.
/// </summary>
public static string StrMeterAppDownloadFailed {
get {
return ResourceManager.GetString("StrMeterAppDownloadFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Burn - Trigger upgrade required.
/// </summary>
public static string StrMeterAppDownloadSucceeded {
get {
return ResourceManager.GetString("StrMeterAppDownloadSucceeded", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Verification required.
/// </summary>
public static string StrMeterAppDownloadSuspicious {
get {
return ResourceManager.GetString("StrMeterAppDownloadSuspicious", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Erase - Trigger upgrade required.
/// </summary>
public static string StrMeterAppErasureRequired {
get {
return ResourceManager.GetString("StrMeterAppErasureRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installation required.
/// </summary>
public static string StrMeterAppInstallationRequired {
get {
return ResourceManager.GetString("StrMeterAppInstallationRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Application not required.
/// </summary>
public static string StrMeterAppNotInstalled {
get {
return ResourceManager.GetString("StrMeterAppNotInstalled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Erasure succeeded.
/// </summary>
public static string StrMeterAppSuccessfulErased {
get {
return ResourceManager.GetString("StrMeterAppSuccessfulErased", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update succeeded.
/// </summary>
public static string StrMeterAppSuccessfulUpdated {
get {
return ResourceManager.GetString("StrMeterAppSuccessfulUpdated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Up To Date.
/// </summary>
public static string StrMeterAppUpToDate {
get {
return ResourceManager.GetString("StrMeterAppUpToDate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update required.
/// </summary>
public static string StrMeterAppVersionOutdated {
get {
return ResourceManager.GetString("StrMeterAppVersionOutdated", resourceCulture);
}
}
}
}
@@ -0,0 +1,159 @@
<?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="StrMeterAppNotInstalled" xml:space="preserve">
<value>Anwendung nicht erforderlich</value>
</data>
<data name="StrMeterAppUpToDate" xml:space="preserve">
<value>Version ist aktuell</value>
</data>
<data name="StrMeterAppInstallationRequired" xml:space="preserve">
<value>Installation notwendig</value>
</data>
<data name="StrMeterAppVersionOutdated" xml:space="preserve">
<value>Version ist unterschiedlich</value>
</data>
<data name="StrInvalidCrc" xml:space="preserve">
<value>Prüfsummenfehler (CRC)</value>
</data>
<data name="StrMeterAppErasureRequired" xml:space="preserve">
<value>Lösche beim Abschlußvorgang</value>
</data>
<data name="StrMeterAppDownloadSucceeded" xml:space="preserve">
<value>Übernehme beim Abschlußvorgang</value>
</data>
<data name="StrMeterAppDownloadSuspicious" xml:space="preserve">
<value>Überprüfung notwendig</value>
</data>
<data name="StrMeterAppSuccessfulErased" xml:space="preserve">
<value>Löschen erforderlich</value>
</data>
<data name="StrMeterAppSuccessfulUpdated" xml:space="preserve">
<value>Aktualisierung war erfolgreich</value>
</data>
<data name="StrMeterAppDownloadFailed" xml:space="preserve">
<value>Dateiübertragung fehlgeschlagen</value>
</data>
<data name="StrMeterAppDownloadActive" xml:space="preserve">
<value>EINSPIELEN...</value>
</data>
<data name="StrFileAppInvalid" xml:space="preserve">
<value>Ungültige Aktualisierungsdatei</value>
</data>
</root>
@@ -0,0 +1,159 @@
<?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="StrMeterAppNotInstalled" xml:space="preserve">
<value>Application not required</value>
</data>
<data name="StrMeterAppUpToDate" xml:space="preserve">
<value>Up To Date</value>
</data>
<data name="StrMeterAppInstallationRequired" xml:space="preserve">
<value>Installation required</value>
</data>
<data name="StrMeterAppVersionOutdated" xml:space="preserve">
<value>Update required</value>
</data>
<data name="StrInvalidCrc" xml:space="preserve">
<value>CRC ERROR</value>
</data>
<data name="StrMeterAppErasureRequired" xml:space="preserve">
<value>Erase - Trigger upgrade required</value>
</data>
<data name="StrMeterAppDownloadSucceeded" xml:space="preserve">
<value>Burn - Trigger upgrade required</value>
</data>
<data name="StrMeterAppDownloadSuspicious" xml:space="preserve">
<value>Verification required</value>
</data>
<data name="StrMeterAppSuccessfulErased" xml:space="preserve">
<value>Erasure succeeded</value>
</data>
<data name="StrMeterAppSuccessfulUpdated" xml:space="preserve">
<value>Update succeeded</value>
</data>
<data name="StrMeterAppDownloadFailed" xml:space="preserve">
<value>Download failed</value>
</data>
<data name="StrMeterAppDownloadActive" xml:space="preserve">
<value>DOWNLOAD...</value>
</data>
<data name="StrFileAppInvalid" xml:space="preserve">
<value>INVALID UPDATE FILE</value>
</data>
</root>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
@@ -0,0 +1,260 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0E524D43-7B66-45CD-AA74-41D51AFC3D99}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="EventArguments\BaseDataEventArgs.cs" />
<Compile Include="EventArguments\CalibDataEventArgs.cs" />
<Compile Include="EventArguments\FlowDataEventArgs.cs" />
<Compile Include="EventArguments\RegisterUpdateEventArgs.cs" />
<Compile Include="EventArguments\RequestResponseDataEventArgs.cs" />
<Compile Include="MeasurementRecords\CalibrationRecord.cs" />
<Compile Include="MeasurementRecords\FlowTestRecord.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RadioConfigurationParams.cs" />
<Compile Include="RegisterToDb.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1B02C79E-0B19-43E2-8F6B-71EF0C786C97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Metrology\Measurements\Measurements.csproj">
<Project>{6CCDB656-C676-4360-BABA-C4596AAD175D}</Project>
<Name>Measurements</Name>
</ProjectReference>
<ProjectReference Include="..\Registers\Registers.csproj">
<Project>{F4AAF7E6-7333-4284-B735-E32DEB076C64}</Project>
<Name>Registers</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -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;
}
}
@@ -0,0 +1,20 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class CalibDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// Stream record for calibration on channel
/// </summary>
public CalibrationRecord CalibChl;
/// <inheritdoc />
public override Object GetEventData()
{
return CalibChl;
}
}
}
@@ -0,0 +1,20 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class FlowDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// new record from Stream
/// </summary>
public FlowTestRecord NewData;
/// <inheritdoc />
public override Object GetEventData()
{
return NewData;
}
}
}
@@ -0,0 +1,31 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class RegisterUpdatedEventArgs : EventArgs
{
/// <inheritdoc />
/// <summary>
/// Ctor with base record
/// </summary>
/// <param name="register">base record <see cref="F:Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments.RegisterUpdatedEventArgs.Register" /></param>
/// <param name="value">base record <see cref="F:Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments.RegisterUpdatedEventArgs.Value" /></param>
public RegisterUpdatedEventArgs(RegisterDefinition register, Byte[] value)
{
Register = register;
Value = value;
}
/// <summary>
/// Register witch has updated
/// </summary>
public RegisterDefinition Register;
/// <summary>
/// New Value in Register
/// </summary>
public Byte[] Value;
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
/// <summary>
/// EventArgs for Request Responses
/// </summary>
public class RequestResponseDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// Request response from meter
/// </summary>
public List<Byte> RequestResponseData;
/// <inheritdoc />
public override Object GetEventData()
{
return RequestResponseData;
}
}
}
@@ -0,0 +1,152 @@
using System;
using System.Text;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Metrology.Measurements;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords
{
/// <inheritdoc />
/// <summary>
/// hold streaming record for protocol H (contains calibration record)
/// </summary>
public class CalibrationRecord : MeasurementRecord
{
/// <summary>
/// record validation
/// </summary>
public UInt16 Validation;
/// <summary>
/// total time of flight in seconds
/// </summary>
public Double TotalTimeOfFlightS;
/// <summary>
/// delta time of flight in seconds
/// </summary>
public Double DeltaTimeOfFlightS;
/// <summary>
/// total time of flight in cordonel units
/// </summary>
public Int32 RAWTotalTimeOfFlight;
/// <summary>
/// delta time of flight in cordonel units
/// </summary>
public Int32 RAWDeltaTimeOfFlight;
/// <summary>
/// volume scale (default: 1024) means
/// 1024digits = 1ml
/// </summary>
public Double VolumeScaleRawPerMl;
/// <summary>
/// volume factor to convert raw to m³
/// uses 1E-6 (ml to m³) / VolumeScaleRawPerMl
/// </summary>
public Double VolumeFactorRawToQm;
/// <summary>
/// raw volume between two samples
/// </summary>
public Double DeltaVolumeRaw;
/// <summary>
/// calculated out of dRawVolume * VolumeFactorRawToQm .
/// in cubic meters
/// </summary>
public Double DeltaVolumeQm;
/// <summary>
/// accumulated raw volume
/// </summary>
public Double AccuVolumeRaw;
/// <summary>
/// sample interval between two samples in seconds
/// </summary>
public Double SampleIntervalS;
/// <summary>
/// high threshold amplitude in V
/// </summary>
public Double AmplitudeUpV;
/// <summary>
/// low threshold amplitude in V
/// </summary>
public Double AmplitudeDownV;
/// <summary>
/// high ratio for pulse width
/// </summary>
public Double PulseWidthRatioUp;
/// <summary>
/// low ratio for pulse width
/// </summary>
public Double PulseWidthRatioDown;
/// <summary>
/// raw temperature
/// </summary>
public Double TemperatureRaw;
/// <summary>
/// temperature scale
/// </summary>
public Double TemperaturePowFactor;
/// <summary>
/// calculated temperature in degree C
/// </summary>
public Double TemperatureDegC;
/// <summary>
/// Get result as string
/// </summary>
/// <returns></returns>
public override String ToString()
{
var sb = new StringBuilder();
sb.Append($"Channel={Channel}").Append(",")
.Append($"Validation={Validation}").Append(",")
.Append($"TotalTimeOfFlightS={TotalTimeOfFlightS}").Append(",")
.Append($"DeltaTimeOfFlightS={DeltaTimeOfFlightS}").Append(",")
.Append($"DeltaVolumeRaw={DeltaVolumeRaw}").Append(",")
.Append($"VolumeScaleRawPerMl={VolumeScaleRawPerMl}").Append(",")
.Append($"VolumeFactorRawToQm={VolumeFactorRawToQm}").Append(",")
.Append($"DeltaVolumeRaw={DeltaVolumeRaw}").Append(",")
.Append($"DeltaVolumeQm={DeltaVolumeQm}").Append(",")
.Append($"AccuVolumeRaw={AccuVolumeRaw}").Append(",")
.Append($"VolumeCm={VolumeCm}").Append(",")
.Append($"AccuDutOverflowVolumeCm={OverflowVolumeCm}").Append(",")
.Append($"SampleIntervalS={SampleIntervalS}").Append(",")
.Append($"AmplitudeUpV={AmplitudeUpV}").Append(",")
.Append($"AmplitudeDownV={AmplitudeDownV}").Append(",")
.Append($"PulseWidthRatioUp={PulseWidthRatioUp}").Append(",")
.Append($"PulseWidthRatioDown={PulseWidthRatioDown}").Append(",")
.Append($"TemperatureRaw={TemperatureRaw}").Append(",")
.Append($"TemperaturePowFactor={TemperaturePowFactor}").Append(",")
.Append($"TemperatureDegC={TemperatureDegC}").Append(",")
.Append($"TimeS={TimeS}").Append(",")
.Append($"OverflowTimeS={OverflowTimeS}").Append(",")
.Append($"CRC={Crc}").Append(",")
.Append($"IsValid={IsValid}").Append(",")
.Append($"ReceivedTimeUtc={ReceivedTime}").Append(",")
.Append($"DecodedTimeUtc={DecodedTime}").Append(",")
.Append($"SyncMarkRecord={SyncMarkRecord}");
return sb.ToString();
}
}
}
@@ -0,0 +1,158 @@
//using System;
//using System.Text;
//using Xylem.Common.CommonCore.Consts;
//using Xylem.Common.Metrology.Measurements;
//namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords
//{
// /// <inheritdoc />
// /// <summary>
// /// struct to hold Led record for protocol f (contains measurement record)
// /// </summary>
// public class FlowTestRecord : IMeasurementRecord
// {
// /// <summary>
// /// actual volume in cubic meters
// /// </summary>
// public Double DisplayVolumeQm;
// /// <summary>
// /// Overflow volume at Display
// /// </summary>
// public Double DisplayDutOverflowVolumeCm;
// /// <summary>
// /// time converted to seconds
// /// </summary>
// public Double TimeS;
// /// <summary>
// /// Overflow time
// /// </summary>
// public Double OverflowTimeS;
// /// <summary>
// /// CRC16 CCITT
// /// </summary>
// public UInt16 Crc;
// /// <summary>
// /// record is valid
// /// </summary>
// public Boolean IsValid;
// /// <summary>
// /// Mark package as start, end, intermediate or not sync
// /// </summary>
// public SyncMarkRecord SyncMarkRecord;
// /// <summary>
// /// time when package was decoded
// /// </summary>
// public DateTimeOffset DecodedTime;
// /// <summary>
// /// time when package was received
// /// </summary>
// public DateTimeOffset ReceivedTime;
// public Double VolumeCm { get; set; }
// public Double OverflowVolumeCm { get; set; }
// /// <summary>
// /// Get result as string
// /// </summary>
// /// <returns></returns>
// public override String ToString()
// {
// var sb = new StringBuilder();
// sb = sb.Append($"DisplayVolumeQm={DisplayVolumeQm}").Append(",")
// .Append($"DisplayDutOverflowVolumeCm={DisplayDutOverflowVolumeCm}").Append(",")
// .Append($"TimeS={TimeS}").Append(",")
// .Append($"OverflowTimeS={OverflowTimeS}").Append(",")
// .Append($"CRC={Crc}").Append(",")
// .Append($"IsValid={IsValid}").Append(",")
// .Append($"ReceivedTimeUtc={ReceivedTime}").Append(",")
// .Append($"DecodedTimeUtc={DecodedTime}").Append(",")
// .Append($"SyncMarkRecord={SyncMarkRecord}");
// return sb.ToString();
// }
// /// <inheritdoc />
// public Double GetVolumeCm()
// {
// return DisplayVolumeQm;
// }
// /// <inheritdoc />
// public Double GetTimeS()
// {
// return TimeS;
// }
// /// <inheritdoc />
// public SyncMarkRecord GetDataSyncMark()
// {
// return SyncMarkRecord;
// }
// /// <inheritdoc />
// public Int32 GetChannel()
// {
// //channel 0 is for the entire device without a physical channel assignment
// return 0;
// }
// /// <inheritdoc />
// public Double GetOverflowVolumeCm()
// {
// return DisplayDutOverflowVolumeCm;
// }
// /// <inheritdoc />
// public Double GetOverflowTimeS()
// {
// return OverflowTimeS;
// }
// /// <inheritdoc />
// public Object Clone()
// {
// return MemberwiseClone();
// }
// }
//}
using System;
using System.Text;
using Xylem.Common.Metrology.Measurements;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords
{
/// <inheritdoc />
/// <summary>
/// struct to hold Led record for protocol F (contains measurement record)
/// </summary>
public class FlowTestRecord : MeasurementRecord
{
/// <summary>
/// Get result as string
/// </summary>
/// <returns></returns>
public override String ToString()
{
var sb = new StringBuilder();
sb = sb.Append($"VolumeCm={VolumeCm}").Append(",")
.Append($"OverflowVolumeCm={OverflowVolumeCm}").Append(",")
.Append($"TimeS={TimeS}").Append(",")
.Append($"OverflowTimeS={OverflowTimeS}").Append(",")
.Append($"CRC={Crc}").Append(",")
.Append($"IsValid={IsValid}").Append(",")
.Append($"ReceivedTimeUtc={ReceivedTime}").Append(",")
.Append($"DecodedTimeUtc={DecodedTime}").Append(",")
.Append($"SyncMarkRecord={SyncMarkRecord}");
return sb.ToString();
}
}
}
@@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
using System.Reflection;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die einer Assembly zugeordnet sind.
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages")]
[assembly: AssemblyDescription("Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages")]
[assembly: Guid("DBE76882-217A-4446-89BB-8F5E8CD8CB65")]
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages
{
//obs
public class RadioConfigurationParams
{
public string PcbId;
public UInt32 SerialNumber;
public UInt32 RadioAddress;
public UInt32 PowerLevel;
public UInt32 PowerLevelOption;
public UInt32 ImpedanceCodeNew;
public UInt32 ImpedanceCodeOption;
public Byte[] EncryptionKey;
public short? FrqOffset { get; set; }
}
}
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages
{
public class RegisterToDb
{
public RegisterToDb(byte[] adresse, byte[] value)
{
Adresse = adresse;
Value = value;
}
public byte[] Adresse;
public byte[] Value;
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,87 @@
using System;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore;
using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
/// <summary>
/// Setup of communication timeouts and retries for request and streaming protocol,
/// this timeout starts the retry.
///
/// The individual timings of the hardware will be covert by the
/// <see cref="TransmitPortSettings.ResponseTimeoutMs"/>
/// injected by the different transmit protocols:
/// <see cref="IrdaTransmitProtocol"/>
/// <see cref="LedTransmitProtocol"/>
/// <see cref="RfidTransmitProtocol"/>
/// <see cref="UartTransmitProtocol"/>
/// </summary>
public static class CommunicationConfig
{
private static Int32 _requestRetries = DefaultRequestRetries;
/// <summary>
/// Maximum retries for one command at request protocol
/// at missing response.
/// </summary>
public static Int32 RequestRetries
{
get => _requestRetries;
set => _requestRetries = value > MaxRequestRetries ? MaxRequestRetries : value;
}
/// <summary>
/// Acceptable error code as valid answer to skip retries,
/// this might be 0x0004 for not installed application.
/// </summary>
public const UInt16 SkipRetryErrorCode = 0x0004;
/// <summary>
/// Default retries for one command at request protocol
/// at missing response.
/// </summary>
public const Int32 DefaultRequestRetries = 2;
/// <summary>
/// Limit retries to this value.
/// </summary>
public const Int32 MaxRequestRetries = 6;
/// <summary>
/// Send delay between records in milliseconds before
/// new record is going to be send or between retries,
/// this time is independent of the timeout, it is
/// used to delay the next send record after successful
/// response from meter.
/// </summary>
public const Int32 InterRecordSendDelayMs = 5;
/// <summary>
/// Timeout before retry will be initiated in milliseconds
/// for the response of the request protocol. This is an
/// offset value, the transmission specific timing will be
/// added from the transmit protocol timeout. This timeout
/// will be multiplied with the (retry + 1)!
/// </summary>
public const Int32 ResponseTimeoutMs = 100;
/// <summary>
/// Timeout before retry will be initiated if the meter is
/// not ready indicated by a wakeup-message or decoding error.
/// </summary>
public const Int32 BusyTimeoutMs = 1000;
/// <summary>
/// Time to avoid automatic lock off of genesis device due to
/// missing communication with request protocol in seconds
/// </summary>
public const Int32 KeepSessionTimeS = 60 * 3;
/// <summary>
/// Time to update intermediate record for overflow detection
/// during a measurement and update of short-term measurement
/// in seconds
/// </summary>
public const Int32 MeasurementUpdateTimeS = 10;
}
}
@@ -0,0 +1,24 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const
{
public static class MeterConfig
{
/// <summary>
/// Calibration Factor on init from meter
/// Is also the factor to convert CalibrationFactors to readable number,
/// this is the default if the ProccessConfig.json file cannot be read.
/// </summary>
public const UInt16 CalibrationDefault = 15625;
/// <summary>
/// Channels for calibration
/// </summary>
public const Int32 CalibChannels = 3;
/// <summary>
/// Maximum calibration value in percent
/// </summary>
public const Double DefaultMaxCalibFactorTolerancePercent = 100.0;
}
}
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F013A56B-7BE6-4852-9CD8-B971CD1ADD0C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>false</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\NLog.4.5.6\lib\net40-client\NLog.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="CommunicationConfig.cs" />
<Compile Include="Const\MeterConfig.cs" />
<Compile Include="ProcessConfig.cs" />
<Compile Include="PortConfig.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SlotConfig.cs" />
<Compile Include="SlotType.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,86 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of meter
/// </summary>
public class MeterConfig
{
/// <summary>
/// ctor
/// </summary>
public MeterConfig()
{
UseRegisterWatchService = false;
UseErrorLogger = false;
}
/// <inheritdoc />
public MeterConfig(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
if (!File.Exists(configFile))
{
UseRegisterWatchService = false;
UseErrorLogger = false;
return;
}
}
using (var tr = new StreamReader(configFile))
{
var meterConfig = JsonConvert.DeserializeObject<MeterConfig>(tr.ReadToEnd());
UseRegisterWatchService = meterConfig.UseRegisterWatchService;
RegisterWatchServiceUrl = meterConfig.RegisterWatchServiceUrl;
UseMinMaxCheck = meterConfig.UseMinMaxCheck;
UseErrorLogger = meterConfig.UseErrorLogger;
ErrorLoggerServiceUrl = meterConfig.ErrorLoggerServiceUrl;
UseCalibrationLogger = meterConfig.UseCalibrationLogger;
CalibrationLoggerServiceUrl = meterConfig.CalibrationLoggerServiceUrl;
}
}
/// <summary>
///
/// </summary>
public Boolean UseRegisterWatchService { get; set; }
/// <summary>
///
/// </summary>
public String RegisterWatchServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseMinMaxCheck { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseErrorLogger { get; set; }
/// <summary>
///
/// </summary>
public String ErrorLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseCalibrationLogger { get; set; }
/// <summary>
///
/// </summary>
public String CalibrationLoggerServiceUrl { get; set; }
}
}
@@ -0,0 +1,65 @@
using System;
using System.IO.Ports;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
/// <summary>
/// Collection of port settings
/// </summary>
public struct PortConfig
{
private String _portName;
/// <summary>
/// Assigns a port name and creates the serial port object
/// Port name e.g. "COM2"
/// </summary>
public String PortName
{
get
{
//if the serial port object does not exists
return GetSerialPort() == null ? "NA" : _portName;
}
set
{
if (value == null) return;
_portName = value;
//create a serial port object
if (GetSerialPort() == null)
{
SetSerialPort(new SerialPort(value));
}
else
{
GetSerialPort().PortName = value;
}
}
}
/// <summary>
/// setup of port type from name (referenced as "Type" in config file)
/// </summary>
public String Type { get; set; }
private SerialPort _serialPort;
/// <summary>
/// using dotNets <see cref="GetSerialPort()"/> for connection
/// </summary>
public SerialPort GetSerialPort()
{
return _serialPort;
}
/// <summary>
/// using dotNets <see cref="GetSerialPort()"/> for connection
/// </summary>
private void SetSerialPort(SerialPort value)
{
_serialPort = value;
}
}
}
@@ -0,0 +1,114 @@
using System;
using System.ComponentModel;
using System.IO;
using Newtonsoft.Json;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of meter
/// </summary>
public class ProccessConfig
{
/// <summary>
/// ctor
/// </summary>
public ProccessConfig()
{
UseRegisterWatchService = false;
UseErrorLogger = false;
AutoUpdateFiles = false;
ProductionMode = true;
}
/// <inheritdoc />
public ProccessConfig(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
if (!File.Exists(configFile))
{
UseRegisterWatchService = false;
UseErrorLogger = false;
return;
}
}
using (var tr = new StreamReader(configFile))
{
var meterConfig = JsonConvert.DeserializeObject<ProccessConfig>(tr.ReadToEnd());
UseRegisterWatchService = meterConfig.UseRegisterWatchService;
RegisterWatchServiceUrl = meterConfig.RegisterWatchServiceUrl;
UseMinMaxCheck = meterConfig.UseMinMaxCheck;
UseErrorLogger = meterConfig.UseErrorLogger;
ErrorLoggerServiceUrl = meterConfig.ErrorLoggerServiceUrl;
UseCalibrationLogger = meterConfig.UseCalibrationLogger;
CalibrationLoggerServiceUrl = meterConfig.CalibrationLoggerServiceUrl;
AutoUpdateFiles = meterConfig.AutoUpdateFiles;
ProductionMode = meterConfig.ProductionMode;
}
}
public void Update(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
}
File.WriteAllText(file, JsonConvert.SerializeObject(this));
}
/// <summary>
///
/// </summary>
public Boolean UseRegisterWatchService { get; set; }
/// <summary>
///
/// </summary>
public String RegisterWatchServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseMinMaxCheck { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseErrorLogger { get; set; }
/// <summary>
///
/// </summary>
public String ErrorLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseCalibrationLogger { get; set; }
/// <summary>
///
/// </summary>
public String CalibrationLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public bool AutoUpdateFiles { get; set; }
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool ProductionMode { get; set; } = true;
}
}
@@ -0,0 +1,114 @@
using System;
using System.ComponentModel;
using System.IO;
using Newtonsoft.Json;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of meter
/// </summary>
public class ProcessConfig
{
/// <summary>
/// ctor
/// </summary>
public ProcessConfig()
{
UseRegisterWatchService = false;
UseErrorLogger = false;
AutoUpdateFiles = false;
ProductionMode = true;
}
/// <inheritdoc />
public ProcessConfig(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
if (!File.Exists(configFile))
{
UseRegisterWatchService = false;
UseErrorLogger = false;
return;
}
}
using (var tr = new StreamReader(configFile))
{
var meterConfig = JsonConvert.DeserializeObject<ProcessConfig>(tr.ReadToEnd());
UseRegisterWatchService = meterConfig.UseRegisterWatchService;
RegisterWatchServiceUrl = meterConfig.RegisterWatchServiceUrl;
UseMinMaxCheck = meterConfig.UseMinMaxCheck;
UseErrorLogger = meterConfig.UseErrorLogger;
ErrorLoggerServiceUrl = meterConfig.ErrorLoggerServiceUrl;
UseCalibrationLogger = meterConfig.UseCalibrationLogger;
CalibrationLoggerServiceUrl = meterConfig.CalibrationLoggerServiceUrl;
AutoUpdateFiles = meterConfig.AutoUpdateFiles;
ProductionMode = meterConfig.ProductionMode;
}
}
public void Update(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
}
File.WriteAllText(file, JsonConvert.SerializeObject(this));
}
/// <summary>
///
/// </summary>
public Boolean UseRegisterWatchService { get; set; }
/// <summary>
///
/// </summary>
public String RegisterWatchServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseMinMaxCheck { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseErrorLogger { get; set; }
/// <summary>
///
/// </summary>
public String ErrorLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseCalibrationLogger { get; set; }
/// <summary>
///
/// </summary>
public String CalibrationLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public bool AutoUpdateFiles { get; set; }
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool ProductionMode { get; set; } = true;
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig")]
[assembly: AssemblyDescription("")]
//[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GenesisConfig")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("4FBC1BEE-D9E2-4326-A164-79340CD4090F")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyVersion("1.0.0.0")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,30 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
/// <summary>
/// Container for port configuration
/// </summary>
public class SlotConfig
{
/// <summary>
/// Slot number
/// </summary>
public Int32 Slot { get; set; }
/// <summary>
/// Request port settings
/// </summary>
public PortConfig Request { get; set; }
/// <summary>
/// Streaming port settings
/// </summary>
public PortConfig Streaming { get; set; }
/// <summary>
/// Streaming port settings
/// </summary>
public SlotType Type { get; set; }
}
}
@@ -0,0 +1,8 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig
{
public enum SlotType
{
DutMeter,
TemperatureMeter,
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net40" />
</packages>
@@ -0,0 +1,107 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
/// <summary>
/// Calibration content
/// </summary>
public class CalibFactor
{
/// <summary>
/// has been calibrated
/// </summary>
public Boolean IsCalculated;
/// <summary>
/// Channel for calibration
/// </summary>
public Int32 Channel { get; }
/// <summary>
/// Register to store the calibration
/// </summary>
public String RegisterToStoreCalibFactor { get; }
/// <summary>
/// Ctor for calibration factor
/// </summary>
/// <param name="channel"></param>
/// <param name="registerToStoreCalibFactor"></param>
public CalibFactor(Int32 channel, String registerToStoreCalibFactor)
{
Channel = channel;
RegisterToStoreCalibFactor = registerToStoreCalibFactor;
IsCalculated = false;
}
private UInt16 _meterRawCalibFactor = MeterConfig.CalibrationDefault;
/// <summary>
/// set the user readable relative (around 1.0) calibration factor
/// and convert it to the raw calibration factor for the meter
/// </summary>
/// <param name="relativeCalibFactor"></param>
/// <exception cref="Exception"></exception>
public void BuildMeterCalibFactor(Double relativeCalibFactor)
{
try
{
var tmp = Math.Round(relativeCalibFactor * GetMeterRawCalibFactor(), 0);
_meterRawCalibFactor = Convert.ToUInt16(tmp);
}
catch (Exception ex)
{
throw new Exception("Unable to set calibration factor", ex);
}
}
/// <summary>
/// Read the relative (around 1.0) calibration factor calculated from the actual
/// meter calibration factor divided by the default calibration factor
/// </summary>
/// <returns></returns>
public Double GetRelativeCalibFactor()
{
Double calibFactor = GetMeterRawCalibFactor();
var calibDivider = GetMeterRawCalibFactorDefault();
if (0 != calibDivider)
{
calibFactor /= calibDivider;
}
else
{
throw new ApplicationException("Default calibration factor row value is 0");
}
return calibFactor;
}
/// <summary>
/// Genesis internal raw default value (around 15625)
/// </summary>
/// <returns></returns>
public UInt16 GetMeterRawCalibFactorDefault()
{
return MeterConfig.CalibrationDefault;
}
/// <summary>
/// Set the meter raw calibration factor (around 15625)
/// </summary>
/// <param name="meterRawCalibFactor"></param>
public void SetMeterRawCalibFactor(UInt16 meterRawCalibFactor)
{
_meterRawCalibFactor = meterRawCalibFactor;
}
/// <summary>
/// Get the meter raw calibration factor (around 15625)
/// </summary>
/// <returns></returns>
public UInt16 GetMeterRawCalibFactor()
{
return _meterRawCalibFactor;
}
}
}
@@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts
{
class Applications
{
}
}
@@ -0,0 +1,33 @@
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts
{
/// <summary>
/// Streaming modes that supported by genesis meter.
/// Set meter to this Streaming mode means that , the meter pushes mode-specific record to port without any responses
/// </summary>
public enum LedMode
{
/// <summary>
/// Test mode with raw record to log. Should give one <see cref="FlowTestRecord"/>
/// 3 times <see cref="CalibrationRecord"/> and repeat this till led mode is switch
/// </summary>
FlowTestAndCalibData = 6,
/// <summary>
/// Test mode should give <see cref="FlowTestRecord"/> out
/// </summary>
FlowTestData = 4,
/// <summary>
/// Calibration mode should give <see cref="CalibrationRecord"/> out
/// </summary>
CalibData = 3,
/// <summary>
/// Deactivate streaming
/// </summary>
Off = 0,
/// <summary>
/// not set or an new not supported Streaming mode
/// </summary>
Unknown = -1
}
}
@@ -0,0 +1,453 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Xylem.Common.Hardware.WaterMeter.Genesis.Applications;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.RegisterProperties;
using Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
/// <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;
/// <summary>
/// Applications defined in configuration.json file
/// </summary>
public List<ApplicationDefinition> ConfigApplicationDefinitions;
/// <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");
}
Convert(File.ReadAllText(configFilePath));
}
/// <summary>
/// Convert file content to register list
/// </summary>
/// <param name="fileContent"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private void Convert(String fileContent)
{
var applicationGroups = (JObject)JsonConvert.DeserializeObject(fileContent);
ConfigRegistersDefinitions = new List<IRegister>();
ConfigApplicationDefinitions = new List<ApplicationDefinition>();
try
{
//extract information of every application
foreach (var currentApplication in applicationGroups)
{
var o = (JObject)currentApplication.Value;
var application = new ApplicationDefinition();
Byte applicationAddress = 0;
foreach (var groupProp in o)
{
try
{
if (groupProp.Key == "id")
{
applicationAddress = groupProp.Value.Value<Byte>();
application.AppName = currentApplication.Key;
application.AppId = applicationAddress;
ConfigApplicationDefinitions.Add(application);
}
else if (groupProp.Key == "registers")
{
foreach (var registers in groupProp.Value)
{
var register = ((JProperty)registers);
try
{
foreach (var registerJ in register.Value)
{
JProperty reg = ((JProperty)registerJ);
Byte? registerAddress = null;
if (reg.Name == "id")
{
registerAddress = (Byte)reg.Value;
}
if (reg.Name == "details")
{
var details = ((JArray)reg.Value);
foreach (var jdetail in details.Children())
{
var actualRegister = new RegisterDefinition();
var idTag = reg.Parent.First(f => ((JProperty)f).Name == "id");
var id = (Byte)(JProperty)idTag;//.Value<int>();
if (registerAddress.HasValue)
{
throw new Exception("invalid json cast");
}
actualRegister.RegAddressInApp = id;
actualRegister.AppName = currentApplication.Key;
actualRegister.AppAddress = applicationAddress;
actualRegister.RegisterDetail = new RegisterDetail();
actualRegister.RegisterName = register.Name;
var detailO = (JObject)jdetail;
JProperty detail;
detail = detailO.Property("privilege");
if (detail.Name == "privilege")
{
actualRegister.RegisterDetail.Privilege = JsonConvert.DeserializeObject<Privilege>(detail.Value.ToString(), Settings);
}
detail = detailO.Property("type");
if (detail.Name == "type")
{
actualRegister.DataType = typeof(String);// detail.Value.ToString();
switch (detail.Value.ToString().ToLower())
{
case "bool_t":
actualRegister.DataType = typeof(Boolean);
break;
case "rpc":
actualRegister.DataType = typeof(Rpc);
break;
case "string":
actualRegister.DataType = typeof(String);
break;
case "uint32_t":
actualRegister.DataType = typeof(UInt32);
break;
case "time_t":
actualRegister.DataType = typeof(TimeT);
break;
case "status_t":
actualRegister.DataType = typeof(StatusT);
break;
case "uint16_t":
actualRegister.DataType = typeof(UInt16);
break;
case "uint8_t":
actualRegister.DataType = typeof(byte);
break;
case "enum8":
actualRegister.DataType = typeof(Enum8);
break;
case "uint64_t":
actualRegister.DataType = typeof(UInt64);
break;
case "uint96_t":
actualRegister.DataType = typeof(UInt96);
break;
case "int8_t":
actualRegister.DataType = typeof(sbyte);
break;
case "int32_t":
actualRegister.DataType = typeof(Int32);
break;
case "int64_t":
actualRegister.DataType = typeof(Int64);
break;
case "int16_t":
actualRegister.DataType = typeof(Int16);
break;
}
}
detail = detailO.Property("description");
if (detail.Name == "description")
{
actualRegister.RegisterDetail.Description = detail.Value.ToString();
}
detail = detailO.Property("version");
if (detail.Name == "version")
{
actualRegister.RegisterDetail.Version = JsonConvert.DeserializeObject<FwVersion>(detail.Value.ToString());
}
detail = detailO.Property("values");
if (detail != null && detail.Name == "values")
{
foreach (var dValues in detail.Children().First())
{
switch (((JProperty)dValues).Name)
{
case "default":
actualRegister.Default = (Int64)((JProperty)dValues).Value;
break;
case "maximum":
actualRegister.Maximum = (Int64)((JProperty)dValues).Value;
break;
case "minimum":
actualRegister.Minimum = (Int64)((JProperty)dValues).Value;
break;
}
}
}
detail = detailO.Property(StaticType.StaticTypeFieldIdentifier);
if (detail != null && detail.Name == StaticType.StaticTypeFieldIdentifier)
{
actualRegister.RestoreCapability = new StaticType(detail.Value.ToString());
}
ConfigRegistersDefinitions.Add(actualRegister);
}
}
}
}
catch (Exception e)
{
//todo log ex
}
}
}
}
catch (Exception e)
{
//todo log ex
}
}
}
}
catch (Exception ex)
{
}
////tofile
var sb = new StringBuilder();
var lsitOfRegisters = new List<RegisterDefinition>();
foreach (var item in ConfigRegistersDefinitions)
{
if (item is RegisterDefinition Reg)
{
if (lsitOfRegisters.Any(a => a.GetIdent() == Reg.GetIdent()))
{
var oldItem = lsitOfRegisters.First(a => a.GetIdent() == Reg.GetIdent());
if (oldItem.RegisterDetail.Version.Last < Reg.RegisterDetail.Version.Last)
{
lsitOfRegisters.Remove(oldItem);
lsitOfRegisters.Add(Reg);
}
}
else
{
lsitOfRegisters.Add(Reg);
}
}
}
//#region create a file for my american friends
//var sbCSV = new StringBuilder();
//sbCSV.AppendLine("Filter;Ident;First;Last");
//foreach (var item in ConfigRegistersDefinitions)
//{
// if (item is RegisterDefinition Reg)
// {
// sbCSV.AppendLine($"ALL;{Reg.GetIdent()};{Reg.RegisterDetail.Version.First}; {Reg.RegisterDetail.Version.Last};");
// }
//}
//foreach (var item in lsitOfRegisters)
//{
// sbCSV.AppendLine($"InUse;{item.GetIdent()};{item.RegisterDetail.Version.First}; {item.RegisterDetail.Version.Last};");
//}
//foreach (var item in lsitOfRegisters)
//{
// sbCSV.AppendLine($"InUse;{item.GetIdent()};{item.RegisterDetail.Version.First}; {item.RegisterDetail.Version.Last};");
//}
//var k = sbCSV.ToString();
//sbCSV.AppendLine("###");
//sbCSV.AppendLine("###");
//sbCSV.AppendLine("###");
//sbCSV.AppendLine("App;Version");
//foreach (var item in lsitOfRegisters)
//{
// sbCSV.AppendLine($"InUse;{item.GetIdent()};{item.RegisterDetail.Version.First}; {item.RegisterDetail.Version.Last};");
//}
//foreach (var grpReg in lsitOfRegisters.GroupBy(g => g.AppName))
//{
// sb.AppendLine($"public static class {grpReg.Key} {{");
// foreach (var item in grpReg)
// {
// sbCSV.Append($"{grpReg.Key};{item.RegisterName};{item.DataType.Name};{item.RegisterDetail.Description}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl1.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl2.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl3.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl4.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl5.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl6.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl7.ToString()}");
// sbCSV.Append($";{ item.RegisterDetail.Privilege.Lvl8.ToString()}");
// sb.AppendLine($"public static readonly RegisterDefinition {item.RegisterName} = new RegisterDefinition() {{");
// sb.AppendLine($"DataType = typeof({item.DataType.Name}),");
// sb.AppendLine($"Address = new byte[2] {{ (byte){item.RegisterAddress[0]}, (byte){item.RegisterAddress[1]} }},");
// sb.AppendLine($"Default = new long?(");
// if (item.Default.HasValue)
// {
// sb.Append(item.Default.Value);
// sbCSV.Append($";{item.Default.Value}");
// }
// else
// {
// sbCSV.Append(";-");
// }
// sb.Append($"),");
// sb.AppendLine($"Maximum = new long?(");
// if (item.Maximum.HasValue)
// {
// sb.Append(item.Maximum.Value);
// sbCSV.Append($";{item.Maximum.Value}");
// }
// else
// {
// sbCSV.Append(";-");
// }
// sb.Append($"),");
// sb.AppendLine($"Minimum = new long?(");
// if (item.Minimum.HasValue)
// {
// sb.Append(item.Minimum.Value);
// sbCSV.Append($";{item.Minimum.Value}");
// }
// else
// {
// sbCSV.Append(";-");
// }
// sb.Append($"),");
// sb.AppendLine($"Detail = new Detail(){{");
// sb.AppendLine($" Description = \"{item.RegisterDetail.Description}\",");
// sb.AppendLine($" Version = new Version(1,{item.RegisterDetail.Version.Last}),");
// sb.AppendLine($" Privilege = new Privilege(){{");
// //
// sb.AppendLine($" Lvl1 = Access.{item.RegisterDetail.Privilege.Lvl1.ToString()},");
// sb.AppendLine($" Lvl2 = Access.{item.RegisterDetail.Privilege.Lvl2.ToString()},");
// sb.AppendLine($" Lvl3 = Access.{item.RegisterDetail.Privilege.Lvl3.ToString()},");
// sb.AppendLine($" Lvl4 = Access.{item.RegisterDetail.Privilege.Lvl4.ToString()},");
// sb.AppendLine($" Lvl5 = Access.{item.RegisterDetail.Privilege.Lvl5.ToString()},");
// sb.AppendLine($" Lvl6 = Access.{item.RegisterDetail.Privilege.Lvl6.ToString()},");
// sb.AppendLine($" Lvl7 = Access.{item.RegisterDetail.Privilege.Lvl7.ToString()},");
// sb.AppendLine($" Lvl8 = Access.{item.RegisterDetail.Privilege.Lvl8.ToString()},");
// sb.AppendLine($" }}");
// sb.AppendLine($"}}");
// sb.AppendLine($"}};");
// sbCSV.AppendLine(";");
// }
// sb.AppendLine($"}}");
//}
//#endregion
//var z = sbCSV.ToString();
//var code = sb.ToString();
}
internal class LvlConverter : JsonConverter
{
public override Boolean CanConvert(Type t) => t == typeof(Access) || t == typeof(Access?);
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);
switch (value)
{
case "NA":
return Access.Na;
case "RO":
return Access.Ro;
case "RW":
return Access.Rw;
case "WO":
return Access.Wo;
}
throw new Exception("Cannot unmarshal type Lvl");
}
public override void WriteJson(JsonWriter writer, Object untypedValue, JsonSerializer serializer)
{
if (untypedValue == null)
{
serializer.Serialize(writer, null);
return;
}
var value = (Access)untypedValue;
switch (value)
{
case Access.Na:
serializer.Serialize(writer, "NA");
return;
case Access.Ro:
serializer.Serialize(writer, "RO");
return;
case Access.Rw:
serializer.Serialize(writer, "RW");
return;
case Access.Wo:
serializer.Serialize(writer, "WO");
return;
}
throw new Exception("Cannot marshal type Lvl");
}
public static readonly LvlConverter Singleton = new LvlConverter();
}
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
LvlConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
@@ -0,0 +1,378 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{04201E95-90A0-4B73-A72F-379739B2A3AE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.xml</DocumentationFile>
<RegisterForComInterop>false</RegisterForComInterop>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>6</LangVersion>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Core.xml</DocumentationFile>
<RegisterForComInterop>true</RegisterForComInterop>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Core.xml</DocumentationFile>
<RegisterForComInterop>true</RegisterForComInterop>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Core.xml</DocumentationFile>
<RegisterForComInterop>true</RegisterForComInterop>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Core.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Core.xml</DocumentationFile>
<RegisterForComInterop>true</RegisterForComInterop>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Core.xml</DocumentationFile>
<RegisterForComInterop>true</RegisterForComInterop>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<GenerateSerializationAssemblies>On</GenerateSerializationAssemblies>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Consts\LedMode.cs" />
<Compile Include="GenesisMeter.cs" />
<Compile Include="CalibFactor.cs" />
<Compile Include="GenesisMeterStreamingQuality.cs" />
<Compile Include="MeterPortScanner.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="GenesisConfigReader.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="RegisterRestorer.cs" />
<Compile Include="_GenesisMeterCalibration.cs" />
<Compile Include="_GenesisMeterMeasurement.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.12.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Net" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\CommonCore.Configuration\CommonCore.Configuration.csproj">
<Project>{9eb1f659-6f73-4f65-bcab-4114fd94f5fc}</Project>
<Name>CommonCore.Configuration</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1B02C79E-0B19-43E2-8F6B-71EF0C786C97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\CommonCore\ThreadWatcher\ThreadWatcher.csproj">
<Project>{94ca71f3-c5d7-4ec6-ba47-fa8eea3c9406}</Project>
<Name>ThreadWatcher</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Logic.PrdoctionToProductMapper\Logic.ProductionToProductMapper.csproj">
<Project>{D0C859D9-D55E-4E85-AC50-0FF31B1CE50D}</Project>
<Name>Logic.ProductionToProductMapper</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Logic\ProductionOrderCore\ProductionOrderCore.csproj">
<Project>{6D2777BB-7A88-466D-A49B-3266F9BF0160}</Project>
<Name>ProductionOrderCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Logic\RelatePcb\RelatePcb.csproj">
<Project>{f581264d-3c20-4e4f-9e74-2606869aaab7}</Project>
<Name>RelatePcb</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Metrology\Measurements\Measurements.csproj">
<Project>{6CCDB656-C676-4360-BABA-C4596AAD175D}</Project>
<Name>Measurements</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\SoftwareAccessHelper\SoftwareAccessHelper.csproj">
<Project>{A33E3C6D-EAAE-4A20-A0EE-F9E6922FD029}</Project>
<Name>SoftwareAccessHelper</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\ByteArrayStyle\ByteArrayStyle.csproj">
<Project>{8A619CD5-6C8D-420C-A132-7946C6F13E6E}</Project>
<Name>ByteArrayStyle</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\Logging\Logging.csproj">
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\ProcessExec\ProcessExec.csproj">
<Project>{2FD60CF1-45CD-4060-B8E5-4F39FBA7F1CB}</Project>
<Name>ProcessExec</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Interfaces\Ports\SerialPorts\SerialPorts.csproj">
<Project>{72037E89-4F63-4B3F-BEFF-4138E578CE99}</Project>
<Name>SerialPorts</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\..\WaterMeterCore\WaterMeterCore.csproj">
<Project>{8203ABB8-38E5-472E-A742-33F38B7A74A6}</Project>
<Name>WaterMeterCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\WaterMeterCore\WaterMeterRegisters\WaterMeterRegisters.csproj">
<Project>{26afcf1e-1287-48b8-a506-32b1e8a0dd0e}</Project>
<Name>WaterMeterRegisters</Name>
</ProjectReference>
<ProjectReference Include="..\Applications\Applications.csproj">
<Project>{315E94B8-B82F-4B25-B5BD-297DF3BC1D54}</Project>
<Name>Applications</Name>
</ProjectReference>
<ProjectReference Include="..\DataPackages\DataPackages.csproj">
<Project>{0E524D43-7B66-45CD-AA74-41D51AFC3D99}</Project>
<Name>DataPackages</Name>
</ProjectReference>
<ProjectReference Include="..\GenesisConfig\GenesisConfig.csproj">
<Project>{F013A56B-7BE6-4852-9CD8-B971CD1ADD0C}</Project>
<Name>GenesisConfig</Name>
</ProjectReference>
<ProjectReference Include="..\Protocols\ProtocolCore\ProtocolCore.csproj">
<Project>{8BDD2789-96D4-43EA-AAD7-0F49D0347998}</Project>
<Name>ProtocolCore</Name>
</ProjectReference>
<ProjectReference Include="..\Protocols\RequestProtocol\RequestProtocol.csproj">
<Project>{D32ED790-7E1E-435B-8619-9F906C992066}</Project>
<Name>RequestProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\Protocols\StreamingProtocol\StreamingProtocol.csproj">
<Project>{29617814-313E-409E-AA5D-59C78A9D4DF1}</Project>
<Name>StreamingProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\Registers\Registers.csproj">
<Project>{F4AAF7E6-7333-4284-B735-E32DEB076C64}</Project>
<Name>Registers</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="configuration.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.de.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.de.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties configuration_1json__JsonSchema="http://json.schemastore.org/bower" />
</VisualStudio>
</ProjectExtensions>
<!-- 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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Ports.SerialPorts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
public class GenesisMeterStreamingQuality : GenesisMeter
{
public QualityResult LastQualityResult;
private StreamingDecoder sd;
public Boolean IsRunning;
private DateTimeOffset? _startTime;
private DateTimeOffset? _stopTime;
private List<String> _collectedRecords;
private int? TillDecodedCount = null;
public void StartStreamingQualityCheck(int? tillDecodedCount = null)
{
if (IsRunning)
{
return;
}
TillDecodedCount = tillDecodedCount;
if (TillDecodedCount.HasValue)
{
sd = new StreamingDecoder(false);
}
if (StreamingPort == null || !(StreamingPort is LedSerialPort))
{
throw new ApplicationException("No valid streaming port");
}
_stopTime = null;
_collectedRecords = new List<String>();
SyncStreamingBuffer(SyncMarkRecord.FlushBuffer);
SyncStreamingBuffer(SyncMarkRecord.DecodeEveryPackage);
((LedSerialPort)StreamingPort).OnRawRecordReceived += GenesisMeterStreamingQuality_OnRawRecordReceived;
_startTime = DateTimeOffset.UtcNow;
LogRawData(true);
IsRunning = true;
}
private void GenesisMeterStreamingQuality_OnRawRecordReceived(Object sender, BasePortDataEventArgs e)
{
_collectedRecords.Add(e.GetData().ToString());
if (TillDecodedCount.HasValue)
{
if (sd.DecodeMsg(e.GetData().ToString()))
{
TillDecodedCount = TillDecodedCount.Value - 1;
if (TillDecodedCount <= 0)
{
IsRunning = false;
LastQualityResult = StopStreamingQualityCheck(10);
}
}
}
}
public QualityResult StopStreamingQualityCheck(Int32 sampleRateHz)
{
_stopTime = DateTimeOffset.UtcNow;
if (!IsRunning && !_startTime.HasValue)
{
return new QualityResult();
}
if (StreamingPort == null || !(StreamingPort is LedSerialPort))
{
throw new ApplicationException("No valid streaming port");
}
LogRawData(false);
((LedSerialPort)StreamingPort).OnRawRecordReceived -= GenesisMeterStreamingQuality_OnRawRecordReceived;
SyncStreamingBuffer(SyncMarkRecord.SkipDecoding);
IsRunning = false;
return new QualityResult(_collectedRecords, _stopTime.Value - _startTime.Value, sampleRateHz);
}
public class QualityResult
{
public readonly Int32 TotalLinesCount;
public readonly Int32 TotalDecodedCount;
public readonly Int32 TotalTransportErrorCount;
public readonly Int32 TotalValidationWithoutErrorsCount;
public readonly Int32 ExpectedLinesCount;
public readonly TimeSpan TotalTime;
public readonly Dictionary<UInt16, Int32> ErrorOccurredCounter;
public QualityResult(List<String> collectedRecords, TimeSpan ts, Int32 sampleRateHz)
{
TotalLinesCount = collectedRecords.Count;
TotalTime = ts;
ExpectedLinesCount = (Int32)(Math.Round( ts.TotalSeconds * sampleRateHz, 0));
ErrorOccurredCounter = new Dictionary<UInt16, Int32>();
foreach (var rawText in collectedRecords)
{
var sd = new StreamingDecoder(false);
if (!sd.DecodeMsg(rawText))
{
TotalTransportErrorCount++;
continue;
}
//here the decoding is valid including the CRC
TotalDecodedCount++;
//the DecodeMsg has assigned either a DataCalib or DataFlowTest data set
var calibrationRecord = sd.DataCalib;
var flowTestRecord = sd.DataFlowTest;
if (flowTestRecord != null)
{
TotalValidationWithoutErrorsCount++;
continue;
}
if (calibrationRecord != null)
{
if (calibrationRecord.Validation == 0)
{
TotalValidationWithoutErrorsCount++;
}
else
{
if (ErrorOccurredCounter.ContainsKey(calibrationRecord.Validation))
{
ErrorOccurredCounter[calibrationRecord.Validation] += 1;
}
else
{
ErrorOccurredCounter.Add(calibrationRecord.Validation, 1);
}
}
}
}
}
public QualityResult()
{
TotalLinesCount = 0;
TotalTime = TimeSpan.Zero;
ErrorOccurredCounter = new Dictionary<UInt16, Int32>();
}
}
}
}
@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using Newtonsoft.Json;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
/// <summary>
/// Port scanner:
/// using the Windows Device Manager listed ports,
/// tries to open the port with an exception if it cannot be accessed (very time consuming),
/// tries to use the request protocol to detect a Genesis device.
/// </summary>
public class MeterPortScanner : IProcessState, IDisposable
{
/// <summary>
/// Genesis for test access
/// </summary>
private GenesisMeter _currentGenesis;
private MeterBatch _meterBatch;
private readonly String _portType;
/// <summary>
/// Auto detected port name
/// </summary>
public String AutoDetectedPortName;
/// <summary>
/// Port scan result event for message dispatcher to caller
/// </summary>
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
/// <summary>
/// Stop the port scan
/// </summary>
public Boolean StopPortScan;
/// <summary>
/// Number of ports
/// </summary>
public Int32 NumberOfPorts;
/// <summary>
/// Actual Port Counter
/// </summary>
public Int32 ActualPortCtr;
/// <summary>
/// Returns the port scan state
/// </summary>
/// <returns></returns>
/// <remarks date="2020-Oct-21" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public String GetPortScanState()
{
return $@"{Resources.StrScanPort} {ActualPortCtr}/{NumberOfPorts}";
}
/// <summary>
/// Ctor
/// </summary>
/// <returns>true if one port has been validated</returns>
/// <remarks date="2020-Dec-11" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <param name="portType">type of communication port to meter like IrDA</param>
public MeterPortScanner(String portType)
{
_portType = portType;
}
/// <inheritdoc />
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mai-11" author="Thomas Wiedebusch">
/// - Remove all meters removed as _meterBatch.Dispose will remove all meters.
/// </remarks>
public void Dispose()
{
// removal from meter batch includes a disposal of meter
_meterBatch?.Dispose();
}
/// <summary>
/// Use initially the port configuration to speed up search,
/// If configuration file contains wrong port setup than scan all serial ports listed in
/// the windows device manager,
/// Read available ports,
/// Try to open port and connect to Genesis meter.
/// </summary>
/// <param name="slot">slot to search for</param>
/// <param name="portConfigFilePathName">configuration of port to speed up search</param>
/// <returns>true if port found</returns>
/// <returns>true if one port has been validated</returns>
/// <remarks date="2020-Oct-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2020-Dec-11" author="Thomas Wiedebusch">
/// - Port type from Ctor,
/// - Event message changed:
/// - Overall message: ctr / counts - ongoing scan,
/// - Actual message: Information to log
/// </remarks>
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
/// - Changed exit of function,
/// - Added configuration file handling.
/// </remarks>
/// <remarks date="2021-Mai-11" author="Thomas Wiedebusch">
/// - Remove all meters removed as _meterBatch.Dispose will remove all meters.
/// </remarks>
public Boolean ScanAllSerialPorts(Int32 slot, String portConfigFilePathName)
{
var serialPort = new SerialPort();
var serialPorts = new List<String>();
_meterBatch = new MeterBatch();
serialPorts.AddRange(SerialPort.GetPortNames());
NumberOfPorts = serialPorts.Count;
ActualPortCtr = 0;
// if configuration file exists, read it and check for slot setup
var slotPortConfigs = new List<SlotConfig>();
if (portConfigFilePathName != null && File.Exists(portConfigFilePathName))
{
using (var tr = new StreamReader(portConfigFilePathName))
{
var fileStream = tr.ReadToEnd();
slotPortConfigs.AddRange(JsonConvert.DeserializeObject<SlotConfig[]>(fileStream));
}
foreach (var slotPortConfig in slotPortConfigs.Where(slotPortConfig =>
slotPortConfig.Slot == slot && !string.IsNullOrEmpty(slotPortConfig.Request.PortName)))
{
// add port on bottom of port list even if it is doubled that way
NumberOfPorts++;
serialPorts.Add(slotPortConfig.Request.PortName);
break;
}
}
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0,
$@"{NumberOfPorts} {Resources.StrPortListDeviceManger}"));
var serialPortDetected = false;
StopPortScan = false;
// start with last port,this might be added at current connection of interface
for (var ctr = serialPorts.Count - 1; ctr >= 0; ctr--)
{
if (StopPortScan) break;
ActualPortCtr++;
serialPort.PortName = serialPorts[ctr];
var overallProcessCtrPercent = 100.0 * ActualPortCtr / (NumberOfPorts > 0 ? NumberOfPorts : 1);
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Resources.StrPortAccessRequest}"));
// skip all ports which are already opened, this might be another customer
if (serialPort.IsOpen) continue;
PortConfig portConfig;
try
{
// removal from meter batch includes a disposal of meter
_meterBatch.RemoveAllMeters();
portConfig = new PortConfig
{
PortName = serialPort.PortName,
Type = _portType
};
_currentGenesis = new GenesisMeter(slot, portConfig, null);
if (_currentGenesis == null) return false;
// configuration has to be set BEFORE adding meter to batch to avoid e.g. auto update files
// from network and therefore have a long network request timeout before the task starts
_currentGenesis._configuration.UseRegisterWatchService = false;
_currentGenesis._configuration.UseMinMaxCheck = false;
_currentGenesis._configuration.AutoUpdateFiles = false;
_meterBatch.AddMeter(_currentGenesis);
}
catch (Exception)
{
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Resources.StrPortFailedToOpen}"));
continue;
}
// if port cannot be opened an access to the port is senseless, try the next port
if (_currentGenesis != null && !_currentGenesis.RequestPort.IsOpen()) continue;
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Resources.StrPortSuccessfullyAccessed}"));
// read PCB ID from meter, this is the indicator, that a Genesis is connected to this port
if (_currentGenesis == null || string.Empty == _currentGenesis.GetPcbId())
{
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Resources.StrRequestPortDetectionFailed}"));
continue;
}
AutoDetectedPortName = serialPort.PortName;
serialPortDetected = true;
// check if needed to store new configuration if slot and port name is not in file
if (!slotPortConfigs.Any(c => c.Slot == slot && c.Request.PortName == AutoDetectedPortName))
{
slotPortConfigs.Add(new SlotConfig(){ Slot = slot, Request = portConfig});
var text = JsonConvert.SerializeObject(slotPortConfigs);
if (portConfigFilePathName != null) File.WriteAllText(portConfigFilePathName, text);
}
// stop loop if one Genesis has been detected
break;
}
serialPort.Dispose();
// removal from meter batch includes a disposal of meter
_meterBatch?.Dispose();
return serialPortDetected;
}
}
}
@@ -0,0 +1,8 @@
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.Core")]
[assembly: AssemblyDescription("Xylem.Common.Hardware.WaterMeter.Genesis.Core")]
[assembly: ComVisible(true)]
[assembly: Guid("6B2FAA13-9ADB-430B-98F5-C6150BE8717F")]
@@ -0,0 +1,171 @@
//------------------------------------------------------------------------------
// <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.GenesisCore.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.GenesisCore.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 Compare register.
/// </summary>
internal static string StrCompareRegister {
get {
return ResourceManager.GetString("StrCompareRegister", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Access request..
/// </summary>
internal static string StrPortAccessRequest {
get {
return ResourceManager.GetString("StrPortAccessRequest", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to open..
/// </summary>
internal static string StrPortFailedToOpen {
get {
return ResourceManager.GetString("StrPortFailedToOpen", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Ports listed in the Windows device manager..
/// </summary>
internal static string StrPortListDeviceManger {
get {
return ResourceManager.GetString("StrPortListDeviceManger", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Automatic port scan started..
/// </summary>
internal static string StrPortScanStarted {
get {
return ResourceManager.GetString("StrPortScanStarted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Successfully accessed..
/// </summary>
internal static string StrPortSuccessfullyAccessed {
get {
return ResourceManager.GetString("StrPortSuccessfullyAccessed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Read register.
/// </summary>
internal static string StrReadRegister {
get {
return ResourceManager.GetString("StrReadRegister", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reading registers after update.
/// </summary>
internal static string StrReadRegistersAfterUpdate {
get {
return ResourceManager.GetString("StrReadRegistersAfterUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reading registers before update.
/// </summary>
internal static string StrReadRegistersBeforeUpdate {
get {
return ResourceManager.GetString("StrReadRegistersBeforeUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cordonel not detected!.
/// </summary>
internal static string StrRequestPortDetectionFailed {
get {
return ResourceManager.GetString("StrRequestPortDetectionFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Scan port.
/// </summary>
internal static string StrScanPort {
get {
return ResourceManager.GetString("StrScanPort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Write register.
/// </summary>
internal static string StrWriteRegister {
get {
return ResourceManager.GetString("StrWriteRegister", resourceCulture);
}
}
}
}
@@ -0,0 +1,156 @@
<?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="StrPortAccessRequest" xml:space="preserve">
<value>Teste Zugriff.</value>
</data>
<data name="StrPortFailedToOpen" xml:space="preserve">
<value>Zugriff verweigert.</value>
</data>
<data name="StrPortListDeviceManger" xml:space="preserve">
<value>Kommunikationsanschlüsse im Windows Gerätemanager.</value>
</data>
<data name="StrPortScanStarted" xml:space="preserve">
<value>Automatische Erkennung gestartet.</value>
</data>
<data name="StrPortSuccessfullyAccessed" xml:space="preserve">
<value>Zugriff erfolgreich.</value>
</data>
<data name="StrRequestPortDetectionFailed" xml:space="preserve">
<value>Keinen Cordonel erkannt!</value>
</data>
<data name="StrScanPort" xml:space="preserve">
<value>Suche Anschluß....</value>
</data>
<data name="StrCompareRegister" xml:space="preserve">
<value>Vergleiche Register</value>
</data>
<data name="StrReadRegister" xml:space="preserve">
<value>Lese Register</value>
</data>
<data name="StrWriteRegister" xml:space="preserve">
<value>Schreibe Register</value>
</data>
<data name="StrReadRegistersBeforeUpdate" xml:space="preserve">
<value>Lese Register vor dem Firmware Update</value>
</data>
<data name="StrReadRegistersAfterUpdate" xml:space="preserve">
<value>Lese Register nach dem Firmware Update</value>
</data>
</root>
@@ -0,0 +1,156 @@
<?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="StrPortAccessRequest" xml:space="preserve">
<value>Access request.</value>
</data>
<data name="StrPortFailedToOpen" xml:space="preserve">
<value>Failed to open.</value>
</data>
<data name="StrPortListDeviceManger" xml:space="preserve">
<value>Ports listed in the Windows device manager.</value>
</data>
<data name="StrPortScanStarted" xml:space="preserve">
<value>Automatic port scan started.</value>
</data>
<data name="StrPortSuccessfullyAccessed" xml:space="preserve">
<value>Successfully accessed.</value>
</data>
<data name="StrRequestPortDetectionFailed" xml:space="preserve">
<value>Cordonel not detected!</value>
</data>
<data name="StrScanPort" xml:space="preserve">
<value>Scan port</value>
</data>
<data name="StrReadRegistersAfterUpdate" xml:space="preserve">
<value>Reading registers after update</value>
</data>
<data name="StrReadRegistersBeforeUpdate" xml:space="preserve">
<value>Reading registers before update</value>
</data>
<data name="StrCompareRegister" xml:space="preserve">
<value>Compare register</value>
</data>
<data name="StrReadRegister" xml:space="preserve">
<value>Read register</value>
</data>
<data name="StrWriteRegister" xml:space="preserve">
<value>Write register</value>
</data>
</root>
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <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.GenesisCore.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}
@@ -0,0 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
</SettingsFile>
@@ -0,0 +1,379 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers.DataTypes;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
/// <summary>
/// Read, compare and restore if unequal al registers marked with
/// <see cref="StaticType"/> equals
/// <see cref="StaticType.RestoreRequired"/>".
/// The reader will use the byte values as they are unmodified and
/// will not be rounded and therefor possibly fail the comparison.
/// For logging the register value will be converted its unit.
/// </summary>
public class RegisterRestorer : IProcessState
{
/// <summary>
/// PCB ID for pre update for comparison
/// </summary>
private String _pcbIdPreUpdate = "X";
/// <summary>
/// PCB ID for post update for comparison
/// </summary>
private String _pcbIdPostUpdate = "?";
/// <summary>
/// Register content before update
/// </summary>
private readonly MeterRegisters _registersPreUpdate = new MeterRegisters();
//private readonly Dictionary<String, Byte[]> _registersDictionaryBeforeUpdate = new Dictionary<String, Byte[]>();
/// <summary>
/// Register content after update
/// </summary>
private readonly MeterRegisters _registersPostUpdate = new MeterRegisters();
//private Dictionary<String, Byte[]> _registersDictionaryAfterUpdate = new Dictionary<String, Byte[]>();
/// <summary>
/// Register access event for message dispatcher to caller
/// </summary>
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
/// <summary>
/// Actual Port Counter
/// </summary>
public Int32 ActualRegisterCtr;
/// <summary>
/// Stop register access
/// </summary>
public Boolean StopRegisterAccess;
/// <summary>
/// Genesis meter object
/// </summary>
private GenesisMeter _currentGenesis;
/// <summary>
/// Extraction of registers names needed to restore after FW-Update
/// </summary>
private readonly List<String> _restoreRegNames = new List<String>();
/// <summary>
/// Extraction of registers names needed to log
/// </summary>
private readonly List<String> _logRegNames = new List<String>();
/// <summary>
/// Ctor
/// </summary>
/// <param name="currentGenesis"></param>
public RegisterRestorer(GenesisMeter currentGenesis)
{
_currentGenesis = currentGenesis;
}
/// <summary>
/// Assign new genesis after reboot and keep the RegisterRestorer object.
/// </summary>
/// <param name="genesisMeter"></param>
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public void AssignGenesis(GenesisMeter genesisMeter)
{
_currentGenesis = genesisMeter;
}
/// <summary>
/// Avoid doubling of register restore on subsequent FW-Update trials.
/// </summary>
/// <returns>true if registers already backed up</returns>
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean RegisterRestoreExecuted()
{
if (_currentGenesis == null) return false;
if (_currentGenesis.PcbId != _pcbIdPostUpdate) return false;
return _registersPostUpdate.MeterRegisterDic.Count > 0;
}
/// <summary>
/// Avoid doubling of register backup on subsequent FW-Update trials.
/// </summary>
/// <returns>true if registers already backed up</returns>
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
/// - Extended checks for Genesis is assigned and PCB ID is identical.
/// </remarks>
public Boolean RegisterBackupExecuted()
{
if (_currentGenesis == null) return false;
if (_currentGenesis.PcbId != _pcbIdPreUpdate) return false;
return _registersPreUpdate.MeterRegisterDic.Count > 0;
}
/// <summary>
/// Restore all registers which can be restored and finalize with "safe configuration process".
/// </summary>
/// <returns>true if restoring succeeded</returns>
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean RestoreRegisters()
{
if (_currentGenesis == null && _pcbIdPostUpdate != _pcbIdPreUpdate) return false;
// write store configuration to all applications
_currentGenesis?.StoreAllApps();
return true;
}
/// <summary>
/// Compare all registers which can be restored.
/// </summary>
/// <returns>true if equal</returns>
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean CompareRegisters()
{
if (_currentGenesis == null && _pcbIdPostUpdate != _pcbIdPreUpdate) return false;
return true;
}
/// <summary>
/// Read the registers after the update for comparision. Build the post update registers here,
/// because an update may have changed the registers (new or removed registers).
/// </summary>
/// <returns>true if registers could be read</returns>
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2020-Dec-20" author="Thomas Wiedebusch">
/// - Build here the post update registers, they may have changed due to update!
/// </remarks>
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
/// - Clear pre update registers if post indicates a different PCB ID to avoid wrong overwriting of
/// registers to non matching new meter.
/// </remarks>
public Boolean PostUpdateReadRegisters()
{
if (_currentGenesis == null) return false;
// deny update of registers if already done
if (_pcbIdPostUpdate == _currentGenesis.PcbId) return false;
_pcbIdPostUpdate = _currentGenesis.PcbId;
// clear the contents of the restore registers if the water meter backed up was not identical
if (_pcbIdPreUpdate != _pcbIdPostUpdate)
{
_registersPreUpdate.MeterRegisterDic.Clear();
_restoreRegNames.Clear();
}
// get the pre-defined dictionary
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
// logging registers amy have changed during update!
_logRegNames.Clear();
// extract all registers which can be read and create two dictionary for before update to safe program
// internally the register content for comparison ability.
foreach (var reg in meterRegisters.MeterRegisterDic.Where(r => r.Key.RegisterDetail?.Version.First != null
&& r.Key.RegisterDetail.Version.Last != null && r.Key.RestoreCapability.StaticTypeValue != null
&& r.Key.DataType != typeof(Rpc) && r.Key.IsAvailable &&
(r.Key.RegisterDetail.Privilege.Lvl8 == Access.Rw || r.Key.RegisterDetail.Privilege.Lvl8 == Access.Ro)))
{
_registersPostUpdate.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
// only registers which are present before the update
_logRegNames.Add(reg.Key.GetIdent());
}
// sort by name to access a group of registers depending on application
_logRegNames.Sort();
// log to report file the after update register read string
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}", 0,
Resources.StrReadRegistersAfterUpdate));
return ReadRegistersSet(_registersPostUpdate, _logRegNames);
}
/// <summary>
/// Get DEFINED registers from GenesisMeter class and read all
/// registers which are flagged with read write (RW) or read only (RO).
/// Build a list of all registers being able to restore referenced by name,
/// flagged with <see cref="StaticType"/>
/// equals <see cref="StaticType.RestoreRequired"/>".
/// The defined registers are based on the "configuration.json" and
/// the installed application with a specific version.
/// </summary>
/// <returns>true if registers could be read</returns>
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
/// - Stored registers before and after update.
/// </remarks>
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
/// - Extended register check.
/// </remarks>
/// <remarks date="2020-Dec-20" author="Thomas Wiedebusch">
/// - Removed after update registers and moved them to PostUpdateReadRegisters. They may have changed
/// during the update.
/// </remarks>
/// <remarks date="2020-Dec-21" author="Thomas Wiedebusch">
/// - Deny register pre update if _pcbIdPreUpdate indicates, that this has been already processed.
/// </remarks>
public Boolean PreUpdateReadRegisters()
{
if (_currentGenesis == null) return false;
// deny update of registers if already done
if (_pcbIdPreUpdate == _currentGenesis.PcbId) return false;
// remind the PCB ID before update for comparison
_pcbIdPreUpdate = _currentGenesis.PcbId;
// get the pre-defined dictionary
var meterRegisters = _currentGenesis.GetConfigRegistersDefinitions();
_restoreRegNames.Clear();
_logRegNames.Clear();
// extract all registers which can be read and create two dictionary for before update to safe program
// internally the register content for comparison ability.
foreach (var reg in meterRegisters.MeterRegisterDic.Where(r => r.Key.RegisterDetail?.Version.First != null
&& r.Key.RegisterDetail.Version.Last != null && r.Key.RestoreCapability.StaticTypeValue != null
&& r.Key.DataType != typeof(Rpc) && r.Key.IsAvailable &&
(r.Key.RegisterDetail.Privilege.Lvl8 == Access.Rw || r.Key.RegisterDetail.Privilege.Lvl8 == Access.Ro)))
{
_registersPreUpdate.MeterRegisterDic.TryAdd(reg.Key, reg.Value);
// only registers which are present before the update
_logRegNames.Add(reg.Key.GetIdent());
}
// sort by name to access a group of registers depending on application
_logRegNames.Sort();
// take the pre-selected registers for update restore capability check
// extract all register names being able to restore. Even if the registers after the update may have
// changed, only registers which are present before the update can be restored!
foreach (var reg in _registersPreUpdate.MeterRegisterDic.Where(r =>
r.Key.RestoreCapability.CheckRestoreCapability() &&
r.Key.RegisterDetail.Privilege.Lvl8 == Access.Rw))
{
_restoreRegNames.Add(reg.Key.GetIdent());
}
// sort by name to access a group of registers depending on application
_restoreRegNames.Sort();
// log to report file the initial register read string
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}", 0,
Resources.StrReadRegistersBeforeUpdate));
return ReadRegistersSet(_registersPreUpdate, _logRegNames);
}
/// <summary>
/// Register read loop.
/// All registers needed to be read with <see cref="ReadRegistersSet"/>from the Genesis meter as they are only
/// prepared as template and NOT filled with the register data!
/// The <see cref="ProcessExecEventArgs"/> uses the overall process text and value for user information process
/// bar and text and the actual process text for logging to the report file!
/// </summary>
/// <param name="registers">registers to read</param>
/// <param name="registerNames">list of registers to read</param>
/// <returns>true if registers could be read</returns>
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2020-Dec-18" author="Thomas Wiedebusch">
/// - Set value to actual process message.
/// </remarks>
/// <remarks date="2020-Dec-20" author="Thomas Wiedebusch">
/// - Added PCB ID to logging.
/// </remarks>
/// <remarks date="2021-Mar-09" author="Thomas Wiedebusch">
/// - Removed PCB ID from logging.
/// </remarks>
private Boolean ReadRegistersSet(MeterRegisters registers, ICollection<String> registerNames)
{
if (_currentGenesis == null) return false;
StopRegisterAccess = false;
ActualRegisterCtr = 0;
// read values from Genesis
_currentGenesis.ReLogin();
if (!_currentGenesis.IsLoggedOn) return false;
foreach (var regName in registerNames)
{
ActualRegisterCtr++;
var regDef = registers.GetRegisterDefinitionByName(regName);
var overallProcessCtrPercent = 100.0 * ActualRegisterCtr /
(registerNames.Count > 0 ? registerNames.Count : 1);
try
{
Byte[] rawRegister;
if (regDef.DataType == typeof(String) || regDef.DataType == typeof(Int64) ||
regDef.DataType == typeof(UInt64))
{
rawRegister = _currentGenesis.ReadRegister(regName, 12);
}
else
{
rawRegister = _currentGenesis.ReadRegister(regName);
}
// safe value to program internal dictionary
registers.Set(regDef, rawRegister);
var tmpList = rawRegister.ToList();
tmpList.Reverse();
var strValueResult = "-----";
var strRawResult = BitConverter.ToString(tmpList.ToArray());
try
{
var strValue = RegisterConverter.ConvertToText(rawRegister, regDef.DataType);
// remove non printable char
strValueResult = Regex.Replace(strValue, @"\p{C}+", string.Empty);
}
catch (Exception)
{
// nothing to do
}
finally
{
var actualProcessMsg = $"{Resources.StrReadRegister}: {regName} - " +
$"{strRawResult} - {strValueResult}";
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs($@"{Resources.StrReadRegister}" +
$@" {ActualRegisterCtr} / {registerNames.Count}",
overallProcessCtrPercent, actualProcessMsg));
}
if (StopRegisterAccess) break;
}
catch (Exception)
{
// nothing to do
}
}
_currentGenesis.Logout();
return true;
}
}
}
@@ -0,0 +1,147 @@
using System;
using System.Linq;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
partial class GenesisMeter
{
/// <summary>
/// Calibration factors for all channels
/// </summary>
private readonly CalibFactor[] _calibFactor = new CalibFactor[MeterConfig.CalibChannels];
/// <summary>
/// Registers needed to store the calibration for the individual channel
/// </summary>
private readonly String[] _registersToStoreCalibFactor =
{
Register.Genesisflow.CalFactor1,
Register.Genesisflow.CalFactor2,
Register.Genesisflow.CalFactor3
};
/// <inheritdoc />
public void InitCalibration()
{
_logger.Info($"Slot:{Slot} - Init calibration and set calibration values of all channels to default");
for (var channel = 0; channel < MeterConfig.CalibChannels; channel++)
{
_calibFactor[channel] = new CalibFactor(channel, _registersToStoreCalibFactor[channel]);
WriteRegister(_registersToStoreCalibFactor[channel],
_calibFactor[channel].GetMeterRawCalibFactorDefault(), checkRegister: true);
}
PreMeasurement();
}
/// <inheritdoc />
public void StartCalibration()
{
StartMeasurement();
}
/// <inheritdoc />
/// <summary>
/// Just stop recording without calculation
/// </summary>
public void StopCalibration()
{
StopMeasurement();
}
/// <inheritdoc />
public MeasurementStates GetCalibrationState()
{
return GetMeasurementState();
}
/// <inheritdoc />
public void BuildAndCheckCalibFactorsAllChannels(Double refVolumeCm, Double? refTimeS, Double reqDeviationPercent, Double maxCalibFactorTolerancePercent = MeterConfig.DefaultMaxCalibFactorTolerancePercent)
{
for (var channel = 0; channel < MeterConfig.CalibChannels; channel++)
{
_calibFactor[channel] = new CalibFactor(channel, _registersToStoreCalibFactor[channel]);
}
foreach (var item in _listOfIMeasurement.Where(m => m.GetType() == typeof(CalibrationRecord)))
{
if (!(item is CalibrationMeasurement)) continue;
var calib = item as CalibrationMeasurement;
var channel = calib.GetChannel();
if( channel == 0 || channel > MeterConfig.CalibChannels)
{
var message = $"Slot:{Slot}, Channel:{channel} - Calibration channel out of range";
_logger.Warn(message);
throw new ApplicationException(message);
}
_logger.Info($"Slot:{Slot}, Channel:{channel} - Calculate Calibrationfactor Relative(RefVolumeCm:{refVolumeCm}, refTimeS:{refTimeS},reqDeviationPercent:{reqDeviationPercent})");
var calibFactorRel = calib.CalculateCalibFactorRel(refVolumeCm, refTimeS, reqDeviationPercent);
//if the null return indicates an invalid calibration factor the calibFactorX.IsCalculated remains false
if (null == calibFactorRel)
{
var message = $"Slot:{Slot}, Channel:{channel} - Calibration factor not calculated";
_logger.Warn(message);
throw new ApplicationException(message);
}
_calibFactor[channel-1].BuildMeterCalibFactor(calibFactorRel.Value);
_calibFactor[channel-1].IsCalculated = true;
_logger.Info($"Slot:{Slot}, Channel:{channel} - New calibration factor({_calibFactor[channel - 1].GetRelativeCalibFactor()}rel/{calibFactorRel})");
_logger.Info($"Slot:{Slot}, Channel:{channel} - Calc /{ _calibFactor[channel - 1].GetMeterRawCalibFactor()} * {calibFactorRel.Value}) -> UInt16");
//Setup the required tolerance value to check against
//plus/minus x% tolerance allowed
calib.CalibFactorTolerancePercent = maxCalibFactorTolerancePercent;
if (calib.IsCalibFactorInTolerance(calibFactorRel.Value)) continue;
var warnMessage = $"Slot:{Slot}, Channel:{channel} - " +
$"Calibration factor is out of range({calibFactorRel.Value}rel)";
_logger.Warn(warnMessage);
throw new ApplicationException(warnMessage);
}
}
/// <inheritdoc />
public void SetCalibFactorsAllChannels(Boolean justLog = false)
{
for (var channel = 0; channel < MeterConfig.CalibChannels; channel++)
{
if (_calibFactor[channel].IsCalculated == false) continue;
if (justLog)
{
_logger.Warn($"Slot:{Slot} - Calculated calibration factors but not stored to registers");
}
else
{
var registerRawCalibFactor = _calibFactor[channel].GetMeterRawCalibFactor();
WriteRegister(_calibFactor[channel].RegisterToStoreCalibFactor,
registerRawCalibFactor, checkRegister: true);
_logger.Info($"Slot:{Slot}, Channel:{channel} - Stored calibration factor({_registersToStoreCalibFactor})");
}
}
WriteRegister<Byte>(Register.Genesisflow.SampleRate, 10, checkRegister: true);
WriteRegister(Register.Genesisflow.ResetAccumulators, true);
//WriteRegister<UInt16>(Register.Genesisflow.ForwardArrow, 0, checkRegister: true);
WriteRegister<UInt16>(Register.Genesisflow.StoreCalibration, 1);
_logger.Info($"Slot:{Slot} - Set forward arrow of LCD, reset accumulators, set sample rate to 2Hz");
}
}
}
@@ -0,0 +1,313 @@
using ByteArrayStyle;
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore
{
public partial class GenesisMeter
{
/// <summary>
/// List of ongoing measurements, base- and calibration-measurements
/// </summary>
private List<IMeasurement> _listOfIMeasurement = new List<IMeasurement>();
/// <summary>
/// All channels required to process
/// </summary>
public Boolean AllChannelsRequired = true;
private const bool QualityWatchMode = false;
/// <inheritdoc />
/// <summary>
/// Perpetration of Measurement
/// SampleRate 10 and Led mode Test
/// </summary>
public void InitMeasurement()
{
_logger.Info($"Slot:{Slot} - Init measurement");
PreMeasurement();
}
private void PreMeasurement()
{
//if (!SkipPrepearationForTestBench)
//{
ResetAlarm(Alarm.EMPTY_PIPE | Alarm.REBOOT);
//TODO set display unit
//TODO calculated overflow volume for display
WriteRegister<Byte>(Register.Genesisflow.SampleRate, 16, checkRegister: true);
WriteRegister(Register.Genesisflow.LedMode, (Byte)LedMode.FlowTestAndCalibData, checkRegister: true);
WriteRegister("GENESISFLOW_SealDisplay", 0, true, true);
var displayState = ReadRegister(Register.Genesisflow.TriggerIdle);
if (displayState == null)
{
displayState = ReadRegister(Register.Genesisflow.TriggerIdle);
if (displayState == null)
{
_logger.Info($"Slot:{Slot} - Not able to read display state");
return;
}
}
var displayStateString = ByteStyler.ToString(displayState);
if (displayStateString.Substring(0, 2) != "FF" || displayStateString.Substring(0, 5) == "FF-55")
{
WriteRegister(Register.Genesisflow.TriggerIdle, 0, true, true);
displayState = ReadRegister(Register.Genesisflow.TriggerIdle);
displayStateString = ByteStyler.ToString(displayState);
if (displayStateString != "00-00-00-00")
{
WriteRegister(Register.Genesisflow.TriggerActive, 1, true);
}
}
else
{
WriteLog($"meter not active because display code has an error on code:{displayStateString}");
}
WriteRegister("GENESISFLOW_DisplayPow10", (new Byte[] { 0x00, 0x00, 0x00, 0xFA }).Reverse().ToArray(), true, false);
//}
//else
//{
// WriteLog($"wait for measurement is starting");
//}
ProcessStatus = ProcessState.WaitForMeasurement;
}
/// <inheritdoc />
public void StartMeasurement()
{
_listOfIMeasurement = new List<IMeasurement>
{
new BaseMeasurement(Slot, typeof(FlowTestRecord), IntermediateUpdateTimeS)
};
//add channels for calibration measurement
for (var i = 1; i <= MeterConfig.CalibChannels; i++)
{
_listOfIMeasurement.Add(new CalibrationMeasurement(Slot, typeof(CalibrationRecord), IntermediateUpdateTimeS, i));
}
foreach (var measurement in _listOfIMeasurement)
{
measurement.OnActionStateHasChanged += Measurement_ActionStateHasChanged;
}
_logger.Info($"Slot:{Slot} - Start measurement");
ProcessStatus = ProcessState.MeasurementIsActive;
_listOfIMeasurement.ForEach(m => m.MeasurementState = MeasurementStates.IsWaitingForStartData);
SyncStreamingBuffer(SyncMarkRecord.SyncStart);
}
/// <inheritdoc />
public void StopMeasurement()
{
_logger.Info($"Slot:{Slot} - Stop measurement");
ProcessStatus = ProcessState.MeasurementIsDone;
_listOfIMeasurement.ForEach(m => m.MeasurementState = MeasurementStates.IsWaitingForEndData);
SyncStreamingBuffer(SyncMarkRecord.SyncEnd);
}
/// <summary>
/// Testing the flow direction
/// </summary>
/// <returns></returns>
public Boolean CheckChannelFlowDirection()
{
Boolean? dirPositive = null;
foreach (var cali in _listOfIMeasurement.Where(m => m.GetType() == typeof(CalibrationRecord)))
{
var result = cali.GetIntermediateMeasurementResult();
var channelDirPositive = result.DutVolumeCm > 0.0;
if (!dirPositive.HasValue)
{
dirPositive = channelDirPositive;
}
if (dirPositive != channelDirPositive)
{
return false;
}
}
return true;
}
/// <inheritdoc />
/// <summary>
/// the first measurement is ALWAYS a FlowTestRecord due to the underlying<see cref="GetAllMeasurementResults"/> routine logic.
/// </summary>
public MeasurementResults GetMainMeasurementResult(Double? refVolume, Double? refTimeS, Boolean intermediateMeasurement = false)
{
if (!intermediateMeasurement && GetMeasurementState(0) != MeasurementStates.IsCompleted)
{
throw new ApplicationException("Measurement is not completed");
}
return GetAllMeasurementResults(refVolume, refTimeS, intermediateMeasurement, 0).First();
}
/// <summary>
/// The first measurement is ALWAYS a FlowTestRecord.
/// </summary>
public List<MeasurementResults> GetAllMeasurementResults(Double? refVolume, Double? refTimeS, Boolean intermediateMeasurement = false, Int32? channel = null)
{
if (!intermediateMeasurement && GetMeasurementState(channel) != MeasurementStates.IsCompleted)
{
throw new ApplicationException("Measurement is not completed");
}
var mainMeasurement = _listOfIMeasurement.FirstOrDefault(m => m.GetType() == typeof(FlowTestRecord));
if (mainMeasurement == null)
{
throw new ApplicationException("Can not find an ongoing measurement");
}
var results = new List<MeasurementResults>();
var mainResult = intermediateMeasurement
? mainMeasurement.GetIntermediateMeasurementResult(refVolume, refTimeS)
: mainMeasurement.GetFinalMeasurementResult(refVolume, refTimeS);
results.Add(mainResult);
_logger.Info($"Slot:{Slot}, Channel:{mainMeasurement.GetChannel()} - MeasuredTime({mainResult.DutTimeS}s), " +
$"MeasuredVolume({mainResult.DutVolumeCm}Qm)");
foreach (var otherMeasurement in _listOfIMeasurement.Where(m => m.GetType() != typeof(FlowTestRecord)))
{
try
{
var otherResults = intermediateMeasurement
? otherMeasurement.GetIntermediateMeasurementResult(refVolume, refTimeS)
: otherMeasurement.GetFinalMeasurementResult(refVolume, refTimeS);
_logger.Info($"Slot:{Slot}, Channel:{otherMeasurement.GetChannel()} - ChannelTime({otherResults.DutTimeS}s), " +
$"ChannelVolume({otherResults.DutVolumeCm}Qm)");
results.Add(otherResults);
}
catch (Exception ex)
{
_logger.Warn($"Slot:{Slot}, No Results for channel:{otherMeasurement.GetChannel()} Ex: {ex.Message}");
if (channel == null)
{
throw new ApplicationException($"Can not read result from channel:{otherMeasurement.GetChannel() } Ex: {ex.Message}");
}
}
}
return results;
}
private void AddData(IMeasurementRecord newRecord)
{
foreach (var measurement in _listOfIMeasurement)
{
if (measurement.GetType() != newRecord.GetType())
{
continue;
}
if (measurement.GetChannel() == newRecord.GetChannel())
{
measurement.AddData(newRecord);
}
}
// when every measurement has state IsRunning or every measurement has state IsCompleted, the streaming buffer set to skip decoding
if (_listOfIMeasurement.All(m => m.MeasurementState == MeasurementStates.IsRunning)
|| _listOfIMeasurement.All(m => m.MeasurementState == MeasurementStates.IsCompleted))
{
if (QualityWatchMode)
{
SyncStreamingBuffer(SyncMarkRecord.DecodeIntermediateQuality);
}
else
{
SyncStreamingBuffer(SyncMarkRecord.SkipDecoding);
}
}
}
private void Measurement_ActionStateHasChanged(Object sender, EventArgs e)
{
SyncStreamingBuffer(SyncMarkRecord.DecodeIntermediate);
}
/// <inheritdoc />
public MeasurementStates GetMeasurementState(Int32? onlyChannelNr = null)
{
_logger.Trace($"Slot:{Slot} - Get measurement state");
if (!_listOfIMeasurement.Any())
{
return MeasurementStates.NotStarted;
}
if (onlyChannelNr.HasValue)
{
var measurement = _listOfIMeasurement.First(f => f.GetChannel() == onlyChannelNr.Value);
if (measurement == null)
{
return MeasurementStates.NotStarted;
}
return measurement.GetMeasurementState();
}
return _listOfIMeasurement.Min(m => m.MeasurementState);
}
/// <inheritdoc />
public bool RequestIntermediateMeasurementState(Int32? onlyChannelNr = null)
{
_logger.Trace($"Slot:{Slot} - Get measurement state");
if (!_listOfIMeasurement.Any())
{
return false;
}
bool? ret = null;
foreach (var item in _listOfIMeasurement)
{
if (!onlyChannelNr.HasValue || item.GetChannel() == onlyChannelNr.Value)
{
var measurement = _listOfIMeasurement.First(f => f.GetChannel() == onlyChannelNr.Value);
if (measurement != null &&
(measurement.MeasurementState == MeasurementStates.IsRunning
|| measurement.MeasurementState == MeasurementStates.IsWaitingForIntermediateData))
{
measurement.RequestIntermediateRecord();
if (!ret.HasValue)
{
ret = true;
}
}
else
{
ret = false;
}
}
}
return ret.HasValue ? ret.Value : false;
}
}
}
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net40" />
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,65 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts
{
/// <summary>
/// Genesis meter firmware update states
/// </summary>
public enum FwUpdateState
{
/// <summary>
/// FW Update State Idle
/// </summary>
Idle = 0,
/// <summary>
/// FW update single step failed
/// </summary>
StepFailed = 1,
/// <summary>
/// FW update entire process failed
/// </summary>
UpdateFailed = 2,
/// <summary>
/// Start initial FW update
/// </summary>
StartInitial = 3,
/// <summary>
/// Repeat FW update
/// </summary>
RepeatFwUpdate = 4,
/// <summary>
/// Upload (partial-) update files
/// </summary>
UploadFileApps = 5,
/// <summary>
/// Download (partial-) update files
/// </summary>
DownloadFileApps = 6,
/// <summary>
/// Compare (partial-) update files
/// </summary>
CompareFileApps = 7,
/// <summary>
/// Download upgrade control file
/// </summary>
DownloadUpgradeControlFile = 8,
/// <summary>
/// Build upgrade control file
/// </summary>
BuildUpgradeControlFile = 9,
/// <summary>
/// Build Update Files
/// </summary>
BuildFileApps = 10,
/// <summary>
/// Trigger Upgrade
/// </summary>
TriggerUpgrade = 11,
/// <summary>
/// Erase update file
/// </summary>
EraseUpdateFile = 12,
/// <summary>
/// Delimiter for list
/// </summary>
FwUpdateStateListDelimiter = 13
}
}
@@ -0,0 +1,82 @@
using System;
using System.Linq;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Properties;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts
{
/// <summary>
/// Concatenation of state and string
/// </summary>
public static class FwUpdateStateInfo
{
/// <summary>
/// Build state information
/// </summary>
private static readonly FwUpdateStateText[] StateInfos;
/// <summary>
/// Ctor
/// </summary>
static FwUpdateStateInfo()
{
StateInfos = new FwUpdateStateText[(Int32)FwUpdateState.FwUpdateStateListDelimiter];
StateInfos[0].State = FwUpdateState.Idle;
StateInfos[0].Message = Resources.StrFwUpdateStateIdle;
StateInfos[1].State = FwUpdateState.StepFailed;
StateInfos[1].Message = Resources.StrFwUpdateStateStepFailed;
StateInfos[2].State = FwUpdateState.UpdateFailed;
StateInfos[2].Message = Resources.StrFwUpdateStateUpdateFailed;
StateInfos[3].State = FwUpdateState.StartInitial;
StateInfos[3].Message = Resources.StrFwUpdateStateStartInitial;
StateInfos[4].State = FwUpdateState.RepeatFwUpdate;
StateInfos[4].Message = Resources.StrFwUpdateStateRepeatFwUpdate;
StateInfos[5].State = FwUpdateState.UploadFileApps;
StateInfos[5].Message = Resources.StrFwUpdateStateUploadFileApps;
StateInfos[6].State = FwUpdateState.DownloadFileApps;
StateInfos[6].Message = Resources.StrFwUpdateStateDownloadFileApps;
StateInfos[7].State = FwUpdateState.CompareFileApps;
StateInfos[7].Message = Resources.StrFwUpdateStateCompareFileApps;
StateInfos[8].State = FwUpdateState.DownloadUpgradeControlFile;
StateInfos[8].Message = Resources.StrFwUpdateStateDownloadUpgradeControlFile;
StateInfos[9].State = FwUpdateState.BuildUpgradeControlFile;
StateInfos[9].Message = Resources.StrFwUpdateStateBuildUpgradeControlFile;
StateInfos[10].State = FwUpdateState.BuildFileApps;
StateInfos[10].Message = Resources.StrFwUpdateStateBuildFileApps;
StateInfos[11].State = FwUpdateState.TriggerUpgrade;
StateInfos[11].Message = Resources.StrFwUpdateStateTriggerUpgrade;
StateInfos[12].State = FwUpdateState.EraseUpdateFile;
StateInfos[12].Message = Resources.StrFwUpdateStateEraseUpdateFile;
}
/// <summary>
/// Get the current information
/// </summary>
/// <param name="state">state to search for information text</param>
public static String GetTextFromState(FwUpdateState state)
{
return (from stateInfo in StateInfos
where stateInfo.State == state select stateInfo.Message).FirstOrDefault();
}
/// <summary>
/// Get the state of the text
/// </summary>
/// <param name="text">information text to search if state is assigned</param>
public static FwUpdateState GetStateFromText(String text)
{
return (from stateInfo in StateInfos
where stateInfo.Message == text select stateInfo.State).FirstOrDefault();
}
}
}
@@ -0,0 +1,19 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile.Consts
{
/// <summary>
/// Concatenation between firmware update state and text
/// </summary>
public struct FwUpdateStateText
{
/// <summary>
/// The state of the firmware update
/// </summary>
public FwUpdateState State;
/// <summary>
/// The message text of the firmware update state
/// </summary>
public String Message;
}
}
@@ -0,0 +1,163 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2C6378C6-6BA1-4CEE-8992-8404D8369A90}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>false</Deterministic>
<TargetFrameworkProfile />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\ServiceFwUpdate\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Transactions" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Consts\FwUpdateState.cs" />
<Compile Include="Consts\FwUpdateStateInfo.cs" />
<Compile Include="Consts\FwUpdateStateText.cs" />
<Compile Include="MeterFileRestore.cs" />
<Compile Include="MeterFilesEraseRestore.cs" />
<Compile Include="MeterFwUpdate.cs" />
<Compile Include="MeterFwUpdateCapability.cs" />
<Compile Include="MeterFwUpdateRuler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MeterFile.cs" />
<Compile Include="MeterPwdFile.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>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\Utils\Logging\Logging.csproj">
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Utils\ProcessExec\ProcessExec.csproj">
<Project>{2FD60CF1-45CD-4060-B8E5-4F39FBA7F1CB}</Project>
<Name>ProcessExec</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\..\WaterMeterCore\WaterMeterCore.csproj">
<Project>{8203abb8-38e5-472e-a742-33f38b7a74a6}</Project>
<Name>WaterMeterCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\WaterMeterCore\WaterMeterRegisters\WaterMeterRegisters.csproj">
<Project>{26AFCF1E-1287-48B8-A506-32B1E8A0DD0E}</Project>
<Name>WaterMeterRegisters</Name>
</ProjectReference>
<ProjectReference Include="..\Applications\Applications.csproj">
<Project>{315E94B8-B82F-4B25-B5BD-297DF3BC1D54}</Project>
<Name>Applications</Name>
</ProjectReference>
<ProjectReference Include="..\GenesisConfig\GenesisConfig.csproj">
<Project>{f013a56b-7be6-4852-9cd8-b971cd1add0c}</Project>
<Name>GenesisConfig</Name>
</ProjectReference>
<ProjectReference Include="..\GenesisCore\GenesisCore.csproj">
<Project>{04201E95-90A0-4B73-A72F-379739B2A3AE}</Project>
<Name>GenesisCore</Name>
</ProjectReference>
<ProjectReference Include="..\GenesisPwd\GenesisPwd.csproj">
<Project>{AC434B18-4D7B-49BA-9AD5-0527C79E8297}</Project>
<Name>GenesisPwd</Name>
</ProjectReference>
<ProjectReference Include="..\Registers\Registers.csproj">
<Project>{F4AAF7E6-7333-4284-B735-E32DEB076C64}</Project>
<Name>Registers</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="MeterFilesEraseRestore.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.de.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.de.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Connected Services\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<ProjectExtensions>
<VisualStudio>
<UserProperties MeterFilesEraseRestore_1json__JSONSchema="https://json.schemastore.org/bower" />
</VisualStudio>
</ProjectExtensions>
</Project>
@@ -0,0 +1,804 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Utils.ProcessExec;
using Xylem.Common.Utils.ProcessExec.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
/// <summary>
/// Genesis meter file operations
/// </summary>
public class MeterFile : IProcessState
{
/// <summary>
/// Port scan result event for message dispatcher to caller
/// </summary>
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
//the raw data size is limited by the meter input buffer size, removed protocol length
private const Int32 MaxRawDataSize = 56;
private const Int32 ExtremeFileWriteTimeoutMs = 30000;
private const Int32 ExtendedFileIoTimeoutMs = 5000;
private const Int32 CommandWriteRetries = 2;
//timeout value loaded dynamically starting with extended timeout, on retry to extreme timeout
private Int32 _fileWriteTimeOutMs = ExtendedFileIoTimeoutMs;
private readonly GenesisMeter _currentGenesis;
private Int32 _filePointer;
private const String StrNewFileReadWriteMode = "wb+";
private const String StrOpenFileReadOnlyMode = "rb";
private const String StrOpenFileReadWriteMode = "rb+";
private String _fileName;
private Int32 _overallBytesCtr;
/// <summary>
/// Drive zero of meter.
/// </summary>
public const String StrMeterDrive0 = "0\\";
/// <summary>
/// Drive one of meter.
/// </summary>
public const String StrMeterDrive1 = "1\\";
/// <summary>
/// String delimiter of files from Genesis.
/// </summary>
public const Char StringDelimiter = '\0';
/// <summary>
/// Wildcard for search all.
/// </summary>
public const Char WildcardAll = '*';
// public const String StrWildcardAll = "*";
/// <summary>
/// Processed bytes counter
/// </summary>
public Int32 ProcessedBytesCtr;
private Boolean _stopProcess;
/// <summary>
/// Increase timeout to extreme value
/// </summary>
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
/// - Timeout decreased to speed up stop operation.
/// </remarks>
public void SetExtremeFileWriteTimeout()
{
_fileWriteTimeOutMs = ExtremeFileWriteTimeoutMs;
}
/// <summary>
/// Set timeout back to default timeout
/// </summary>
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
/// - Timeout decreased to speed up stop operation.
/// </remarks>
public void SetDefaultFileWriteTimeout()
{
_fileWriteTimeOutMs = ExtendedFileIoTimeoutMs;
}
/// <summary>
/// Stop process
/// </summary>
/// <remarks date="2020-Dec-17" author="Thomas Wiedebusch">
/// - Timeout decreased to speed up stop operation.
/// </remarks>
public Boolean StopProcess
{
get => _stopProcess;
set
{
_currentGenesis?.TransmitProtocol.SetDefaultResponseTimeout();
_stopProcess = value;
}
}
/// <summary>
/// Ctor for genesis meter file operations
/// </summary>
/// <param name="genesisMeter"></param>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
/// - Stop process added
/// </remarks>
public MeterFile(GenesisMeter genesisMeter)
{
_currentGenesis = genesisMeter;
_fileName = "";
_stopProcess = false;
}
/// <summary>
/// Process counter in percent
/// </summary>
/// <remarks date="2019-Jun-24" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Double ProcessCtrPercent => 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
/// <summary>
/// Writing a file to the meter
/// </summary>
/// <param name="fileName">file name as string</param>
/// <param name="dataBytes">data to store</param>
/// <param name="fileOffset">for partial file write procedures this is the offset to the file pointer</param>
/// <returns>true, if file can be find and stored and, if required, read back and verified</returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Jun-22" author="Thomas Wiedebusch">
/// - Early return on failed communication
/// </remarks>
/// <remarks date="2019-Jun-24" author="Thomas Wiedebusch">
/// - Added overall bytes counter for single file processing information
/// </remarks>
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
/// - Stop process added
/// </remarks>
/// <remarks date="2019-Jul-01" author="Thomas Wiedebusch">
/// - CHeck if file exists
/// </remarks>
/// <remarks date="2019-Nov-29" author="Thomas Wiedebusch">
/// - Extended response timeout
/// </remarks>
/// <remarks date="2019-Dec-02/03" author="Thomas Wiedebusch">
/// - Register read/write retry 0 implemented.
/// </remarks>
/// <remarks date="2019-Dec-12" author="Thomas Wiedebusch">
/// - Stop FWrite if FWrite failed,
/// - FClose if FWrite failed,
/// - avoid FTell if FWrite failed
/// </remarks>
/// <remarks date="2020-Jan-17" author="Thomas Wiedebusch">
/// - Retries changed for and back on different position,
/// - Timeout changed for and back on different position,
/// - Dummy read removed.
/// </remarks>
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
/// - Event on changed process counter.
/// </remarks>
/// <remarks date="2020-Dec-19" author="Thomas Wiedebusch">
/// - Dynamic timeout load from extended to extreme timeout on retries.
/// </remarks>
public Boolean WriteMeterFile(String fileName, Byte[] dataBytes, Int32 fileOffset = 0)
{
var returnValue = false;
//set processing counter
ProcessedBytesCtr = 0;
//inform the caller of changed statue
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
_overallBytesCtr = dataBytes.Length;
//check if file is unlocked for write
if (_fileName != fileName) return false;
if (fileOffset == 0 && OpenMeterFile(fileName, StrNewFileReadWriteMode)
|| fileOffset > 0 && OpenMeterFile(fileName, StrOpenFileReadWriteMode))
{
returnValue = true;
//adjust file pointer if offset is required
if (fileOffset > 0)
{
returnValue = SetFilePointerOffset(fileOffset);
//read file pointer offset
returnValue &= fileOffset == ReadFilePointerOffset();
}
if (returnValue && !_stopProcess)
{
//set extended timeout for file operations to extended timeout
_currentGenesis.TransmitProtocol.SetResponseTimeout(_fileWriteTimeOutMs);
//set register write retries to 0 to avoid doubling of records, if wakeup message from
//serial communication routine received, one retry will kicked off in the request protocol
CommunicationConfig.RequestRetries = 0;
//write size to 1
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 1, false);
//write counts to data size
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, dataBytes.Length, false);
//write file pointer
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, _filePointer);
//calculate records to send based on MaxRawDataSize
var chunkCounts = dataBytes.Length / MaxRawDataSize;
if (0 != dataBytes.Length % MaxRawDataSize)
{
chunkCounts += 1;
}
var dataByteList = new List<Byte>();
dataByteList.AddRange(dataBytes);
var dataChunk = new List<Byte>();
var ctr = 0;
//the preceding routine needs to wait for response, else this is always true!
while (returnValue && ctr < chunkCounts && !_stopProcess)
{
var index = ctr * MaxRawDataSize;
var size = index + MaxRawDataSize > dataByteList.Count ? dataByteList.Count - index : MaxRawDataSize;
//prepare data to write
dataChunk.Clear();
dataChunk.AddRange(dataByteList.GetRange(index, size));
//break on failed write of this part
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, dataChunk.ToArray());
//set processed bytes for write loop
ProcessedBytesCtr += size;
//inform the caller of changed statue
var processCtrPercent = 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", processCtrPercent));
ctr++;
}
//read back bytes written
var writtenBytes = 0;
var returnedFileOffset = 0;
//the preceding routine needs to wait for response, else this is always true!
if (returnValue)
{
writtenBytes = RegisterConverter.ConvertTo<Int32>(
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
returnedFileOffset = ReadFilePointerOffset();
}
//else
//{
// //read DUMMY to overcome FW malfunction
// _currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
//}
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
if (writtenBytes != ProcessedBytesCtr || returnedFileOffset != fileOffset + writtenBytes)
returnValue = false;
}
}//file open
returnValue &= CloseMeterFile();
//processed bytes not longer needed
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
ProcessedBytesCtr = 0;
_overallBytesCtr = 0;
return returnValue;
}
/// <summary>
/// Read file from the meter
/// </summary>
/// <param name="fileName">file name as string</param>
/// <param name="readBackData">empty list of data</param>
/// <param name="readBackCount">size to read</param>
/// <returns>file content as byte array</returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Jun-27" author="Thomas Wiedebusch">
/// - Stop process added
/// </remarks>
/// <remarks date="2019-Nov-30" author="Thomas Wiedebusch">
/// - Register read/write retry 0 implemented,
/// - Extended communication timeout
/// </remarks>
/// <remarks date="2019-Dec-02/03/05" author="Thomas Wiedebusch">
/// - Command write retry 0 implemented,
/// - Extended timeout.
/// </remarks>
/// <remarks date="2020-Jan-17" author="Thomas Wiedebusch">
/// - Retries changed for and back,
/// - Timeout changed for and back,
/// - Dummy read removed.
/// </remarks>
/// <remarks date="2020-Jan-22" author="Thomas Wiedebusch">
/// - Wait after writing for response before reading!
/// </remarks>
/// <remarks date="2020-Sep-21" author="Thomas Wiedebusch">
/// - Check remaining size to abort operation.
/// </remarks>
/// <remarks date="2020-Dec-10" author="Thomas Wiedebusch">
/// - Event on changed process counter.
/// </remarks>
public Boolean ReadMeterFile(String fileName, out List<Byte> readBackData, Int32 readBackCount)
{
//set processing counter
ProcessedBytesCtr = 0;
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
readBackData = new List<Byte>();
var returnValue = OpenMeterFile(fileName, StrOpenFileReadOnlyMode);
if (returnValue)
{
//set register write retries to 0 to avoid doubling of records
CommunicationConfig.RequestRetries = 0;
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
//write size to 1
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, 1, false);
//write counts to expected length
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, readBackCount, false);
//write file pointer
_currentGenesis.WriteRegister(Register.Configexchange.FileRead, _filePointer);
//the preceding routine needs to wait for response, else this is always true!
while (readBackCount > 0 && returnValue)
{
returnValue = _stopProcess;
var remainingData = RegisterConverter.ConvertTo<Int32>(
_currentGenesis.ReadRegister(Register.Configexchange.FileRead));
readBackCount = remainingData > 0 ? readBackCount - remainingData : 0;
while (remainingData > 0)
{
var size = remainingData > MaxRawDataSize ? MaxRawDataSize : remainingData;
readBackData.AddRange(_currentGenesis.ReadRegister(Register.Configexchange.FileRead, size));
remainingData -= size;
//set processed bytes for read loop
ProcessedBytesCtr += size;
//inform the caller of changed statue
var processCtrPercent = 100.0 * ProcessedBytesCtr / (_overallBytesCtr > 0 ? _overallBytesCtr : 1);
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", processCtrPercent));
}
}
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
}//file open succeeded
//dummy read to avoid 0x040B feedback from meter
//_currentGenesis.ReadRegister(Register.Configexchange.FileRead, 1);
returnValue &= CloseMeterFile();
//processed bytes not longer needed
ProcessedBytesCtr = 0;
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0));
//read raw data from meter
return returnValue;
}
/// <summary>
/// Read catalog of all meter files
/// </summary>
/// <param name="files">file names as string</param>
/// <param name="drive">e.g. "1\\" for drive 1</param>
/// <param name="wildcard">e.g. "upd*" all upgrade files on drive</param>
/// <returns></returns>
/// <remarks date="2021-Feb-16" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean ReadMeterFileCatalog(out List<String> files, String drive, String wildcard)
{
files = new List<String>();
//write the initial key
var key = 0;
_stopProcess = false;
// if both key are identical, the last file has been listed
while (!_stopProcess)
{
// remind entry key as this will be changed with the read back
var lastKey = key;
var searchPattern = drive + wildcard;
_currentGenesis.WriteRegister(Register.Configexchange.Catalogue, searchPattern);
_currentGenesis.WriteRegister(Register.Configexchange.Catalogue, key);
// initial read
var readData = "";
String backupData;
do
{
backupData = readData;
readData += RegisterConverter.ConvertTo<String>(
_currentGenesis.ReadRegister(Register.Configexchange.Catalogue));
} while (!readData.Contains(StringDelimiter) && backupData != readData);
if (!string.IsNullOrEmpty(readData))
{
//read new key
key = RegisterConverter.ConvertTo<Int32>(
_currentGenesis.ReadRegister(Register.Configexchange.Catalogue));
}
if (key == lastKey) break;
// remove the "\0"
var strMeterFile = readData.Split(StringDelimiter);
// avoid identical files repetition
var filePathName = drive + strMeterFile[0];
files.Add(filePathName);
}
return files.Count > 0;
}
/// <summary>
/// Create an empty meter file as placeholder for space in the file system.
/// </summary>
/// <param name="fileName">file name as string</param>
/// <param name="fileSize">the file size for the file pointer</param>
/// <returns>true, if file can be find and stored and, if required, read back and verified</returns>
/// <remarks date="2021-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Boolean CreateEmptyMeterFile(String fileName, Int32 fileSize = 0)
{
var returnValue = false;
//check if file is unlocked for write
if (_fileName != fileName) return false;
if (OpenMeterFile(fileName, StrNewFileReadWriteMode))
{
returnValue = true;
//adjust file pointer if offset is required
if (fileSize > 0)
{
// adjust file pointer to the end of file
returnValue = SetFilePointerOffset(fileSize - 1);
//read file pointer offset
returnValue &= (fileSize - 1) == ReadFilePointerOffset();
}
if (returnValue && !_stopProcess)
{
//set extended timeout for file operations to extended timeout
_currentGenesis.TransmitProtocol.SetResponseTimeout(_fileWriteTimeOutMs);
//set register write retries to 0 to avoid doubling of records, if wakeup message from
//serial communication routine received, one retry will kicked off in the request protocol
CommunicationConfig.RequestRetries = 0;
//write size to 1
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 1, false);
//write block of data to 1 as only one byte needed to be at the end of file
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 1, false);
//write file pointer
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, _filePointer);
//write one byte at the end of file
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.FileWrite, 0xFF);
//read back bytes written
var returnedFileOffset = 0;
//the preceding routine needs to wait for response, else this is always true!
if (returnValue)
{
RegisterConverter.ConvertTo<Int32>(
_currentGenesis.ReadRegister(Register.Configexchange.FileWrite));
returnedFileOffset = ReadFilePointerOffset();
}
//else
//{
// //read DUMMY to overcome FW malfunction
// _currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
//}
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
if (returnedFileOffset != fileSize) returnValue = false;
}
}//file open
returnValue &= CloseMeterFile();
return returnValue;
}
/// <summary>
/// Meter file verification against given data
/// </summary>
/// <param name="fileName">file name as string</param>
/// <param name="dataBytes">data to store</param>
/// <returns>true: file found and verified</returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Boolean VerifyMeterFile(String fileName, Byte[] dataBytes)
{
if (!OpenMeterFile(fileName, StrOpenFileReadOnlyMode)) return false;
//TODO THW implement
//read raw data from meter
return true;
}
/// <summary>
/// Erase file from the meter
/// </summary>
/// <param name="fileName">file name as string</param>
/// <returns></returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Dec-03" author="Thomas Wiedebusch">
/// - Extended timeout.
/// </remarks>
/// <remarks date="2021-May-18" author="Thomas Wiedebusch">
/// - Removed retries to speed up erasure as it returns error code 0x040B.
/// </remarks>
public Boolean EraseMeterFile(String fileName)
{
//check if file is unlocked for erase
if (_fileName != fileName) return false;
//check if logged on to meter and access level is as expected
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn) return false;
//set extended timeout for file operations
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
CommunicationConfig.RequestRetries = 0;
//write "remove-command" to meter, wait for result and read back (read back returns false because of unreadable content)
_currentGenesis.WriteRegister(Register.Configexchange.FileRemove, fileName, true, true);
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
return true;
}
/// <summary>
/// Two stage unlock sequence for meter file write or erase access
/// </summary>
/// <param name="fileName">file name as string</param>
/// <returns></returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Boolean UnlockEraseWriteMeterFile(String fileName)
{
//check if logged on to meter and access level is as expected
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn) return false;
//remind file name being unlocked
_fileName = fileName;
return true;
}
/// <summary>
/// FSeek implementation of genesis meter
/// </summary>
/// <param name="filePointerOffset"></param>
/// <returns></returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Nov-30" author="Thomas Wiedebusch">
/// - Command write retry implemented.
/// </remarks>
/// <remarks date="2019-Dec-03" author="Thomas Wiedebusch">
/// - Extended timeout.
/// </remarks>
/// <remarks date="2020-Jan-17/18" author="Thomas Wiedebusch">
/// - Retries changed for and back,
/// - Timeout changed for and back.
/// </remarks>
private Boolean SetFilePointerOffset(Int32 filePointerOffset)
{
Boolean returnValue;
var retryCtr = 0;
do
{
if (retryCtr > 0)
{
//read DUMMY to overcome FW malfunction
//_currentGenesis.ReadRegister(Register.Genesisflow.LedMode);
_currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
Thread.Sleep(1000);
}
//set extended timeout for file operations
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
CommunicationConfig.RequestRetries = 0;
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset,
_filePointer, false);
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset,
filePointerOffset, false);
returnValue &= _currentGenesis.WriteRegister(Register.Configexchange.SetFilePointerOffset,
0, false);
//function call returns 0 if FSeek successfully set
returnValue &= 0 == RegisterConverter.ConvertTo<UInt32>(_currentGenesis.ReadRegister(
Register.Configexchange.SetFilePointerOffset));
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
} while (!returnValue && retryCtr++ < CommandWriteRetries);
return returnValue;
}
/// <summary>
/// FTell implementation
/// </summary>
/// <returns>file pointer offset</returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Nov-30" author="Thomas Wiedebusch">
/// - Command write retry implemented.
/// </remarks>
/// <remarks date="2019-Dec-03" author="Thomas Wiedebusch">
/// - Extended timeout.
/// </remarks>
/// <remarks date="2020-Jan-17/18" author="Thomas Wiedebusch">
/// - Retries changed for and back,
/// - Timeout changed for and back.
/// </remarks>
private Int32 ReadFilePointerOffset()
{
Boolean returnValue;
var retryCtr = 0;
Int32 filePointerOffset;
do
{
if (retryCtr > 0)
{
//read DUMMY to overcome FW malfunction
//_currentGenesis.ReadRegister(Register.Genesisflow.LedMode);
_currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
Thread.Sleep(1000);
}
//set extended timeout for file operations
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
CommunicationConfig.RequestRetries = 0;
//read file pointer offset
returnValue = _currentGenesis.WriteRegister(Register.Configexchange.GetFilePointerOffset,
_filePointer, false);
filePointerOffset = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister(
Register.Configexchange.GetFilePointerOffset));
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
} while (!returnValue && retryCtr++ < CommandWriteRetries);
return filePointerOffset;
}
/// <summary>
/// FOpen implementation of genesis meter
/// Open a file of the meter and sets the file pointer
/// </summary>
/// <param name="fileName"></param>
/// <param name="accessMode"></param>
/// <returns>true if filePointer higher than zero</returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Dec-02/03/05" author="Thomas Wiedebusch">
/// - Extended timeouts,
/// - Read DUMMY to overcome FW malfunction.
/// </remarks>
/// <remarks date="2020-Jan-17/18" author="Thomas Wiedebusch">
/// - Retries changed for and back,
/// - Timeout changed for and back.
/// </remarks>
private Boolean OpenMeterFile(String fileName, String accessMode)
{
//check if logged on to meter and access level is as expected
if (_currentGenesis == null || !_currentGenesis.IsLoggedOn) return false;
Boolean returnValue;
var retryCtr = 0;
do
{
if (retryCtr > 0)
{
//read DUMMY to overcome FW malfunction
//_currentGenesis.ReadRegister(Register.Genesisflow.LedMode);
_currentGenesis.ReadRegister(Register.System.MonotonicSeconds);
Thread.Sleep(1000);
}
//set extended timeout for file operations
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
CommunicationConfig.RequestRetries = 0;
//write file name to meter with meter open command, don't wait for response
_currentGenesis.WriteRegister(Register.Configexchange.FileOpen, fileName, false);
//write access flag
_currentGenesis.WriteRegister(Register.Configexchange.FileOpen, accessMode, false);
//get the file pointer
_filePointer = RegisterConverter.ConvertTo<Int32>(_currentGenesis.ReadRegister(
Register.Configexchange.FileOpen));
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
returnValue = 0 < _filePointer;
} while (!returnValue && retryCtr++ < CommandWriteRetries);
return returnValue;
}
/// <summary>
/// FClose implementation of genesis meter
/// Close the _filePointer referenced file of the meter
/// </summary>
/// <returns></returns>
/// <remarks date="2019-Jun-20" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2019-Nov-30" author="Thomas Wiedebusch">
/// - Command write retry implemented.
/// </remarks>
/// <remarks date="2019-Dec-03" author="Thomas Wiedebusch">
/// - Extended timeout.
/// </remarks>
/// <remarks date="2020-Jan-17/18" author="Thomas Wiedebusch">
/// - Retries changed for and back,
/// - Timeout changed for and back,
/// - Command retry removed.
/// </remarks>
/// <remarks date="2020-Jan-22" author="Thomas Wiedebusch">
/// - Wait after writing for response before reading!
/// </remarks>
private Boolean CloseMeterFile()
{
//avoid file close if file pointer assignment failed
if (_filePointer <= 0) return false;
//set extended timeout for file operations
_currentGenesis.TransmitProtocol.SetResponseTimeout(ExtendedFileIoTimeoutMs);
CommunicationConfig.RequestRetries = 0;
var returnValue = _currentGenesis.WriteRegister(Register.Configexchange.FileClose,
_filePointer);
//the preceding routine needs to wait for response, else this is always true!
if (returnValue)
{
//function call returns 0 if FClose successfully executed
returnValue &= 0 == RegisterConverter.ConvertTo<Int32>(
_currentGenesis.ReadRegister(Register.Configexchange.FileClose));
}
//set timeout back to default value
_currentGenesis.TransmitProtocol.SetDefaultResponseTimeout();
CommunicationConfig.RequestRetries = CommunicationConfig.DefaultRequestRetries;
return returnValue;
}
}
}
@@ -0,0 +1,24 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
/// <summary>
/// Genesis meter files which have to be restored after the FW-Update.
/// </summary>
public class MeterFileRestore
{
/// <summary>
/// This defines an exactly name only e.g. "1\\tstfile"
/// </summary>
public String Name;
/// <summary>
/// Size of the file in bytes.
/// </summary>
public Int32 ByteSize;
/// <summary>
/// Pattern to write in file, if null the size has just to be generated with FSeek.
/// </summary>
public Char? Pattern;
}
}
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
/// <summary>
/// Genesis meter files which can be erased before FW-Update and have to be restored after FW-Update.
/// </summary>
public class MeterFilesEraseRestore
{
/// <summary>
/// This defines a name only or a mask with ending wildcard e.g. "1\\upg*"
/// </summary>
public List<String> Erase;
/// <summary>
/// This defines an exact name, the size and eventually a test pattern to write to the file,
/// if the test pattern is null, the FSeek can be used to write a single byte at the end.
/// </summary>
public List<MeterFileRestore> Restore;
}
}
@@ -0,0 +1,19 @@
{
"Erase": [
"1\\tstfile",
"1\\fdrdata",
"1\\logdata",
"1\\blklist",
"1\\mettable",
"1\\pulsedbg",
"1\\upg*",
"1\\img*"
],
"Restore": [
{
"Name": "1\\tstfile",
"ByteSize": 262144,
"Pattern": null
}
]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,71 @@
using System;
using System.Linq;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
/// <summary>
/// Update capability check for all necessary settings according to meter.
/// </summary>
public static class MeterFwUpdateCapability
{
/// <summary>
/// It is allowed to upgrade the metrology, causing an upgrade capability check to pass always.
/// </summary>
public const Byte MetrologyUpgradePermitted = 0xFF;
/// <summary>
/// The name of the metrology application.
/// </summary>
public const String MetrologyName = "GENESISFLOW";
/// <summary>
/// Checks if the metrology upgrade is allowed or the version is already installed.
/// The application description file "ADF" has to be read in advance as this contains the "GENSISFLOW" version.
/// </summary>
/// <param name="meterFwUpdate"></param>
/// <param name="currentGenesis">the current Genesis</param>
/// <returns>true if upgrade is allowed or the metrology version is up-to-date</returns>
/// <remarks date="2021-Jan-14" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Jan-27" author="Thomas Wiedebusch">
/// - Removed upgrade permission 0 check.
/// </remarks>
public static Boolean CheckMetrology(MeterFwUpdate meterFwUpdate, GenesisMeter currentGenesis)
{
if (meterFwUpdate == null || currentGenesis == null) return false;
// if the update is allowed, a version check is not needed
if (currentGenesis.MetrologyUpgradePermission == MetrologyUpgradePermitted) return true;
// extract the metrology version and CRC from the Genesis meter.
var installedMetrology =
currentGenesis.MeterAppListVersion.FirstOrDefault(im => im.AppName == MetrologyName);
// extract version and CRC from the ADF, ATTENTION the AppName is not det here, so check for AppId!
var requiredMetrology = meterFwUpdate.FileApps.FirstOrDefault(rm => installedMetrology != null &&
rm.AppId == installedMetrology.AppId);
// if the installed version equals the required version the update is allowed
return requiredMetrology != null && installedMetrology != null &&
installedMetrology.Version == requiredMetrology.Version &&
installedMetrology.Crc == requiredMetrology.Crc;
}
/// <summary>
/// Checks if the core version is in range of required minimum to maximum version.
/// The package file has to be read in advance as this contains the min/max.
/// </summary>
/// <param name="meterFwUpdate"></param>
/// <param name="coreVersionString">string containing the core version</param>
/// <returns>true if version is in range</returns>
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static Boolean CheckCoreRevision(MeterFwUpdate meterFwUpdate, String coreVersionString)
{
if (meterFwUpdate == null || coreVersionString == null) return false;
return meterFwUpdate.CheckCoreRevision(coreVersionString);
}
}
}
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
/// <summary>
/// Definition of releases and update dependencies.
/// </summary>
[Serializable]
public class MeterFwUpdateRuler
{
/// <summary>
/// Name of the release e.g. R1067. referenced as FLEXNETVERSION AppId 0x18
/// </summary>
public String Release;
/// <summary>
/// Region EMEA or NA
/// </summary>
public String Region;
/// <summary>
/// Core version minimum
/// </summary>
public String CoreVersionMin;
/// <summary>
/// Core version maximum
/// </summary>
public String CoreVersionMax;
/// <summary>
/// Version of metrology, as this may not permitted to update, referenced as GENESISFLOW AppId 0x0F,
/// the update capability of the metrology will be flagged by SYSTEM UpgradePermissions Id 0x0E:
/// 0xFF upgrade is permitted, all others the metrology updated is strictly forbidden!
/// </summary>
public String MetrologyVersion;
/// <summary>
/// Radio frequency in MHz referenced a SENSUSRADIO AppId 0x10, Register FrequencyIndicator Id 0x05
/// values: default 433, minimum 433, maximum 868
/// </summary>
public String RadioFrequencyMhz;
/// <summary>
/// Meter size e.g. DN40, DN50, or null if for all sizes
/// </summary>
public List<String> MeterSizes;
/// <summary>
/// Name of person which approves the release
/// </summary>
public String ApproverName;
/// <summary>
/// Date of approval
/// </summary>
public DateTime? ApprovalDate;
}
}
@@ -0,0 +1,241 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile
{
/// <summary>
/// Password file operations
/// </summary>
public class MeterPwdFile
{
public const String StrPasswordFileName = "0\\password";
private readonly GenesisMeter _genesisMeter;
private readonly MeterFile _meterFile;
private Byte[] _hashedPasswordFile;
//seven passwords are needed for Level 1, 2, 4, 5, 6, 7, Level 3 will be generated
//out of the ProcessorUID.
//private const Int32 PwdLevels = 7;
/// <summary>
/// Ctor
/// </summary>
/// <param name="genesisMeter"></param>
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public MeterPwdFile(GenesisMeter genesisMeter)
{
_genesisMeter = genesisMeter;
_meterFile = new MeterFile(_genesisMeter);
}
/// <summary>
/// Set the already hashed password file if this is already pre-generated.
/// This is the preparation to the <see cref="WriteMeterPwdFile"/>.
/// The password file has a unique length of <see cref="MeterPwdDb.PwdFileLength"/>.
/// </summary>
/// <param name="hashedPwdFile">hashed password file with constant length as byte array
/// </param>
/// <returns>true if length matches the expectations</returns>
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean SetHashedPwdFile(Byte[] hashedPwdFile)
{
if (MeterPwdDb.PwdFileLength != hashedPwdFile.Length) return false;
_hashedPasswordFile = new Byte[hashedPwdFile.Length];
_hashedPasswordFile = hashedPwdFile;
return true;
}
///// <summary>
///// Build the password file out of the clear text passwords and the processor UID,
///// password Level 3 has to be build out of the Processor UID.
///// </summary>
///// <param name="pwdLevels">7 sorted passwords levels: 1, 2, 4, 5, 6, 7, 8</param>
///// <param name="processorUid">unique ID of processor used for password level 3</param>
///// <param name="dbSkeletonKey">SkeletonKey from data base for comparison </param>
///// <returns></returns>
///// <remarks date="2019-Mai-15" author="Thomas Wiedebusch">
///// - Initial.
///// </remarks>
///// <remarks date="2020-Mai-15" author="Roland Drahbesh">
///// - Insert of Level 3 password with skeletonKey,
///// - SHA 1 of passwords.
///// </remarks>
///// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
///// - .
///// </remarks>
///// <remarks date="2021-Jan-29" author="Thomas Wiedebusch">
///// - Set Level 3 password to modified <see cref="BuildLevel3Pwd"/> from processor UID,
///// - Removed skeletonKey as this cannot be used here!
///// </remarks>
///// <remarks date="2021-Feb-01" author="Thomas Wiedebusch">
///// - Set Level 3 password to dbSkeletonKey if processorUid is unknown (processorUid == 1)!
///// </remarks>
//public Boolean BuildPwdFile(List<Byte[]> pwdLevels, UInt64 processorUid, String dbSkeletonKey)
//{
// if (pwdLevels.Count != PwdLevels || processorUid == 0 ||
// string.IsNullOrEmpty(dbSkeletonKey) && processorUid == 1)
// {
// return false;
// }
// // selective Level 3 password
// if (processorUid == 1 && !string.IsNullOrEmpty(dbSkeletonKey))
// {
// pwdLevels.Insert(2, Encoding.UTF8.GetBytes(dbSkeletonKey));
// }
// else
// {
// pwdLevels.Insert(2,Encoding.UTF8.GetBytes(BuildLevel3Pwd(processorUid)));
// }
// var ret = new List<Byte>();
// foreach (var keyLvl in pwdLevels)
// {
// using (var sha1 = new System.Security.Cryptography.SHA1Managed())
// {
// var hash = sha1.ComputeHash(keyLvl);
// ret.AddRange(hash.ToList());
// }
// }
// _hashedPasswordFile = ret.ToArray();
// return true;
//}
/// <summary>
/// Build the password file out of the clear text passwords.
/// The Level 3 password is already preset.
/// </summary>
/// <param name="pwdLevels">8 sorted passwords levels: 1, 2, 3, 4, 5, 6, 7, 8</param>
/// <param name="processorUid">unique ID of processor used for password level 3</param>
/// <param name="dbSkeletonKey">SkeletonKey for comparison </param>
/// <returns></returns>
/// <remarks date="2019-Mai-15" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2020-Mai-15" author="Roland Drahbesh">
/// - All 8 password levels will be passed,
/// - Password Level 3 will be compared with data base skeletonKey,
/// - SHA 1 of passwords.
/// </remarks>
public Boolean BuildPwdFile(List<Byte[]> pwdLevels, UInt64 processorUid, String dbSkeletonKey)
{
if (!Encoding.UTF8.GetBytes(dbSkeletonKey).SequenceEqual(pwdLevels[2]))
{
throw new ApplicationException("Level 3 Password is unequal to the old Data Base Content!");
}
var ret = new List<Byte>();
foreach (var keyLvl in pwdLevels)
{
using (var sha1 = new System.Security.Cryptography.SHA1Managed())
{
var hash = sha1.ComputeHash(keyLvl);
ret.AddRange(hash.ToList());
}
}
_hashedPasswordFile = ret.ToArray();
return true;
}
/// <summary>
/// Build skeleton key out of unique processor Id
/// </summary>
/// <param name="processorUid"></param>
/// <returns></returns>
/// <remarks date="2019-Mai-03" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String BuildSkeletonKey(UInt64 processorUid)
{
return $"{(UInt16)((processorUid >> 48) ^ (processorUid >> 32)):X4}" +
$"{(UInt32)((processorUid >> 16) ^ processorUid):X8}";
}
/// <summary>
/// Build level 3 password out of unique processor Id
/// </summary>
/// <param name="processorUid"></param>
/// <returns></returns>
/// <remarks date="2019-Mai-03" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public static String BuildLevel3Pwd(UInt64 processorUid)
{
//password level 3 algorithm
return $"{(UInt16)((processorUid >> 48) ^ processorUid):X4}" +
$"{(UInt32)((processorUid >> 32) ^ processorUid):X8}";
}
/// <summary>
/// Write the meter password file, which was generated at <see cref="BuildPwdFile"/> or
/// use the already hashed password file if this is already pre-generated.
/// The password file has a unique length of <see cref="MeterPwdDb.PwdFileLength"/>.
/// </summary>
/// <param name="hashedPwdFile">hashed password file with constant length as byte array
/// </param>
/// <returns>true if length matches the expectations and password file could be written
/// </returns>
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2020-Dec-08" author="Thomas Wiedebusch">
/// - Modified to take optional hashed password file as input.
/// </remarks>
public Boolean WriteMeterPwdFile(Byte[] hashedPwdFile = null)
{
if (hashedPwdFile != null)
{
if (!SetHashedPwdFile(hashedPwdFile)) return false;
}
if (_hashedPasswordFile != null && _meterFile != null && _genesisMeter != null
&& _genesisMeter.IsLoggedOn && _hashedPasswordFile.Length == MeterPwdDb.PwdFileLength)
{
return _meterFile.WriteMeterFile(StrPasswordFileName, _hashedPasswordFile);
}
return false;
}
/// <summary>
/// Erase the meter password file
/// </summary>
/// <returns></returns>
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean EraseMeterPwdFile()
{
if (_meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn)
{
return _meterFile.EraseMeterFile(StrPasswordFileName);
}
return false;
}
/// <summary>
/// Unlock access to password file for write and erase
/// </summary>
/// <returns></returns>
/// <remarks date="2019-Mai-02" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
public Boolean UnlockEraseWriteMeterPwdFile()
{
if (_meterFile != null && _genesisMeter != null && _genesisMeter.IsLoggedOn)
{
return _meterFile.UnlockEraseWriteMeterFile(StrPasswordFileName);
}
return false;
}
}
}
@@ -0,0 +1,35 @@
using System.Reflection;
using System.Runtime.InteropServices;
//// General Information about an assembly is controlled through the following
//// set of attributes. Change these attribute values to modify the information
//// associated with an assembly.
//[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.GenesisFile")]
//[assembly: AssemblyDescription("")]
//[assembly: AssemblyConfiguration("")]
//[assembly: AssemblyCompany("Xylem")]
//[assembly: AssemblyProduct("GenesisFile")]
//[assembly: AssemblyCopyright("Copyright © Xylem 2019")]
//[assembly: AssemblyTrademark("")]
//[assembly: AssemblyCulture("")]
//// Setting ComVisible to false makes the types in this assembly not visible
//// to COM components. If you need to access a type in this assembly from
//// COM, set the ComVisible attribute to true on that type.
//[assembly: ComVisible(true)]
//// The following GUID is for the ID of the typelib if this project is exposed to COM
//[assembly: Guid("2c6378c6-6ba1-4cee-8992-8404d8369a90")]
//// Version information for an assembly consists of the following four values:
////
//// Major Version
//// Minor Version
//// Build Number
//// Revision
////
//// You can specify all the values or you can default the Build and Revision Numbers
//// by using the '*' as shown below:
//// [assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyVersion("1.0.0.0")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,234 @@
//------------------------------------------------------------------------------
// <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.GenesisFile.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()]
public 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)]
public 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.GenesisFile.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)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Build Update Files.
/// </summary>
public static string StrFwUpdateStateBuildFileApps {
get {
return ResourceManager.GetString("StrFwUpdateStateBuildFileApps", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Build Upgrade Control File.
/// </summary>
public static string StrFwUpdateStateBuildUpgradeControlFile {
get {
return ResourceManager.GetString("StrFwUpdateStateBuildUpgradeControlFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Compare Update Files.
/// </summary>
public static string StrFwUpdateStateCompareFileApps {
get {
return ResourceManager.GetString("StrFwUpdateStateCompareFileApps", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Download Update Files.
/// </summary>
public static string StrFwUpdateStateDownloadFileApps {
get {
return ResourceManager.GetString("StrFwUpdateStateDownloadFileApps", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Download Upgrade Control File.
/// </summary>
public static string StrFwUpdateStateDownloadUpgradeControlFile {
get {
return ResourceManager.GetString("StrFwUpdateStateDownloadUpgradeControlFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Erase Update File.
/// </summary>
public static string StrFwUpdateStateEraseUpdateFile {
get {
return ResourceManager.GetString("StrFwUpdateStateEraseUpdateFile", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to FW Update State Idle.
/// </summary>
public static string StrFwUpdateStateIdle {
get {
return ResourceManager.GetString("StrFwUpdateStateIdle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Repeat FW Update.
/// </summary>
public static string StrFwUpdateStateRepeatFwUpdate {
get {
return ResourceManager.GetString("StrFwUpdateStateRepeatFwUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Start Initial FW Update.
/// </summary>
public static string StrFwUpdateStateStartInitial {
get {
return ResourceManager.GetString("StrFwUpdateStateStartInitial", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to FW Update Process Failed.
/// </summary>
public static string StrFwUpdateStateStepFailed {
get {
return ResourceManager.GetString("StrFwUpdateStateStepFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Trigger Upgrade.
/// </summary>
public static string StrFwUpdateStateTriggerUpgrade {
get {
return ResourceManager.GetString("StrFwUpdateStateTriggerUpgrade", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Start Initial FW Update.
/// </summary>
public static string StrFwUpdateStateUpdateFailed {
get {
return ResourceManager.GetString("StrFwUpdateStateUpdateFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Upload Update Files.
/// </summary>
public static string StrFwUpdateStateUploadFileApps {
get {
return ResourceManager.GetString("StrFwUpdateStateUploadFileApps", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Trigger upgrade succeeded.
/// </summary>
public static string StrMeterAppTriggerAccepted {
get {
return ResourceManager.GetString("StrMeterAppTriggerAccepted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Trigger upgrade failed.
/// </summary>
public static string StrMeterAppTriggerFailed {
get {
return ResourceManager.GetString("StrMeterAppTriggerFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to - Part .
/// </summary>
public static string StrPartOfParts {
get {
return ResourceManager.GetString("StrPartOfParts", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Download.
/// </summary>
public static string StrProcessDownload {
get {
return ResourceManager.GetString("StrProcessDownload", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to - Retry .
/// </summary>
public static string StrProcessRetry {
get {
return ResourceManager.GetString("StrProcessRetry", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Waiting.....
/// </summary>
public static string StrWaiting {
get {
return ResourceManager.GetString("StrWaiting", resourceCulture);
}
}
}
}
@@ -0,0 +1,177 @@
<?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="StrFwUpdateStateBuildFileApps" xml:space="preserve">
<value>Erzeuge Aktualisierungsdateien</value>
</data>
<data name="StrFwUpdateStateBuildUpgradeControlFile" xml:space="preserve">
<value>Erzeuge Initialisierungsdatei für Aktualisierung</value>
</data>
<data name="StrFwUpdateStateCompareFileApps" xml:space="preserve">
<value>Vergleiche Aktualisierungsdateien</value>
</data>
<data name="StrFwUpdateStateDownloadFileApps" xml:space="preserve">
<value>Sende Aktualisierungsdateien</value>
</data>
<data name="StrFwUpdateStateDownloadUpgradeControlFile" xml:space="preserve">
<value>Sende Initialisierungsdatei für Aktualisierung</value>
</data>
<data name="StrFwUpdateStateEraseUpdateFile" xml:space="preserve">
<value>Lösche Aktualisierungsdatei</value>
</data>
<data name="StrFwUpdateStateIdle" xml:space="preserve">
<value>Leerlauf</value>
</data>
<data name="StrFwUpdateStateRepeatFwUpdate" xml:space="preserve">
<value>Wiedehole Aktualisierung</value>
</data>
<data name="StrFwUpdateStateStartInitial" xml:space="preserve">
<value>Starte Aktualisierung</value>
</data>
<data name="StrFwUpdateStateStepFailed" xml:space="preserve">
<value>Aktualisierungsfehler einer Datei</value>
</data>
<data name="StrFwUpdateStateTriggerUpgrade" xml:space="preserve">
<value>Abschluß der Aktualisierung</value>
</data>
<data name="StrFwUpdateStateUpdateFailed" xml:space="preserve">
<value>Aktualisierungsfehler</value>
</data>
<data name="StrFwUpdateStateUploadFileApps" xml:space="preserve">
<value>Lese Dateien aus</value>
</data>
<data name="StrWaiting" xml:space="preserve">
<value>Warte....</value>
</data>
<data name="StrMeterAppTriggerFailed" xml:space="preserve">
<value>Abschlußvorgang mißlungen</value>
</data>
<data name="StrMeterAppTriggerAccepted" xml:space="preserve">
<value>Abschlußvorgang erfolgreich durchgeführt</value>
</data>
<data name="StrProcessRetry" xml:space="preserve">
<value> - Wiederholung </value>
</data>
<data name="StrPartOfParts" xml:space="preserve">
<value> - Teil </value>
</data>
<data name="StrProcessDownload" xml:space="preserve">
<value>Einspielen </value>
</data>
</root>
@@ -0,0 +1,177 @@
<?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="StrFwUpdateStateBuildFileApps" xml:space="preserve">
<value>Build Update Files</value>
</data>
<data name="StrFwUpdateStateBuildUpgradeControlFile" xml:space="preserve">
<value>Build Upgrade Control File</value>
</data>
<data name="StrFwUpdateStateCompareFileApps" xml:space="preserve">
<value>Compare Update Files</value>
</data>
<data name="StrFwUpdateStateDownloadFileApps" xml:space="preserve">
<value>Download Update Files</value>
</data>
<data name="StrFwUpdateStateDownloadUpgradeControlFile" xml:space="preserve">
<value>Download Upgrade Control File</value>
</data>
<data name="StrFwUpdateStateEraseUpdateFile" xml:space="preserve">
<value>Erase Update File</value>
</data>
<data name="StrFwUpdateStateIdle" xml:space="preserve">
<value>FW Update State Idle</value>
</data>
<data name="StrFwUpdateStateRepeatFwUpdate" xml:space="preserve">
<value>Repeat FW Update</value>
</data>
<data name="StrFwUpdateStateStartInitial" xml:space="preserve">
<value>Start Initial FW Update</value>
</data>
<data name="StrFwUpdateStateStepFailed" xml:space="preserve">
<value>FW Update Process Failed</value>
</data>
<data name="StrFwUpdateStateTriggerUpgrade" xml:space="preserve">
<value>Trigger Upgrade</value>
</data>
<data name="StrFwUpdateStateUpdateFailed" xml:space="preserve">
<value>Start Initial FW Update</value>
</data>
<data name="StrFwUpdateStateUploadFileApps" xml:space="preserve">
<value>Upload Update Files</value>
</data>
<data name="StrWaiting" xml:space="preserve">
<value>Waiting....</value>
</data>
<data name="StrMeterAppTriggerFailed" xml:space="preserve">
<value>Trigger upgrade failed</value>
</data>
<data name="StrMeterAppTriggerAccepted" xml:space="preserve">
<value>Trigger upgrade succeeded</value>
</data>
<data name="StrProcessRetry" xml:space="preserve">
<value> - Retry </value>
</data>
<data name="StrPartOfParts" xml:space="preserve">
<value> - Part </value>
</data>
<data name="StrProcessDownload" xml:space="preserve">
<value>Download</value>
</data>
</root>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AC434B18-4D7B-49BA-9AD5-0527C79E8297}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MeterPwdDb.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
@@ -0,0 +1,10 @@
""
{
"FILE_VERSION" = "9237"
"ENLISTMENT_CHOICE" = "NEVER"
"PROJECT_FILE_RELATIVE_PATH" = ""
"NUMBER_OF_EXCLUDED_FILES" = "0"
"ORIGINAL_PROJECT_FILE_PATH" = ""
"NUMBER_OF_NESTED_PROJECTS" = "0"
"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER"
}
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd
{
/// <summary>
/// DB contents of:
/// - passwords,
/// - password-hashes (12 char from password - SHA1),
/// - Order number,
/// - Radio address and
/// - Skeleton key.
/// </summary>
public class MeterPwdDb
{
//password file length is 160 bytes (8 passwords a 20 bytes SHA1)
public const Int32 PwdFileLength = 160;
/// <summary>
/// Skeleton key index in password list.
/// </summary>
public const Int32 SkeletonKeyIdx = 2;
/// <summary>
/// Password Level 8 index in 8 elements password list.
/// </summary>
public const Int32 PwdLevel8Idx = 7;
/// <summary>
/// Order number for this Genesis.
/// </summary>
public Int64? OrderNumber = null;
/// <summary>
/// Optional radio address.
/// </summary>
public Int64? RadioAddress = null;
/// <summary>
/// Skeleton key for login if password file is not installed.
/// </summary>
public String Skeleton = "";
/// <summary>
/// List of hashes.
/// </summary>
public List<Byte[]> ListOfHashes = new List<Byte[]>();
/// <summary>
/// List of passwords.
/// </summary>
public List<Byte[]> ListOfPasswords = new List<Byte[]>();
}
}
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.GenesisPwd")]
[assembly: AssemblyDescription("Genesis Password Handling")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Sensus")]
[assembly: AssemblyProduct("GenesisPwd")]
[assembly: AssemblyCopyright("Copyright © Sensus 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(true)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ac434b18-4d7b-49ba-9ad5-0527c79e8297")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,146 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using Xylem.Common.CommonCore.ThreadWatcher;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore
{
/// <inheritdoc />
/// <summary>
/// Base for all protocol types
/// </summary>
public abstract class BaseProtocol : IProtocol
{
private readonly CancellationTokenSource _decodingToken = new CancellationTokenSource();
/// <inheritdoc />
public abstract event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <inheritdoc />
public virtual event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
private readonly ConcurrentQueue<IPortDataEventArgs> _decodingFifo = new ConcurrentQueue<IPortDataEventArgs>();
//initially do not signal event
private readonly AutoResetEvent _onSyncDecodingThread = new AutoResetEvent(false);
private readonly Thread _decodingThread;
/// <summary>
/// Ident is a combined string of Slot, Port, Protocol and Type
/// </summary>
public readonly String Ident;
private ITransmitProtocol _transmitProtocol;
/// <summary>
/// Starting decoding thread
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
protected BaseProtocol(String ident)
{
Ident = ident;
//assign thread to loop
_decodingThread = new Thread(DecodingThreadLoop) { Name = $"{Ident} Decoding thread" };
//start DecodingThread
ThreadWatcher.Instance.Start(_decodingThread);
}
/// <inheritdoc />
/// <summary>
/// Kill the decoding thread
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public void Dispose()
{
//Cancel receive tokens
_decodingToken.Cancel();
//Run thread again to notice CancellationToken has changed
_onSyncDecodingThread.Set();
//this timeout counter is being used for dispose only
var timeoutCounter = 100;
while (_decodingThread.ThreadState != ThreadState.Stopped && timeoutCounter > 0)
{
Thread.Sleep(1);
timeoutCounter = timeoutCounter - 1;
}
if (_decodingThread.ThreadState != ThreadState.Stopped)
{
//if thread still running, he stuck so try to abort
// try to avoid abort (takes age to run and is not safe)
_decodingThread.Abort();
}
}
/// <summary>
/// Fill FIFO with received data
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public void FillDecodingBuffer(IPortDataEventArgs data)
{
if (_decodingToken.IsCancellationRequested) return;
//put data to FIFO
_decodingFifo.Enqueue(data);
//put DecodingThread state from WaitSleepJoin to Running
_onSyncDecodingThread.Set();
}
/// <summary>
/// Decoding thread loop calling the individual decoding
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
private void DecodingThreadLoop()
{
try
{
while (!_decodingToken.IsCancellationRequested)
{
IPortDataEventArgs receivedRecord;
while (_decodingFifo.TryDequeue(out receivedRecord))
{
DecodeRecord(receivedRecord);
}
//put DecodingThread to WaitSleepJoin until next data arrived
_onSyncDecodingThread.WaitOne();
}
}
catch (ThreadAbortException)
{ }
}
/// <summary>
/// The decoding routine
/// </summary>
/// <param name="data"></param>
public abstract void DecodeRecord(IPortDataEventArgs data);
/// <inheritdoc />
public ITransmitProtocol GetTransmitProtocol()
{
return _transmitProtocol;
}
/// <inheritdoc />
public void SetTransmitProtocol(ITransmitProtocol transmitProtocol)
{
_transmitProtocol = transmitProtocol;
}
}
}
@@ -0,0 +1,42 @@
using System;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore
{
/// <inheritdoc />
/// <summary>
/// Protocol interface
/// </summary>
public interface IProtocol : IDisposable
{
/// <summary>
/// Fill the buffer with data ready for decoding
/// </summary>
/// <param name="rawData"></param>
void FillDecodingBuffer(IPortDataEventArgs rawData );
/// <summary>
/// Record is ready to send including all protocols
/// </summary>
event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
/// <summary>
/// Record is successfully decoded and ready for further processing
/// </summary>
event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <summary>
/// Return of transmit protocol settings
/// </summary>
/// <returns></returns>
ITransmitProtocol GetTransmitProtocol();
/// <summary>
/// Assignment of transmit protocol
/// </summary>
/// <param name="transmitProtocol"></param>
void SetTransmitProtocol(ITransmitProtocol transmitProtocol);
}
}
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Protocols.ProtocolCore")]
[assembly: AssemblyDescription("")]
[assembly: Guid("1A402949-095C-4039-8AE3-6594A7FB2242")]
@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8BDD2789-96D4-43EA-AAD7-0F49D0347998}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>6</LangVersion>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BaseProtocol.cs" />
<Compile Include="IProtocol.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\CommonCore\ThreadWatcher\ThreadWatcher.csproj">
<Project>{94ca71f3-c5d7-4ec6-ba47-fa8eea3c9406}</Project>
<Name>ThreadWatcher</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2e139f63-b6fe-4ea9-a098-85364fe9b26c}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\..\DataPackages\DataPackages.csproj">
<Project>{0e524d43-7b66-45cd-aa74-41d51afc3d99}</Project>
<Name>DataPackages</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,125 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Commands that the genesis meter support over the request protocol.
/// Functional Specification Breeze Core and Applications Revision:3.03(12748DOC11 - functional spec ICOE472.pdf)
/// </summary>
public static class Commands
{
/// <summary>
/// 9.2.5
///Command 0x00 (NOP)
///This command will do nothing, and will have no response.
/// </summary>
public const Byte Nop = 0x00;
/// <summary>
/// 9.2.6
/// Command 0x01 (Query capabilities)
/// This command will be used by the external computer to discover the protocol parameters that may be varied.
/// These can then be compared with the external computer’s capabilities and the best match selected.
/// </summary>
public const Byte QueryCaps = 0x01;
/// <summary>
/// response on <see cref="QueryCaps"/>
/// </summary>
public const Byte QueryCapsReply = 0x02;
/// <summary>
/// 9.2.7
/// Command 0x03 (Set capabilities)
/// Used to finalize the baud rate and packet settings after negotiation.
/// The reply will be sent at the currently selected baud rate and packet length,
/// after which the settings will take effect
/// </summary>
public const Byte SetCaps = 0x03;
/// <summary>
/// response on <see cref="SetCaps"/>
/// </summary>
public const Byte SetCapsReply = 0x04;
/// <summary>
/// 9.2.8
/// Command 0x05 (Train)
///This command will perform target driven data training, that is, where the target is in control of the data flow.
///See also command 0x11.
/// </summary>
public const Byte Train = 0x05;
/// <summary>
/// response on <see cref="Train"/>
/// </summary>
public const Byte TrainReply = 0x06;
/// <summary>
/// 9.2.9
/// Command 0x07 (Repeat last)
/// Used by the external computer to request the last response to be resent, for example if it was found to be corrupted.
/// Note that the response 0x08 will never be sent, the reply to command 0x07 will be a verbatim resend of the last response.
/// </summary>
public const Byte RepeatLast = 0x07;
/// <summary>
/// response on <see cref="RepeatLast"/>
/// </summary>
public const Byte RepeatLastReply = 0x08;
/// <summary>
/// 9.2.10
///Command 0x09 (Read data)
///This command makes reads of any random selection of configuration registers, up to the maximum packet size negotiated.
/// </summary>
public const Byte ReadData = 0x09;
/// <summary>
/// response on <see cref="ReadData"/>
/// </summary>
public const Byte ReadDataReply = 0x0A;
/// <summary>
/// 9.2.11
/// Command 0x0B (Write data)
/// This command makes writes to any random selection of configuration registers, up to the maximum packet size negotiated.
/// </summary>
public const Byte WriteData = 0x0B;
/// <summary>
/// response on <see cref="WriteData"/>
/// </summary>
public const Byte WriteDataReply = 0x0C;
/// <summary>
/// 9.2.12
///Command 0x0D (Multiple read data)
///This command makes reads of one register multiple times, which will be more efficient than performing successive reads using command 0x09.
///This command will be available from protocol version 0.40, for earlier protocol versions command 0x09 should be used.
/// </summary>
public const Byte MultipleReadData = 0x0D;
/// <summary>
/// response on <see cref="MultipleReadData"/>
/// </summary>
public const Byte MultipleReadDataReply = 0x0E;
/// <summary>
/// 9.2.13
/// Command 0x0F (Multiple write data)
/// This command makes writes to one register multiple times, which will be more efficient than performing successive reads using command 0x0B.
/// This command will be available from protocol version 0.40, for earlier protocol versions command 0x0B should be used.
/// </summary>
public const Byte MultipleWriteData = 0x0F;
/// <summary>
/// response on <see cref="MultipleWriteData"/>
/// </summary>
public const Byte MultipleWriteDataReply = 0x10;
/// <summary>
/// 9.2.14
/// Command 0x11 (Set level)
/// This command will perform external driven data training, that is, where the external computer is in control of the data flow.
/// This command will be available from protocol version 0.42, for earlier protocol versions command 0x05 should be used.
/// </summary>
public const Byte SetLevel = 0x11;
/// <summary>
/// response on <see cref="SetLevel"/>
/// </summary>
public const Byte SetLevelReply = 0x12;
//todo remove?
//public const byte SetBitrateLsb = 0x03;
//public const byte SetBitrateMsb = 0x05;
//public const byte SetPacketSizesLsb = 0x04;
//public const byte SetPacketSizesMsb = 0x05;
//public const byte SetProtocolLsb = 0x02;
//public const byte SetProtocolMsb = 0x05;
}
}
@@ -0,0 +1,31 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Config Exchange error codes.
/// </summary>
public enum ConfigExErrors
{
#pragma warning disable CS1591 //as a matter of course
Base = 0x04,
LockedOut = 0x00,
AuthenticationFail = 0x01,
AccessDenied = 0x02,
UnknownParameter = 0x03,
InUse = 0x04,
SizesDoesNotMatch = 0x05,
CantReadCfgFile = 0x06,
UserNotKnown = 0x07,
CantCreateCfgFile = 0x08,
StoringFailed = 0x09,
StoreCorrupt = 0x0A,
ExpectedWrite = 0x0B,
ExpectedRead = 0x0C,
StopCycling = 0x0D,
TooManyOpen = 0x0E,
NeverOpened = 0x0F,
FileProtected = 0x1F,
PartialRecall = 0x2F,
DefaultPasswordUsed = 0x3F,
#pragma warning restore
}
}
@@ -0,0 +1,25 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Transport errors for request protocol
/// </summary>
public enum HighLevelErrors
{
#pragma warning disable CS1591 //as a matter of course
Base = 0x05,
PayloadCount = 0x00,
InvalidSubReason = 0x01,
InvalidBaudrate = 0x02,
InvalidBufferSize = 0x03,
CrcFailure = 0x04,
UnrecognizedCmd = 0x05,
Framing = 0x06,
Overflow = 0x07,
PacketTimeout = 0x08,
InvalidEscape = 0x09,
UnknownParameter = 0x0A,
TrainingFailed = 0x0B,
NoBreak = 0x0C,
#pragma warning restore
}
}
@@ -0,0 +1,47 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// List of constance for error codes from genesis meter
/// </summary>
public static class LowLevelErrors
{
/// <summary>
/// everything is fine
/// </summary>
public const Int16 NoError = 0;
/// <summary>
/// Port is not open
/// </summary>
public const Int16 SerialPortClosed = -2;
/// <summary>
/// fails to write to serial port
/// </summary>
public const Int16 SerialPortWriteFailure = -3;
/// <summary>
/// fails to read from serial port
/// </summary>
public const Int16 SerialPortReadFailure = -4;
/// <summary>
/// Command is to long
/// </summary>
public const Int16 MaxDataLength = -5;
/// <summary>
/// deeper exception, check out log if this happen
/// </summary>
public const Int16 Exception = -6;
/// <summary>
/// wrong CRC
/// </summary>
public const Int16 CrcCalcError = -7;
/// <summary>
/// something strange
/// </summary>
public const Int16 Unknown = -8;
/// <summary>
/// Timeout occur
/// </summary>
public const Int16 Timeout = -9;
}
}
@@ -0,0 +1,36 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Acknowledge feedback from meter after communication
/// </summary>
public enum RequestAcknowledgeState
{
/// <summary>
/// Response missing
/// </summary>
NoResponse,
/// <summary>
/// Response command not match the required command
/// </summary>
CommandError,
/// <summary>
/// Lost connection (logged out from meter), a re-authorization is required
/// to access this command and/or register
/// </summary>
AuthorizationRequired,
/// <summary>
/// Meter error received, a retry may be useful
/// </summary>
MeterError,
/// <summary>
/// The response record couldn't be decoded
/// </summary>
DecodingError,
/// <summary>
/// Valid meter response record
/// </summary>
Ok
}
}
@@ -0,0 +1,25 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.EventArguments
{
/// <inheritdoc />
/// <summary>
/// if session is gone and a re authorization is necessary
/// </summary>
public class AuthorizationRequiredEventArgs : EventArgs
{
/// <inheritdoc />
/// <summary>
/// Ctor
/// </summary>
/// <param name="command">this command will be resend after authorization</param>
public AuthorizationRequiredEventArgs(RequestRecord command = null)
{
Command = command;
}
/// <summary>
/// Command to resend
/// </summary>
public RequestRecord Command;
}
}
@@ -0,0 +1,59 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Exceptions
{
/// <summary>
/// Holds all errors can occur on the request protocol
/// </summary>
public class RequestProtocolException : Exception
{
/// <summary>
/// Exception occurred on this command
/// </summary>
public Object Command;
/// <summary>
/// Exception occurred while receiving this data
/// </summary>
public Object ReceivedData;
/// <summary>
/// Error is interpreted and has a define error code
/// if its not null is Genesis.Protocols.Request.Const.ConfigExErrors
/// </summary>
public Int32? ConfigExErrorCode;
/// <summary>
/// Error is interpreted and has a define error code
/// if its not null is Genesis.Protocols.Request.Const.HighLevelError
/// </summary>
public Int32? HighLevelErrorCode;
/// <summary>
/// Ctor with only message
/// </summary>
/// <param name="message">error message</param>
public RequestProtocolException(String message) : base(message)
{
}
/// <summary>
/// Ctor with message and <see cref="Command"/>
/// </summary>
/// <param name="message">error message</param>
/// <param name="command"><see cref="Command"/></param>
public RequestProtocolException(String message, Object command) : base(message)
{
Command = command;
ReceivedData = null;
}
/// <summary>
/// Ctor with message, <see cref="Command"/> and <see cref="ReceivedData"/>
/// </summary>
/// <param name="message">error message</param>
/// <param name="command"><see cref="Command"/></param>
/// <param name="receivedData"><see cref="ReceivedData"/> </param>
public RequestProtocolException(String message, Object command, Object receivedData) : base(message)
{
Command = command;
ReceivedData = receivedData;
}
}
}
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request")]
[assembly: AssemblyDescription("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request")]
[assembly: Guid("91026D9C-212F-4B59-9237-7E34424A4DBD")]
@@ -0,0 +1,648 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Xylem.Common.Utils.Logging;
using NLog;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
{
/// <summary>
/// a bidirectional protocol support read and write genesis meter registers
/// needed for
/// </summary>
public class RequestProtocol : BaseProtocol
{
//All read and write data sets (payload size) are organized in 4 byte chunks
//Format of RequestProtocol read data
//Request CMD | Register LSB | Register MSB | 0x00 | 0x00 | 0x00 | 0x00
//Response CMD | ErrCode Reason | ErrCode Base | DATA0 | DATA1 | DATA2 | DATA3
//Format of RequestProtocol write data
//Request CMD | Register LSB | Register MSB | DATA0 | DATA1 | DATA2 | DATA3
//Response CMD | ErrCode Reason | ErrCode Base | UNDEF0 | UNDEF1 | UNDEF2 | UNDEF3
//Format of RequestProtocol multiple read data
//Request CMD | Register LSB | Register MSB | RequestChunks LSB | RequestChunks MSB
//Response CMD | ErrCode Reason | ErrCode Base | ErrPosition LSB | ErrPosition MSB |
//DATA0 | DATA1 | DATA2 | DATA3.... (repeat for number of chunks)
//Format of RequestProtocol multiple read data
//Request CMD | Register LSB | Register MSB | WriteChunks LSB | WriteChunks MSB |
//DATA0 | DATA1 | DATA2 | DATA3.... (repeat for number of chunks)
//Response CMD | ErrCode Reason | ErrCode Base | ErrPosition LSB | ErrPosition MSB
//Indexes of the request protocol read/write data
//private const Int32 RequestCommandIndex = 0;
//private const Int32 RequestRegisterIndex = 1;
//private const Int32 RequestDataIndex = 3;
//Indexes for response protocol read/write data
private const Int32 ResponseCommandIndex = 0;
private const Int32 ResponseErrorCodeIndex = 1;
private const Int32 ResponseDataIndex = 3;
//Indexes of the request protocol multiple read/write data
//private const Int32 RequestChunksIndex = 3;
//private const Int32 RequestMultiDataIndex = 5;
//Indexes of the response protocol multiple read/write data
private const Int32 ResponseMultiErrorPosition = 3;
private const Int32 ResponseMultiDataIndex = 5;
//Indexes of error code
private const Int32 ErrorCodeReasonIndex = 0;
private const Int32 ErrorCodeBaseIndex = 1;
//Multiple response header size is 1 byte command 2 bytes error code and 2 bytes error position
private const Int32 ResponseMultiHeaderSize = 5;
//fill byte
private const Byte FillByte = 0x00;
private readonly ILogger _logger;
/// <summary>
/// FIFO of records to be send next
/// </summary>
private readonly ConcurrentQueue<RequestRecord> _recordsSendFifo = new ConcurrentQueue<RequestRecord>();
/// <summary>
/// This is the actual record in the send loop
/// </summary>
private RequestRecord _recordInProcess;
/// <summary>
/// Last communication time for session refresh
/// </summary>
public DateTimeOffset? LastCommTime;
/// <inheritdoc />
public override event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
/// <inheritdoc />
public override event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <summary>
/// Occurs when a meter Response for write password is good
/// </summary>
public event EventHandler OnAuthorizationGrant;
/// <summary>
/// Event after a register entries changed
/// </summary>
public event EventHandler<RegisterUpdatedEventArgs> OnMeterRegisterUpdated;
/// <summary>
/// User adjustable additional retry timeout. This is 0 ms for standard operation.
/// </summary>
public Int32 AdditionalRetryTimeoutMs;
private Int32 _timeOutMs;
private readonly String _ident;
/// <inheritdoc />
public RequestProtocol(String ident) : base(ident)
{
_ident = ident;
_logger = NLogHelper.CreateOrGetMultiLogger(ident, "", "RequestProtocol", "MeterBase", "MeterBase");
}
/// <summary>
/// Process all records needed to be sent, this routine has to be called
/// to kick-off the communication of all RequestRecords saved to the send
/// FIFO <see cref="AddRecordToSendFifo"/>
/// </summary>
/// <remarks date="2018-Dec-07" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
/// - Logging of raw RequestProtocol at time of sending,
/// - Logging of retries.
/// </remarks>
/// <remarks date="2018-Dec-12" author="T.Wiedebusch">
/// - Added timeout from transmit protocol.
/// </remarks>
/// <remarks date="2019-Jan-02" author="Drabesch">
/// - Added reorder FIFO to bring login at first position
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-06" author="T.Wiedebusch">
/// - Retry counter reset if authorization required and this record will be put back into FIFO,
/// - Retry delay corrected for all errors.
/// </remarks>
/// <remarks date="2019-Jun-12" author="T.Wiedebusch">
/// - Dynamic retry delay: ResponseTimeoutMs * Retry counter.
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Skip retries on specific error mask to speed up communication on functional errors
/// or informational feed backs (e.g FW not installed 0x0004)
/// <see cref="RequestRecord.ResponseErrorCode"/>
/// </remarks>
/// <remarks date="2019-Aug-29" author="T.Wiedebusch">
/// - User adjustable additional retry timeout.
/// </remarks>
/// <remarks date="2020-Jan-17/18" author="T.Wiedebusch">
/// - Response timeout message output,
/// - Initialize acknowledge code before communication to NoResponse.
/// </remarks>
/// <remarks date="2020-Jan-22" author="T.Wiedebusch">
/// - Response timeout deviated from system time.
/// </remarks>
/// <remarks date="2021-Feb-02" author="T.Wiedebusch">
/// - Avoid enqueue of _recordInProcess if retry counter is 0.
/// </remarks>
public RequestAcknowledgeState ProcessRecordList()
{
_logger.Trace(" 1 ProcessRecordList");
//The inter record send delay has to be hold before trying to communicate again
const Int32 interRecordSendDelayMs = CommunicationConfig.InterRecordSendDelayMs;
//check if FIFO is empty and get the actual record to process out of it
while (_recordsSendFifo.TryDequeue(out var internalRecord))
{
_logger.Trace(" 1 TryDequeue ProcessRecordList");
_recordInProcess = internalRecord;
do
{
_logger.Trace(" 1 TryDequeue DO ProcessRecordList");
//log retries
if (_recordInProcess.RetryCtr > 0)
{
_logger.Debug($"{_ident} Retry({_recordInProcess.RetryCtr})");
}
//log request protocol content
_logger.Debug(_recordInProcess.HideDataInLog
? $"{_ident} SentData(*********************************)"
: $"{_ident} SentData({BitConverter.ToString(_recordInProcess.RequestProtocolData.ToArray())})");
//remind time for keep-session-active test to deny automatic logout of meter
LastCommTime = DateTimeOffset.UtcNow;
//set timeout for one communication trial adding the transmit protocol specific timeout and
//increase the timeout with each retry
_timeOutMs = _recordInProcess.ResponseTimeoutMs + AdditionalRetryTimeoutMs +
CommunicationConfig.ResponseTimeoutMs * (_recordInProcess.RetryCtr + 1);
//start initial communication or retry
OnRecordReadyToSend?.Invoke(this, new ListBytePortDataEventArgs(_recordInProcess.EncodedRequestData));
var requestTimeUtc = DateTimeOffset.UtcNow;
Int32 responseTimeMs;
//wait for communication acknowledge or until timeout, this also handles the inter record send delay
do
{
//minimum delay is the inter record send delay
Thread.Sleep(interRecordSendDelayMs);
var timeoutTimeUtc = DateTimeOffset.UtcNow;
var timeSpan = timeoutTimeUtc - requestTimeUtc;
//avoid total milliseconds below zero at time overflow
if (timeSpan.TotalMilliseconds < 0)
{
requestTimeUtc = DateTimeOffset.UtcNow;
}
responseTimeMs = (Int32)timeSpan.TotalMilliseconds;
} while (_recordInProcess.Acknowledge != RequestAcknowledgeState.Ok &&
_recordInProcess.SkipRetryErrorCode != _recordInProcess.ResponseErrorCode &&
_timeOutMs > responseTimeMs);
//if timeout value reaches zero, the response hasn't been received or the delay until
//next communication needed to be hold
if (_timeOutMs <= responseTimeMs)
{
_logger.Error(_recordInProcess.Acknowledge == RequestAcknowledgeState.NoResponse
? $"{_ident} Response timeout({responseTimeMs}ms)"
: $"{_ident} Communication delay({responseTimeMs}ms)");
}
//skip loop to handle re-authorization
if (_recordInProcess.Acknowledge != RequestAcknowledgeState.AuthorizationRequired) continue;
//reset retry counter for this record, it will be dispatched after re-authorization
_recordInProcess.RetryCtr = 0;
if (CommunicationConfig.RequestRetries > 0)
{
//add the recordInProcess to the top of the FIFO
_recordsSendFifo.Enqueue(_recordInProcess);
}
//call login
return _recordInProcess.Acknowledge;
} while (_recordInProcess.Acknowledge != RequestAcknowledgeState.Ok &&
_recordInProcess.RetryCtr++ < CommunicationConfig.RequestRetries &&
_recordInProcess.SkipRetryErrorCode != _recordInProcess.ResponseErrorCode);
}
return _recordInProcess.Acknowledge;
}
/// <summary>
/// Assemble record with transmit protocol and put it to record-send-FIFO,
/// backup the ready-to-send, which is the dataEncodedWithTransmitProtocol,
/// to the RequestRecord object for sending including the retry capability.
/// </summary>
/// <param name="payload">Data package with all details like CRC, etc</param>
/// <param name="cmd">Command identifier <see cref="Commands" /></param>
/// <param name="register"><see cref="RegisterDefinition" /> which is intend</param>
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
/// <param name="skipRetryErrorCode">error mask to skip retries</param>
/// <returns>the assembled record for the send FIFO</returns>
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
/// - Logging of raw data (RequestProtocol) moved to ProcessRecordList.
/// </remarks>
/// <remarks date="2018-Dec-12" author="T.Wiedebusch">
/// - Added timeout from transmit protocol.
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Skip retries on specific error mask to speed up communication on functional errors
/// or informational feed backs (e.g FW not installed 0x0004)
/// <see cref="RequestRecord.ResponseErrorCode"/>
/// </remarks>
/// <remarks date="2020-Jan-18" author="T.Wiedebusch">
/// - Initial skip retry error code set to 0x0004 (e.g FW not installed 0x0004).
/// </remarks>
private RequestRecord AddRecordToSendFifo(Byte cmd, Byte[] payload, RegisterDefinition register = null,
Boolean hideDataInLog = false, UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
{
var requestProtocolData = new List<Byte> { cmd };
requestProtocolData.AddRange(payload);
//encode request protocol data with transmit protocol
var transmit = GetTransmitProtocol();
var dataEncodedWithTransmitProtocol = transmit.DecodeDataForPhysicalLayer(cmd, payload);
//remind port specific response timeout
var transmitPortSettings = transmit.GetTransmitPortSettings();
var recordForSendFifo = new RequestRecord(cmd, requestProtocolData, dataEncodedWithTransmitProtocol,
transmitPortSettings.ResponseTimeoutMs, register, hideDataInLog, skipRetryErrorCode);
_recordsSendFifo.Enqueue(recordForSendFifo);
return recordForSendFifo;
}
/// <summary>
/// Called after successful response.
/// Decode and check record, handle Errors and dispatch result.
/// Invokes <see cref="OnRecordIsDecoded" />if somebody is listening
/// </summary>
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
/// - First part reworked to extract the response protocol information
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Error code extraction changed for <see cref="RequestRecord.SkipRetryErrorCode"/>
/// </remarks>
/// <remarks date="2019-Dec-12/16" author="T.Wiedebusch">
/// - Wakeup-message will avoid further timeout (during FW update this is in the range
/// of 15000ms).
/// - Allow one additional retry on decoding error, which is often the wakeup-message.
/// </remarks>
/// <remarks date="2019-Jan-17" author="T.Wiedebusch">
/// - Send time reminder for timeout time calculation as output in log-file,
/// - OnRecordIsDecoded?.Invoke moved before return to assure that the Acknowledge status is set.
/// </remarks>
/// <remarks date="2019-Jan-22" author="T.Wiedebusch">
/// - Avoid activation of wakeup retry if record is meanwhile acknowledged.
/// </remarks>
public override void DecodeRecord(IPortDataEventArgs data)
{
//the request response record covers the entire request protocol
var responseRecord = GetTransmitProtocol().DecodeDataForLogicLayer((List<Byte>)data.GetData());
//if the current record is invalid or on protocol decoding failure or wakeup message
if (null == _recordInProcess || responseRecord.Count == 0)
{
//avoid wakeup retry on acknowledged record
if (null != _recordInProcess && RequestAcknowledgeState.Ok != _recordInProcess.Acknowledge)
{
_recordInProcess.Acknowledge = RequestAcknowledgeState.DecodingError;
//allow one more retry
if (_recordInProcess.RetryCtr == 0 && _recordInProcess.WakeupMessageRetryCtr == 0)
{
_logger.Debug($"{_ident} WakeupMessageRetry(1)");
_recordInProcess.WakeupMessageRetryCtr = 1;
_recordInProcess.RetryCtr--;
}
}
//reset timeout to a normal value if timeout is extremely high but response received
//even if it is a wakeup-message and cannot be decoded
if (_timeOutMs > CommunicationConfig.BusyTimeoutMs)
_timeOutMs = CommunicationConfig.BusyTimeoutMs;
return;
}
//log request protocol content
_logger.Debug(_recordInProcess.HideDataInLog
? $"{_ident} DecodedRecord(*********************************)"
: $"{_ident} DecodedRecord({BitConverter.ToString(responseRecord.ToArray())})");
//extract information command
var replyCmd = responseRecord[ResponseCommandIndex];
//extract position of error for multiple access
if (replyCmd == Commands.MultipleReadDataReply || replyCmd == Commands.MultipleWriteDataReply)
{
_recordInProcess.ResponseErrorPosition =
(UInt16)((responseRecord[ResponseMultiErrorPosition] & 0x00FF) |
(UInt16)((responseRecord[ResponseMultiErrorPosition + 1] << 8) & 0xFF00));
}
//extract the error codes from response record to _record in process
_recordInProcess.ResponseErrorBase = responseRecord[ResponseErrorCodeIndex + ErrorCodeBaseIndex];
_recordInProcess.ResponseErrorReason = responseRecord[ResponseErrorCodeIndex + ErrorCodeReasonIndex];
//combine error base and error reason
_recordInProcess.ResponseErrorCode = (UInt16)(_recordInProcess.ResponseErrorReason |
(_recordInProcess.ResponseErrorBase << 8));
_recordInProcess.ResponsePayload = new List<Byte>();
switch (replyCmd)
{
case Commands.MultipleReadDataReply:
_recordInProcess.ResponsePayload.AddRange(
responseRecord.GetRange(ResponseMultiDataIndex, responseRecord.Count - ResponseMultiHeaderSize));
break;
case Commands.ReadDataReply:
_recordInProcess.ResponsePayload.AddRange(responseRecord.GetRange(ResponseDataIndex, RegisterDefinition.ChunkSize));
break;
}
//Deny access if reply does not match
if (_recordInProcess.ResponseCommand != replyCmd)
{
_logger.Fatal($"{_ident} Wrong command received({replyCmd:X2})," +
$" expected command({_recordInProcess.ResponseCommand:X2})");
_recordInProcess.Acknowledge = RequestAcknowledgeState.CommandError;
OnRecordIsDecoded?.Invoke(this, new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
return;
}
//examine error code, if base (AppId) is not 0 the reason 0 may be an error!!!!
if (_recordInProcess.ResponseErrorBase != 0 || _recordInProcess.ResponseErrorReason != 0)
{
_logger.Warn($"{_ident} Error code received(0x{_recordInProcess.ResponseErrorBase:X2}" +
$"{_recordInProcess.ResponseErrorReason:X2})");
_recordInProcess.Acknowledge = RequestAcknowledgeState.MeterError;
CheckErrorCode();
OnRecordIsDecoded?.Invoke(this, new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
return;
}
//register password acknowledge
if (_recordInProcess.Register.GetIdent() == Register.Configexchange.Password)
{
OnAuthorizationGrant?.Invoke(null, null);
}
//dispatch data
if (_recordInProcess.ResponsePayload != null)
{
OnMeterRegisterUpdated?.Invoke(this, new RegisterUpdatedEventArgs(_recordInProcess.Register,
_recordInProcess.ResponsePayload.ToArray()));
}
_recordInProcess.Acknowledge = RequestAcknowledgeState.Ok;
OnRecordIsDecoded?.Invoke(this, new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
}
/// <summary>
/// Method to send basic Commands to Meter. Creates an integer of 4 bytes for the payload.
/// Supported Commands are: <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
/// Calculate CRCs and Command length and check if command is valid.
/// Use <see cref="AddRecordToSendFifo" /> to push data to Port/Meter.
/// </summary>
/// <param name="command">
/// Supported <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
/// </param>
/// <param name="meterRegister">Register to Read or Write, null for <see cref="Commands.QueryCaps" /></param>
/// <param name="payload">
/// Data to push into the <see cref="RegisterDefinition" />.
/// must be null on Read Commands ( <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />)
/// and not null on WriteData (<see cref="Commands.WriteData" /> and <see cref="Commands.MultipleWriteData" />)
/// </param>
/// <param name="expectedLength"></param>
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
/// <param name="skipRetryErrorCode">mask to skip reties on error</param>
/// <returns>an new command just send to port/meter </returns>
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
/// - Reworked to zero pad payload with chunks of 4 bytes, the caller needn't take care of the size
/// </remarks>
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
/// - Corrected payload content in request protocol
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Skip retries on specific error mask to speed up communication on functional errors
/// or informational feed backs (e.g FW not installed 0x0004)
/// <see cref="RequestRecord.ResponseErrorCode"/>
/// </remarks>
/// <remarks date="2019-Aug-28" author="R.Drabesch">
/// - Multiple read for string split to simple reads.
/// </remarks>
/// <remarks date="2019-Jan-18" author="T.Wiedebusch">
/// - Default <see cref="CommunicationConfig.SkipRetryErrorCode"/> set.
/// </remarks>
public RequestRecord CommandToMeter(Byte command, RegisterDefinition meterRegister = null,
Byte[] payload = null, Int32? expectedLength = null, Boolean hideDataInLog = false,
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
{
if (command == Commands.MultipleReadData && meterRegister != null && meterRegister.DataType == typeof(String))
{
var completeResponse = new List<Byte>();
while (true)
{
var cRecord = CommandToMeter(Commands.ReadData, meterRegister, payload, expectedLength,
hideDataInLog, skipRetryErrorCode);
var rest = ProcessRecordList();
if (rest == RequestAcknowledgeState.Ok && cRecord != null && cRecord.ResponsePayload != null)
{
completeResponse.AddRange(cRecord.ResponsePayload);
if (cRecord.ResponsePayload.Contains(0))
{
cRecord.ResponsePayload = completeResponse;
OnMeterRegisterUpdated?.Invoke(this, new RegisterUpdatedEventArgs(meterRegister, completeResponse.ToArray()));
if (expectedLength.HasValue && completeResponse.Count < expectedLength.Value)
{
_logger.Debug($"Too Short. Payload contains zero");
}
return cRecord;
}
}
else
{
if (expectedLength.HasValue && completeResponse.Any() && completeResponse.Count < expectedLength.Value)
{
if (rest != RequestAcknowledgeState.Ok)
{
_logger.Debug($"Too Short.Request Acknowledge State is {rest}");
}
else if (cRecord == null)
{
_logger.Debug($"Too Short.Record is empty or null");
}
else if (cRecord.ResponsePayload == null)
{
_logger.Debug($"Too Short.ResponsePayload is empty or null");
}
else
{
_logger.Debug($"Too Short.Unkown reason");
}
}
return cRecord;
}
}
}
//the minimum payload is one 4 byte chunk
var chunkSize = RegisterDefinition.ChunkSize;
if (payload == null)
{
payload = new Byte[chunkSize];
for (var i = 0; i < chunkSize; i++)
{
payload[i] = FillByte;
}
}
//real payload based on 4 byte chunks
var requestPayload = new List<Byte>();
requestPayload.AddRange(payload);
//fill-byte padding
if (payload.Length % chunkSize != 0)
{
for (var i = 0; i < chunkSize - payload.Length % chunkSize; i++)
{
requestPayload.Add(FillByte);
}
}
//request protocol excluding the command
var requestProtocol = new List<Byte>();
if (meterRegister != null)
{
requestProtocol.Add(meterRegister.RegisterAddress[1]);
requestProtocol.Add(meterRegister.RegisterAddress[0]);
if (command == Commands.MultipleWriteData)
{
//set counter to inform meter writes are expected
//the payload size is already padded to 4 byte chunks
var numberOfChunks = (UInt16)(requestPayload.Count / chunkSize);
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
}
if (command == Commands.MultipleReadData)
{
//TODO THW create expected size out of meterRegister.DataType
if (!expectedLength.HasValue)
{
if ((meterRegister.DataType == typeof(UInt64) || meterRegister.DataType == typeof(Int64)))
{
expectedLength = 8;
}
else
{
expectedLength = 24;
}
}
var numberOfChunks = (UInt16)(expectedLength.Value / chunkSize);
if (expectedLength % chunkSize != 0) numberOfChunks++;
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
}
//add always the payload to the request protocol
requestProtocol.AddRange(requestPayload);
}
return AddRecordToSendFifo(command, requestProtocol.ToArray(), meterRegister,
hideDataInLog, skipRetryErrorCode);
}
/// <summary>
/// Analyzes the error code
/// </summary>
public void CheckErrorCode()
{
if (_recordInProcess.ResponseErrorBase == (Byte)ConfigExErrors.Base)
{
if (_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.AuthenticationFail ||
_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.AccessDenied ||
_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.LockedOut)
{
_recordInProcess.Acknowledge = RequestAcknowledgeState.AuthorizationRequired;
}
}
}
/// <summary>
/// Reorders the record list so that first entry is the defined topRegister
/// </summary>
/// <param name="topRegister"></param>
public void ReorderRecordList(String topRegister)
{
_recordsSendFifo.Enqueue(_recordInProcess);
//reorder FIFO to bring login at first position
for (var i = 0; i < _recordsSendFifo.Count; i++)
{
RequestRecord tmpRecord;
_recordsSendFifo.TryPeek(out tmpRecord);
if (tmpRecord == null || tmpRecord.Register.GetIdent() == topRegister)
{
break;
}
_recordsSendFifo.TryDequeue(out tmpRecord);
_recordsSendFifo.Enqueue(tmpRecord);
}
}
/// <summary>
/// Checks the active register to access
/// </summary>
/// <param name="registerName"></param>
/// <returns></returns>
public Boolean ContainsRegisterIdent(String registerName)
{
return _recordsSendFifo.Any(a => a.Register.GetIdent().ToLower() == registerName.ToLower());
}
}
}
@@ -0,0 +1,275 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D32ED790-7E1E-435B-8619-9F906C992066}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Consts\Commands.cs" />
<Compile Include="Consts\ConfigExErrors.cs" />
<Compile Include="Consts\HighLevelErrors.cs" />
<Compile Include="Consts\LowLevelErrors.cs" />
<Compile Include="Consts\RequestAcknowledgeState.cs" />
<Compile Include="Exceptions\RequestProtocolException.cs" />
<Compile Include="RequestRecord.cs" />
<Compile Include="EventArguments\AuthorizationRequiredEventArgs.cs" />
<Compile Include="RequestProtocol.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1b02c79e-0b19-43e2-8f6b-71ef0c786c97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\..\Utils\Logging\Logging.csproj">
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\..\DataPackages\DataPackages.csproj">
<Project>{0E524D43-7B66-45CD-AA74-41D51AFC3D99}</Project>
<Name>DataPackages</Name>
</ProjectReference>
<ProjectReference Include="..\..\GenesisConfig\GenesisConfig.csproj">
<Project>{F013A56B-7BE6-4852-9CD8-B971CD1ADD0C}</Project>
<Name>GenesisConfig</Name>
</ProjectReference>
<ProjectReference Include="..\..\Registers\Registers.csproj">
<Project>{F4AAF7E6-7333-4284-B735-E32DEB076C64}</Project>
<Name>Registers</Name>
</ProjectReference>
<ProjectReference Include="..\ProtocolCore\ProtocolCore.csproj">
<Project>{8BDD2789-96D4-43EA-AAD7-0F49D0347998}</Project>
<Name>ProtocolCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
{
/// <summary>
/// Holds request commands with detail parameters to see processing state
/// </summary>
public class RequestRecord
{
/// <summary>
/// Indicates the base command <see cref="Commands" />
/// </summary>
public readonly Byte Command;
/// <summary>
/// Indicates the base command <see cref="Commands" />
/// </summary>
public readonly Byte ResponseCommand;
/// <summary>
/// the register the command refers to.
/// needed to set Register dictionary to link response with dictionary key
/// </summary>
public readonly RegisterDefinition Register;
/// <summary>
/// Command acknowledged
/// </summary>
public RequestAcknowledgeState Acknowledge = RequestAcknowledgeState.NoResponse;
/// <summary>
/// Retry counter
/// </summary>
public Int32 RetryCtr = 0;
/// <summary>
/// Wakeup-message retry counter
/// </summary>
public Int32 WakeupMessageRetryCtr = 0;
/// <summary>
/// indicates error base on <see cref="RequestRecord"/>
/// </summary>
public Byte ResponseErrorBase = 0xFF;
/// <summary>
/// indicates error reason on <see cref="RequestRecord"/>
/// </summary>
public Byte ResponseErrorReason = 0xFF;
/// <summary>
/// Combined error code of base and reason
/// </summary>
public UInt16 ResponseErrorCode = 0xFFFF;
/// <summary>
/// Chunk position of error on multiple read/write access
/// </summary>
public UInt16 ResponseErrorPosition = 0;
/// <summary>
/// Data containing the request protocol
/// </summary>
public List<Byte> RequestProtocolData;
/// <summary>
/// Encoded with transmit protocol, ready to stream to port as is
/// </summary>
public List<Byte> EncodedRequestData { get; internal set; }
/// <summary>
/// Extracted payload of response
/// </summary>
public List<Byte> ResponsePayload;
/// <summary>
/// Extracted payload of response
/// </summary>
public readonly Int32 ResponseTimeoutMs;
/// <summary>
/// avoid logging for e.g. password
/// </summary>
public Boolean HideDataInLog;
/// <summary>
/// error mask to skip retries for functional errors <see cref="ResponseErrorCode"/>
/// </summary>
public UInt16 SkipRetryErrorCode;
/// <summary>
/// Ctor for an base command <see cref="Commands" />
/// </summary>
/// <param name="command"><see cref="Commands" /> as an byte</param>
/// <param name="requestProtocolData">Request protocol data for logging</param>
/// <param name="encodedRequestData"> Ready to send data encoded with transmit protocol retries</param>
/// <param name="responseTimeoutMs">Timeout for response</param>
/// <param name="register"><see cref="RegisterDefinition"/> to do command with</param>
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
/// <param name="skipRetryErrorCode">error mask to skip retries <see cref="ResponseErrorCode"/></param>
public RequestRecord(Byte command, List<Byte> requestProtocolData = null, List<Byte> encodedRequestData = null,
Int32 responseTimeoutMs = 100, RegisterDefinition register = null, Boolean hideDataInLog = false,
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
{
Command = command;
ResponseCommand = (Byte)(command + 1);
Register = register;
RequestProtocolData = requestProtocolData;
EncodedRequestData = encodedRequestData;
ResponseTimeoutMs = responseTimeoutMs;
HideDataInLog = hideDataInLog;
SkipRetryErrorCode = skipRetryErrorCode;
}
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Streaming")]
[assembly: AssemblyDescription("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Streaming")]
[assembly: Guid("CCA84683-47FA-458F-B11C-AE9864DFC580")]
@@ -0,0 +1,446 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
using Xylem.Common.Utils.Crc16Ccitt;
// ReSharper disable UnusedMember.Local
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol
{
/// <summary>
/// Data fields and definitions for GENESIS streaming protocol
/// </summary>
public class StreamingDecoder
{
private const Double MilliLitersToQmFactor = 1.0E-6;
private const Double DefaultVolumeScaleRawPerMl = 1024.0;
private const Double DefaultVolumeFactorRawToQm = MilliLitersToQmFactor /
DefaultVolumeScaleRawPerMl;
private const Double MaxAccuVolumeRaw = 0x100000000; //2^32
private const Double DefaultAccuDutOverflowVolumeCm = MaxAccuVolumeRaw *
DefaultVolumeFactorRawToQm;
private const Double CpuTimeToSecondsFactor = 1.0 / 0x10000;
private const Double CpuTimeOverflowS = 0x100000000 * CpuTimeToSecondsFactor;
private const Double DisplayMlSetupDutOverflowVolumeCm = 1000.0; //overflow of LCD if set to ml
private const Double TofToSecondsFactor38Bit = 1.0 / 0x4000000000; // 2^38
private const Double AmplitudeToVoltFactor = 1.0 / 0x400000 / 1000.0; // 2^22
private const Double PulseWidthToRelFactor = 1.0 / 0x100; // 2^8
private const UInt16 IsValidProtG = 1;
private const UInt16 IsValidProtH = 0;
private CalibrationRecord _dataCalibRec;
/// <summary>
/// Default data for
/// </summary>
private readonly FlowTestRecord _dataDefault = new FlowTestRecord
{
VolumeCm = 0.0,
OverflowVolumeCm = DisplayMlSetupDutOverflowVolumeCm,
TimeS = 0.0,
OverflowTimeS = CpuTimeOverflowS,
Crc = 0xFFFF,
IsValid = false
};
private readonly CalibrationRecord _rawDataDefault = new CalibrationRecord
{
Channel = 0,
Validation = 0xFFFF,
TotalTimeOfFlightS = 0.0,
DeltaTimeOfFlightS = 0.0,
VolumeScaleRawPerMl = DefaultVolumeScaleRawPerMl,
VolumeFactorRawToQm = DefaultVolumeFactorRawToQm,
DeltaVolumeRaw = 0.0,
DeltaVolumeQm = 0.0,
AccuVolumeRaw = 0.0,
VolumeCm = 0.0,
OverflowVolumeCm = DefaultAccuDutOverflowVolumeCm,
SampleIntervalS = 0.0,
AmplitudeUpV = 0.0,
AmplitudeDownV = 0.0,
PulseWidthRatioUp = 0.0,
PulseWidthRatioDown = 0.0,
TemperatureRaw = 20.0,
TemperaturePowFactor = 1.0,
TemperatureDegC = 20.0,
TimeS = 0.0,
OverflowTimeS = CpuTimeOverflowS,
Crc = 0xFFFF,
IsValid = false
};
private readonly FlowTestRecord _dataFlowTestRec;
private String _rawMsgForCrc;
/// <summary>
/// Calibration data
/// </summary>
public CalibrationRecord DataCalib = new CalibrationRecord();
private readonly Boolean _ignoreCorruptedData;
/// <summary>
/// Constructor initializes all decoded members with default values
/// </summary>
public StreamingDecoder(Boolean ignoreCorruptedData = true)
{
_dataFlowTestRec = _dataDefault;
_dataCalibRec = _rawDataDefault;
_ignoreCorruptedData = ignoreCorruptedData;
}
/// <summary>
/// Flow test data
/// </summary>
public FlowTestRecord DataFlowTest { get; private set; }
/// <summary>
/// Decoding the raw message
/// </summary>
/// <param name="rawMsg">message received as one line delimited with LF</param>
/// <returns>true if decoding was successful and data has been validated</returns>
public Boolean DecodeMsg(String rawMsg)
{
var isSuccessfulDecoded = false;
try
{
// save the raw message for CRC calculation before separation to fields
_rawMsgForCrc = rawMsg;
// extract message and split it to fields
rawMsg = rawMsg.Replace('\n', ' ');
var field = rawMsg.Split(' ');
switch (field[0])
{
case "@f":
isSuccessfulDecoded = DecodeProtocolF(field);
if (isSuccessfulDecoded || !_ignoreCorruptedData)
DataFlowTest = _dataFlowTestRec;
break;
case "@g":
{
isSuccessfulDecoded = DecodeProtocolG(ref _dataCalibRec, field);
if (isSuccessfulDecoded || !_ignoreCorruptedData)
DataCalib = _dataCalibRec;
break;
}
case "@h":
{
isSuccessfulDecoded = DecodeProtocolH(ref _dataCalibRec, field);
if (isSuccessfulDecoded || !_ignoreCorruptedData)
DataCalib = _dataCalibRec;
break;
}
}
}
catch (Exception ex)
{
// ignored
}
return isSuccessfulDecoded;
}
/// <summary>
/// Extracting message from string fields for protocol 'f'
/// </summary>
/// <param name="field">Separated fields containing the measurement as string</param>
/// <returns></returns>
private Boolean DecodeProtocolF(IList<String> field)
{
_dataFlowTestRec.DecodedTime = DateTimeOffset.UtcNow;
_dataFlowTestRec.IsValid = true;
//extract bytes of raw message for CRC calculation each character removing CRC (4 char)
var byteArraySize = _rawMsgForCrc.Length - 4;
var byteArray = new Byte[byteArraySize];
for (var i = 0; i < byteArraySize; i++)
byteArray[i] = (Byte)_rawMsgForCrc[i];
_rawMsgForCrc = null;
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
//extract received CRC
_dataFlowTestRec.Crc = ushort.Parse(field[(Int32)ProtFsubString.Crc],
NumberStyles.HexNumber);
//compare received with calculated CRC
if (calculatedCrc != _dataFlowTestRec.Crc)
{
_dataFlowTestRec.IsValid = false;
return false;
}
_dataFlowTestRec.IsValid = true;
//build result values, the display volume can be positive or negative!
_dataFlowTestRec.VolumeCm = int.Parse(field[(Int32)ProtFsubString.DisplayVolume],
NumberStyles.AllowHexSpecifier) * MilliLitersToQmFactor;
_dataFlowTestRec.TimeS = uint.Parse(field[(Int32)ProtFsubString.CpuTime],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
return _dataFlowTestRec.IsValid;
}
/// <summary>
/// Extracting message from string fields to individual raw channel for protocol 'g'
/// </summary>
/// <param name="dataProtGRec">Reference to result structure for raw data for one channel</param>
/// <param name="field">Separated fields containing the measurement as string</param>
/// <returns>true if protocol is valid</returns>
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
/// </remarks>
private Boolean DecodeProtocolG(ref CalibrationRecord dataProtGRec, IList<String> field)
{
dataProtGRec.DecodedTime = DateTimeOffset.UtcNow;
dataProtGRec.IsValid = false;
//extract bytes of raw message for CRC calculation each character removing CRC (4 char)
var byteArraySize = _rawMsgForCrc.Length - 4;
var byteArray = new Byte[byteArraySize];
for (var i = 0; i < byteArraySize; i++)
byteArray[i] = (Byte)_rawMsgForCrc[i];
_rawMsgForCrc = null;
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
//extract received CRC
dataProtGRec.Crc = ushort.Parse(field[(Int32)ProtGsubString.Crc],
NumberStyles.HexNumber);
dataProtGRec.Channel = ushort.Parse(field[(Int32)ProtGsubString.ChanNo],
NumberStyles.HexNumber);
dataProtGRec.Validation = ushort.Parse(field[(Int32)ProtGsubString.Validation],
NumberStyles.HexNumber);
//compare received with calculated CRC
if (calculatedCrc != dataProtGRec.Crc || IsValidProtG != dataProtGRec.Validation)
return false;
//decode rest of message only if CRC and mode are valid
dataProtGRec.IsValid = true;
// Delta time of flight
dataProtGRec.DeltaTimeOfFlightS = int.Parse(field[(Int32)ProtGsubString.Dtof],
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
// Delta raw volume between last sample
dataProtGRec.DeltaVolumeRaw = uint.Parse(field[(Int32)ProtGsubString.RawDVolume],
NumberStyles.AllowHexSpecifier);
//volume scaling
var volumeRawScale = uint.Parse(field[(Int32)ProtGsubString.VolumeScale],
NumberStyles.AllowHexSpecifier);
dataProtGRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
dataProtGRec.VolumeFactorRawToQm = MilliLitersToQmFactor / dataProtGRec.VolumeScaleRawPerMl;
dataProtGRec.OverflowVolumeCm = MaxAccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
//Calculate volume in cubic meters out of the raw volume
dataProtGRec.DeltaVolumeQm = dataProtGRec.DeltaVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
//accumulated volume for each channel received from water meter scaled with volumeScale
//the volume can just be positive
dataProtGRec.AccuVolumeRaw = uint.Parse(field[(Int32)ProtGsubString.AccuVolume],
NumberStyles.AllowHexSpecifier);
dataProtGRec.VolumeCm = dataProtGRec.AccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
//Sample interval
dataProtGRec.SampleIntervalS = uint.Parse(field[(Int32)ProtGsubString.SampleInterval],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
//amplitude for high threshold in V
dataProtGRec.AmplitudeUpV = uint.Parse(field[(Int32)ProtGsubString.AmplitudeUp],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//amplitude for low threshold in V
dataProtGRec.AmplitudeDownV = uint.Parse(field[(Int32)ProtGsubString.AmplitudeDown],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//pulse width ratio high threshold
dataProtGRec.PulseWidthRatioUp = uint.Parse(field[(Int32)ProtGsubString.PulseWidthRatioUp],
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
//pulse width ratio low threshold
dataProtGRec.PulseWidthRatioDown = uint.Parse(field[(Int32)ProtGsubString.PulseWidthRatioDown],
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
//raw temperature
dataProtGRec.TemperatureRaw = int.Parse(field[(Int32)ProtGsubString.RawTemperature],
NumberStyles.AllowHexSpecifier);
//temperature scaling
dataProtGRec.TemperaturePowFactor = uint.Parse(field[(Int32)ProtGsubString.TemperatureScale],
NumberStyles.AllowHexSpecifier);
//Calculate temperature
dataProtGRec.TemperatureDegC = dataProtGRec.TemperatureRaw /
Math.Pow(2.0, dataProtGRec.TemperaturePowFactor);
//absolute CPU time, started at LED mode 3 activation
dataProtGRec.TimeS = uint.Parse(field[(Int32)ProtGsubString.CpuTime],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
return true;
}
/// <summary>
/// Extracting message from string fields to individual raw channel for protocol 'g'
/// </summary>
/// <param name="dataProtHRec">Reference to result structure for raw data for one channel</param>
/// <param name="field">Separated fields containing the measurement as string</param>
/// <returns>true if protocol is valid</returns>
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
/// </remarks>
private Boolean DecodeProtocolH(ref CalibrationRecord dataProtHRec, IList<String> field)
{
dataProtHRec.DecodedTime = DateTimeOffset.UtcNow;
dataProtHRec.IsValid = false;
//extract bytes of raw message for CRC calculation each character removing CRC (4 char)
var byteArraySize = _rawMsgForCrc.Length - 4;
var byteArray = new Byte[byteArraySize];
for (var i = 0; i < byteArraySize; i++)
byteArray[i] = (Byte)_rawMsgForCrc[i];
_rawMsgForCrc = null;
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
//extract received CRC
dataProtHRec.Crc = ushort.Parse(field[(Int32)ProtHsubString.Crc],
NumberStyles.HexNumber);
dataProtHRec.Channel = ushort.Parse(field[(Int32)ProtHsubString.ChanNo],
NumberStyles.HexNumber);
dataProtHRec.Validation = ushort.Parse(field[(Int32)ProtHsubString.Validation],
NumberStyles.HexNumber);
//compare received with calculated CRC
if (_ignoreCorruptedData &&
(calculatedCrc != dataProtHRec.Crc || IsValidProtH != dataProtHRec.Validation))
{
return false;
}
// decode rest of message only if CRC and mode are valid
dataProtHRec.IsValid = true;
// Total time of flight
dataProtHRec.TotalTimeOfFlightS = int.Parse(field[(Int32)ProtHsubString.Ttof],
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
//Delta time of flight
dataProtHRec.DeltaTimeOfFlightS = int.Parse(field[(Int32)ProtHsubString.Dtof],
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
dataProtHRec.RAWTotalTimeOfFlight =
int.Parse(field[(Int32) ProtHsubString.Ttof], NumberStyles.AllowHexSpecifier);
dataProtHRec.RAWDeltaTimeOfFlight = int.Parse(field[(Int32) ProtHsubString.Dtof], NumberStyles.AllowHexSpecifier);
//Delta raw volume between two samples
dataProtHRec.DeltaVolumeRaw = uint.Parse(field[(Int32)ProtHsubString.RawDVolume],
NumberStyles.AllowHexSpecifier);
//volume scaling
var volumeRawScale = uint.Parse(field[(Int32)ProtHsubString.VolumeScale],
NumberStyles.AllowHexSpecifier);
dataProtHRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
dataProtHRec.VolumeFactorRawToQm = MilliLitersToQmFactor / dataProtHRec.VolumeScaleRawPerMl;
//newdataProtHRec.OverflowVolumeCm = MaxAccuVolumeRaw * newdataProtHRec.VolumeFactorRawToQm;
//Calculate volume in cubic meters out of the raw volume
dataProtHRec.DeltaVolumeQm = dataProtHRec.DeltaVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
//accumulated volume for each channel received from water meter scaled with volumeScale
//the volume can just be positive
dataProtHRec.AccuVolumeRaw = uint.Parse(field[(Int32)ProtHsubString.AccuVolume],
NumberStyles.AllowHexSpecifier);
dataProtHRec.VolumeCm = dataProtHRec.AccuVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
//Sample interval
dataProtHRec.SampleIntervalS = uint.Parse(field[(Int32)ProtHsubString.SampleInterval],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
//amplitude for high threshold in V
dataProtHRec.AmplitudeUpV = uint.Parse(field[(Int32)ProtHsubString.AmplitudeUp],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//amplitude for low threshold in V
dataProtHRec.AmplitudeDownV = uint.Parse(field[(Int32)ProtHsubString.AmplitudeDown],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//raw temperature
dataProtHRec.TemperatureRaw = int.Parse(field[(Int32)ProtHsubString.RawTemperature],
NumberStyles.AllowHexSpecifier);
//temperature scaling
dataProtHRec.TemperaturePowFactor = uint.Parse(field[(Int32)ProtHsubString.TemperatureScale],
NumberStyles.AllowHexSpecifier);
//Calculate temperature
dataProtHRec.TemperatureDegC = dataProtHRec.TemperatureRaw / (
Math.Pow(2.0, dataProtHRec.TemperaturePowFactor));
//absolute CPU time, started at LED mode 3 activation
dataProtHRec.TimeS = uint.Parse(field[(Int32)ProtHsubString.CpuTime],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
return true;
}
/// field position in protocol 'f'
private enum ProtFsubString
{
//do not remove this needed for position in record
ProtType,
DisplayVolume,
CpuTime,
Crc
}
/// field position in protocol 'g'
private enum ProtGsubString
{
//do not remove this needed for position in record
ProtType,
ChanNo,
Validation,
Dtof,
RawDVolume,
AccuVolume,
VolumeScale,
SampleInterval,
AmplitudeUp,
AmplitudeDown,
PulseWidthRatioUp,
PulseWidthRatioDown,
RawTemperature,
TemperatureScale,
CpuTime,
Crc
}
/// field position in protocol 'h'
private enum ProtHsubString
{
//do not remove this needed for position in record
ProtType,
ChanNo,
Validation,
Ttof,
Dtof,
RawDVolume,
AccuVolume,
VolumeScale,
SampleInterval,
AmplitudeUp,
AmplitudeDown,
RawTemperature,
TemperatureScale,
CpuTime,
Crc
}
}
}
@@ -0,0 +1,85 @@
using System;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol
{
/// <inheritdoc />
/// <summary>
/// Layer between <see cref="T:Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol.StreamingDecoder" /> CRC16CCITT handler and Events
/// block trashy telegrams
/// </summary>
public class StreamingProtocol : BaseProtocol
{
/// <summary>
/// Decode data and hold results
/// </summary>
private StreamingDecoder _streamingDecode;
private readonly Boolean _ignoreCorruptedData;
/// <inheritdoc />
public override event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <inheritdoc />
public override void DecodeRecord(IPortDataEventArgs dataArgs)
{
var data = (String)dataArgs.GetData();
//Data decoding
_streamingDecode = new StreamingDecoder(_ignoreCorruptedData);
_streamingDecode.DecodeMsg(data);
//Event for new data
if (_streamingDecode.DataFlowTest != null)
{
_streamingDecode.DataFlowTest.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
_streamingDecode.DataFlowTest.ReceivedTime = dataArgs.GetReceivedTime();
if (_streamingDecode.DataFlowTest.IsValid)
{
OnRecordIsDecoded?.Invoke(this,
new FlowDataEventArgs { NewData = _streamingDecode.DataFlowTest, RawData = data });
}
else if (!_ignoreCorruptedData)
{
OnRecordIsDecoded?.Invoke(this,
new FlowDataEventArgs { NewData = _streamingDecode.DataFlowTest, RawData = data });
}
}
else if (_streamingDecode.DataCalib != null)
{
_streamingDecode.DataCalib.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
_streamingDecode.DataCalib.ReceivedTime = dataArgs.GetReceivedTime();
if (_streamingDecode.DataCalib.IsValid)
{
OnRecordIsDecoded?.Invoke(this, new CalibDataEventArgs
{
CalibChl = _streamingDecode.DataCalib,
RawData = data
});
}
else if (!_ignoreCorruptedData)
{
OnRecordIsDecoded?.Invoke(this, new CalibDataEventArgs
{
CalibChl = _streamingDecode.DataCalib,
RawData = data
});
}
}
//toDo: handle trashy telegrams
}
/// <inheritdoc />
public StreamingProtocol(String ident, Boolean ignoreCorruptedData = true) : base(ident)
{
_ignoreCorruptedData = ignoreCorruptedData;
}
}
}

Some files were not shown because too many files have changed in this diff Show More