Initial commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
<?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>{29617814-313E-409E-AA5D-59C78A9D4DF1}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol</RootNamespace>
|
||||
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol</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.StreamingProtocol.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\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Streaming.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.Streaming.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.Streaming.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.Streaming.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.Streaming.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.Streaming.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.Streaming.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.Streaming.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.StreamingProtocol.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\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Streaming.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.Streaming.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="StreamingProtocol.cs" />
|
||||
<Compile Include="StreamingDecoder.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="..\..\..\..\..\Metrology\Measurements\Measurements.csproj">
|
||||
<Project>{6CCDB656-C676-4360-BABA-C4596AAD175D}</Project>
|
||||
<Name>Measurements</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\..\..\Utils\Crc16Ccitt\Crc16Ccitt.csproj">
|
||||
<Project>{91DEF0C0-AA4F-40AE-8F18-CB717FDE7E7F}</Project>
|
||||
<Name>Crc16Ccitt</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
|
||||
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
|
||||
<Name>PortCore</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\DataPackages\DataPackages.csproj">
|
||||
<Project>{0e524d43-7b66-45cd-aa74-41d51afc3d99}</Project>
|
||||
<Name>DataPackages</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,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="NLog" version="4.6.7" targetFramework="net40" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user