Initial commit
This commit is contained in:
@@ -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.*")]
|
||||
+180
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
+23
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -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")]
|
||||
|
||||
+171
@@ -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>
|
||||
+26
@@ -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")]
|
||||
+234
@@ -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
|
||||
}
|
||||
}
|
||||
+25
@@ -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;
|
||||
}
|
||||
}
|
||||
+36
@@ -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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+25
@@ -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;
|
||||
}
|
||||
}
|
||||
+59
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -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>
|
||||
+7
@@ -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
Reference in New Issue
Block a user