Initial commit

This commit is contained in:
Rowlander
2021-10-01 11:09:20 +02:00
commit fe54fceb1e
1301 changed files with 853973 additions and 0 deletions
@@ -0,0 +1,146 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using Xylem.Common.CommonCore.ThreadWatcher;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore
{
/// <inheritdoc />
/// <summary>
/// Base for all protocol types
/// </summary>
public abstract class BaseProtocol : IProtocol
{
private readonly CancellationTokenSource _decodingToken = new CancellationTokenSource();
/// <inheritdoc />
public abstract event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <inheritdoc />
public virtual event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
private readonly ConcurrentQueue<IPortDataEventArgs> _decodingFifo = new ConcurrentQueue<IPortDataEventArgs>();
//initially do not signal event
private readonly AutoResetEvent _onSyncDecodingThread = new AutoResetEvent(false);
private readonly Thread _decodingThread;
/// <summary>
/// Ident is a combined string of Slot, Port, Protocol and Type
/// </summary>
public readonly String Ident;
private ITransmitProtocol _transmitProtocol;
/// <summary>
/// Starting decoding thread
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
protected BaseProtocol(String ident)
{
Ident = ident;
//assign thread to loop
_decodingThread = new Thread(DecodingThreadLoop) { Name = $"{Ident} Decoding thread" };
//start DecodingThread
ThreadWatcher.Instance.Start(_decodingThread);
}
/// <inheritdoc />
/// <summary>
/// Kill the decoding thread
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public void Dispose()
{
//Cancel receive tokens
_decodingToken.Cancel();
//Run thread again to notice CancellationToken has changed
_onSyncDecodingThread.Set();
//this timeout counter is being used for dispose only
var timeoutCounter = 100;
while (_decodingThread.ThreadState != ThreadState.Stopped && timeoutCounter > 0)
{
Thread.Sleep(1);
timeoutCounter = timeoutCounter - 1;
}
if (_decodingThread.ThreadState != ThreadState.Stopped)
{
//if thread still running, he stuck so try to abort
// try to avoid abort (takes age to run and is not safe)
_decodingThread.Abort();
}
}
/// <summary>
/// Fill FIFO with received data
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
public void FillDecodingBuffer(IPortDataEventArgs data)
{
if (_decodingToken.IsCancellationRequested) return;
//put data to FIFO
_decodingFifo.Enqueue(data);
//put DecodingThread state from WaitSleepJoin to Running
_onSyncDecodingThread.Set();
}
/// <summary>
/// Decoding thread loop calling the individual decoding
/// </summary>
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
/// - Initial
/// </remarks>
private void DecodingThreadLoop()
{
try
{
while (!_decodingToken.IsCancellationRequested)
{
IPortDataEventArgs receivedRecord;
while (_decodingFifo.TryDequeue(out receivedRecord))
{
DecodeRecord(receivedRecord);
}
//put DecodingThread to WaitSleepJoin until next data arrived
_onSyncDecodingThread.WaitOne();
}
}
catch (ThreadAbortException)
{ }
}
/// <summary>
/// The decoding routine
/// </summary>
/// <param name="data"></param>
public abstract void DecodeRecord(IPortDataEventArgs data);
/// <inheritdoc />
public ITransmitProtocol GetTransmitProtocol()
{
return _transmitProtocol;
}
/// <inheritdoc />
public void SetTransmitProtocol(ITransmitProtocol transmitProtocol)
{
_transmitProtocol = transmitProtocol;
}
}
}
@@ -0,0 +1,42 @@
using System;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.Interfaces.Protocols.TransmitProtocol;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore
{
/// <inheritdoc />
/// <summary>
/// Protocol interface
/// </summary>
public interface IProtocol : IDisposable
{
/// <summary>
/// Fill the buffer with data ready for decoding
/// </summary>
/// <param name="rawData"></param>
void FillDecodingBuffer(IPortDataEventArgs rawData );
/// <summary>
/// Record is ready to send including all protocols
/// </summary>
event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
/// <summary>
/// Record is successfully decoded and ready for further processing
/// </summary>
event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <summary>
/// Return of transmit protocol settings
/// </summary>
/// <returns></returns>
ITransmitProtocol GetTransmitProtocol();
/// <summary>
/// Assignment of transmit protocol
/// </summary>
/// <param name="transmitProtocol"></param>
void SetTransmitProtocol(ITransmitProtocol transmitProtocol);
}
}
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Protocols.ProtocolCore")]
[assembly: AssemblyDescription("")]
[assembly: Guid("1A402949-095C-4039-8AE3-6594A7FB2242")]
@@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8BDD2789-96D4-43EA-AAD7-0F49D0347998}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>6</LangVersion>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>
</AssemblyOriginatorKeyFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BaseProtocol.cs" />
<Compile Include="IProtocol.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\CommonCore\ThreadWatcher\ThreadWatcher.csproj">
<Project>{94ca71f3-c5d7-4ec6-ba47-fa8eea3c9406}</Project>
<Name>ThreadWatcher</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2e139f63-b6fe-4ea9-a098-85364fe9b26c}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\..\DataPackages\DataPackages.csproj">
<Project>{0e524d43-7b66-45cd-aa74-41d51afc3d99}</Project>
<Name>DataPackages</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,125 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Commands that the genesis meter support over the request protocol.
/// Functional Specification Breeze Core and Applications Revision:3.03(12748DOC11 - functional spec ICOE472.pdf)
/// </summary>
public static class Commands
{
/// <summary>
/// 9.2.5
///Command 0x00 (NOP)
///This command will do nothing, and will have no response.
/// </summary>
public const Byte Nop = 0x00;
/// <summary>
/// 9.2.6
/// Command 0x01 (Query capabilities)
/// This command will be used by the external computer to discover the protocol parameters that may be varied.
/// These can then be compared with the external computer’s capabilities and the best match selected.
/// </summary>
public const Byte QueryCaps = 0x01;
/// <summary>
/// response on <see cref="QueryCaps"/>
/// </summary>
public const Byte QueryCapsReply = 0x02;
/// <summary>
/// 9.2.7
/// Command 0x03 (Set capabilities)
/// Used to finalize the baud rate and packet settings after negotiation.
/// The reply will be sent at the currently selected baud rate and packet length,
/// after which the settings will take effect
/// </summary>
public const Byte SetCaps = 0x03;
/// <summary>
/// response on <see cref="SetCaps"/>
/// </summary>
public const Byte SetCapsReply = 0x04;
/// <summary>
/// 9.2.8
/// Command 0x05 (Train)
///This command will perform target driven data training, that is, where the target is in control of the data flow.
///See also command 0x11.
/// </summary>
public const Byte Train = 0x05;
/// <summary>
/// response on <see cref="Train"/>
/// </summary>
public const Byte TrainReply = 0x06;
/// <summary>
/// 9.2.9
/// Command 0x07 (Repeat last)
/// Used by the external computer to request the last response to be resent, for example if it was found to be corrupted.
/// Note that the response 0x08 will never be sent, the reply to command 0x07 will be a verbatim resend of the last response.
/// </summary>
public const Byte RepeatLast = 0x07;
/// <summary>
/// response on <see cref="RepeatLast"/>
/// </summary>
public const Byte RepeatLastReply = 0x08;
/// <summary>
/// 9.2.10
///Command 0x09 (Read data)
///This command makes reads of any random selection of configuration registers, up to the maximum packet size negotiated.
/// </summary>
public const Byte ReadData = 0x09;
/// <summary>
/// response on <see cref="ReadData"/>
/// </summary>
public const Byte ReadDataReply = 0x0A;
/// <summary>
/// 9.2.11
/// Command 0x0B (Write data)
/// This command makes writes to any random selection of configuration registers, up to the maximum packet size negotiated.
/// </summary>
public const Byte WriteData = 0x0B;
/// <summary>
/// response on <see cref="WriteData"/>
/// </summary>
public const Byte WriteDataReply = 0x0C;
/// <summary>
/// 9.2.12
///Command 0x0D (Multiple read data)
///This command makes reads of one register multiple times, which will be more efficient than performing successive reads using command 0x09.
///This command will be available from protocol version 0.40, for earlier protocol versions command 0x09 should be used.
/// </summary>
public const Byte MultipleReadData = 0x0D;
/// <summary>
/// response on <see cref="MultipleReadData"/>
/// </summary>
public const Byte MultipleReadDataReply = 0x0E;
/// <summary>
/// 9.2.13
/// Command 0x0F (Multiple write data)
/// This command makes writes to one register multiple times, which will be more efficient than performing successive reads using command 0x0B.
/// This command will be available from protocol version 0.40, for earlier protocol versions command 0x0B should be used.
/// </summary>
public const Byte MultipleWriteData = 0x0F;
/// <summary>
/// response on <see cref="MultipleWriteData"/>
/// </summary>
public const Byte MultipleWriteDataReply = 0x10;
/// <summary>
/// 9.2.14
/// Command 0x11 (Set level)
/// This command will perform external driven data training, that is, where the external computer is in control of the data flow.
/// This command will be available from protocol version 0.42, for earlier protocol versions command 0x05 should be used.
/// </summary>
public const Byte SetLevel = 0x11;
/// <summary>
/// response on <see cref="SetLevel"/>
/// </summary>
public const Byte SetLevelReply = 0x12;
//todo remove?
//public const byte SetBitrateLsb = 0x03;
//public const byte SetBitrateMsb = 0x05;
//public const byte SetPacketSizesLsb = 0x04;
//public const byte SetPacketSizesMsb = 0x05;
//public const byte SetProtocolLsb = 0x02;
//public const byte SetProtocolMsb = 0x05;
}
}
@@ -0,0 +1,31 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Config Exchange error codes.
/// </summary>
public enum ConfigExErrors
{
#pragma warning disable CS1591 //as a matter of course
Base = 0x04,
LockedOut = 0x00,
AuthenticationFail = 0x01,
AccessDenied = 0x02,
UnknownParameter = 0x03,
InUse = 0x04,
SizesDoesNotMatch = 0x05,
CantReadCfgFile = 0x06,
UserNotKnown = 0x07,
CantCreateCfgFile = 0x08,
StoringFailed = 0x09,
StoreCorrupt = 0x0A,
ExpectedWrite = 0x0B,
ExpectedRead = 0x0C,
StopCycling = 0x0D,
TooManyOpen = 0x0E,
NeverOpened = 0x0F,
FileProtected = 0x1F,
PartialRecall = 0x2F,
DefaultPasswordUsed = 0x3F,
#pragma warning restore
}
}
@@ -0,0 +1,25 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Transport errors for request protocol
/// </summary>
public enum HighLevelErrors
{
#pragma warning disable CS1591 //as a matter of course
Base = 0x05,
PayloadCount = 0x00,
InvalidSubReason = 0x01,
InvalidBaudrate = 0x02,
InvalidBufferSize = 0x03,
CrcFailure = 0x04,
UnrecognizedCmd = 0x05,
Framing = 0x06,
Overflow = 0x07,
PacketTimeout = 0x08,
InvalidEscape = 0x09,
UnknownParameter = 0x0A,
TrainingFailed = 0x0B,
NoBreak = 0x0C,
#pragma warning restore
}
}
@@ -0,0 +1,47 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// List of constance for error codes from genesis meter
/// </summary>
public static class LowLevelErrors
{
/// <summary>
/// everything is fine
/// </summary>
public const Int16 NoError = 0;
/// <summary>
/// Port is not open
/// </summary>
public const Int16 SerialPortClosed = -2;
/// <summary>
/// fails to write to serial port
/// </summary>
public const Int16 SerialPortWriteFailure = -3;
/// <summary>
/// fails to read from serial port
/// </summary>
public const Int16 SerialPortReadFailure = -4;
/// <summary>
/// Command is to long
/// </summary>
public const Int16 MaxDataLength = -5;
/// <summary>
/// deeper exception, check out log if this happen
/// </summary>
public const Int16 Exception = -6;
/// <summary>
/// wrong CRC
/// </summary>
public const Int16 CrcCalcError = -7;
/// <summary>
/// something strange
/// </summary>
public const Int16 Unknown = -8;
/// <summary>
/// Timeout occur
/// </summary>
public const Int16 Timeout = -9;
}
}
@@ -0,0 +1,36 @@
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts
{
/// <summary>
/// Acknowledge feedback from meter after communication
/// </summary>
public enum RequestAcknowledgeState
{
/// <summary>
/// Response missing
/// </summary>
NoResponse,
/// <summary>
/// Response command not match the required command
/// </summary>
CommandError,
/// <summary>
/// Lost connection (logged out from meter), a re-authorization is required
/// to access this command and/or register
/// </summary>
AuthorizationRequired,
/// <summary>
/// Meter error received, a retry may be useful
/// </summary>
MeterError,
/// <summary>
/// The response record couldn't be decoded
/// </summary>
DecodingError,
/// <summary>
/// Valid meter response record
/// </summary>
Ok
}
}
@@ -0,0 +1,25 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.EventArguments
{
/// <inheritdoc />
/// <summary>
/// if session is gone and a re authorization is necessary
/// </summary>
public class AuthorizationRequiredEventArgs : EventArgs
{
/// <inheritdoc />
/// <summary>
/// Ctor
/// </summary>
/// <param name="command">this command will be resend after authorization</param>
public AuthorizationRequiredEventArgs(RequestRecord command = null)
{
Command = command;
}
/// <summary>
/// Command to resend
/// </summary>
public RequestRecord Command;
}
}
@@ -0,0 +1,59 @@
using System;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Exceptions
{
/// <summary>
/// Holds all errors can occur on the request protocol
/// </summary>
public class RequestProtocolException : Exception
{
/// <summary>
/// Exception occurred on this command
/// </summary>
public Object Command;
/// <summary>
/// Exception occurred while receiving this data
/// </summary>
public Object ReceivedData;
/// <summary>
/// Error is interpreted and has a define error code
/// if its not null is Genesis.Protocols.Request.Const.ConfigExErrors
/// </summary>
public Int32? ConfigExErrorCode;
/// <summary>
/// Error is interpreted and has a define error code
/// if its not null is Genesis.Protocols.Request.Const.HighLevelError
/// </summary>
public Int32? HighLevelErrorCode;
/// <summary>
/// Ctor with only message
/// </summary>
/// <param name="message">error message</param>
public RequestProtocolException(String message) : base(message)
{
}
/// <summary>
/// Ctor with message and <see cref="Command"/>
/// </summary>
/// <param name="message">error message</param>
/// <param name="command"><see cref="Command"/></param>
public RequestProtocolException(String message, Object command) : base(message)
{
Command = command;
ReceivedData = null;
}
/// <summary>
/// Ctor with message, <see cref="Command"/> and <see cref="ReceivedData"/>
/// </summary>
/// <param name="message">error message</param>
/// <param name="command"><see cref="Command"/></param>
/// <param name="receivedData"><see cref="ReceivedData"/> </param>
public RequestProtocolException(String message, Object command, Object receivedData) : base(message)
{
Command = command;
ReceivedData = receivedData;
}
}
}
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request")]
[assembly: AssemblyDescription("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request")]
[assembly: Guid("91026D9C-212F-4B59-9237-7E34424A4DBD")]
@@ -0,0 +1,648 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Xylem.Common.Utils.Logging;
using NLog;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
{
/// <summary>
/// a bidirectional protocol support read and write genesis meter registers
/// needed for
/// </summary>
public class RequestProtocol : BaseProtocol
{
//All read and write data sets (payload size) are organized in 4 byte chunks
//Format of RequestProtocol read data
//Request CMD | Register LSB | Register MSB | 0x00 | 0x00 | 0x00 | 0x00
//Response CMD | ErrCode Reason | ErrCode Base | DATA0 | DATA1 | DATA2 | DATA3
//Format of RequestProtocol write data
//Request CMD | Register LSB | Register MSB | DATA0 | DATA1 | DATA2 | DATA3
//Response CMD | ErrCode Reason | ErrCode Base | UNDEF0 | UNDEF1 | UNDEF2 | UNDEF3
//Format of RequestProtocol multiple read data
//Request CMD | Register LSB | Register MSB | RequestChunks LSB | RequestChunks MSB
//Response CMD | ErrCode Reason | ErrCode Base | ErrPosition LSB | ErrPosition MSB |
//DATA0 | DATA1 | DATA2 | DATA3.... (repeat for number of chunks)
//Format of RequestProtocol multiple read data
//Request CMD | Register LSB | Register MSB | WriteChunks LSB | WriteChunks MSB |
//DATA0 | DATA1 | DATA2 | DATA3.... (repeat for number of chunks)
//Response CMD | ErrCode Reason | ErrCode Base | ErrPosition LSB | ErrPosition MSB
//Indexes of the request protocol read/write data
//private const Int32 RequestCommandIndex = 0;
//private const Int32 RequestRegisterIndex = 1;
//private const Int32 RequestDataIndex = 3;
//Indexes for response protocol read/write data
private const Int32 ResponseCommandIndex = 0;
private const Int32 ResponseErrorCodeIndex = 1;
private const Int32 ResponseDataIndex = 3;
//Indexes of the request protocol multiple read/write data
//private const Int32 RequestChunksIndex = 3;
//private const Int32 RequestMultiDataIndex = 5;
//Indexes of the response protocol multiple read/write data
private const Int32 ResponseMultiErrorPosition = 3;
private const Int32 ResponseMultiDataIndex = 5;
//Indexes of error code
private const Int32 ErrorCodeReasonIndex = 0;
private const Int32 ErrorCodeBaseIndex = 1;
//Multiple response header size is 1 byte command 2 bytes error code and 2 bytes error position
private const Int32 ResponseMultiHeaderSize = 5;
//fill byte
private const Byte FillByte = 0x00;
private readonly ILogger _logger;
/// <summary>
/// FIFO of records to be send next
/// </summary>
private readonly ConcurrentQueue<RequestRecord> _recordsSendFifo = new ConcurrentQueue<RequestRecord>();
/// <summary>
/// This is the actual record in the send loop
/// </summary>
private RequestRecord _recordInProcess;
/// <summary>
/// Last communication time for session refresh
/// </summary>
public DateTimeOffset? LastCommTime;
/// <inheritdoc />
public override event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
/// <inheritdoc />
public override event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <summary>
/// Occurs when a meter Response for write password is good
/// </summary>
public event EventHandler OnAuthorizationGrant;
/// <summary>
/// Event after a register entries changed
/// </summary>
public event EventHandler<RegisterUpdatedEventArgs> OnMeterRegisterUpdated;
/// <summary>
/// User adjustable additional retry timeout. This is 0 ms for standard operation.
/// </summary>
public Int32 AdditionalRetryTimeoutMs;
private Int32 _timeOutMs;
private readonly String _ident;
/// <inheritdoc />
public RequestProtocol(String ident) : base(ident)
{
_ident = ident;
_logger = NLogHelper.CreateOrGetMultiLogger(ident, "", "RequestProtocol", "MeterBase", "MeterBase");
}
/// <summary>
/// Process all records needed to be sent, this routine has to be called
/// to kick-off the communication of all RequestRecords saved to the send
/// FIFO <see cref="AddRecordToSendFifo"/>
/// </summary>
/// <remarks date="2018-Dec-07" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
/// - Logging of raw RequestProtocol at time of sending,
/// - Logging of retries.
/// </remarks>
/// <remarks date="2018-Dec-12" author="T.Wiedebusch">
/// - Added timeout from transmit protocol.
/// </remarks>
/// <remarks date="2019-Jan-02" author="Drabesch">
/// - Added reorder FIFO to bring login at first position
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-06" author="T.Wiedebusch">
/// - Retry counter reset if authorization required and this record will be put back into FIFO,
/// - Retry delay corrected for all errors.
/// </remarks>
/// <remarks date="2019-Jun-12" author="T.Wiedebusch">
/// - Dynamic retry delay: ResponseTimeoutMs * Retry counter.
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Skip retries on specific error mask to speed up communication on functional errors
/// or informational feed backs (e.g FW not installed 0x0004)
/// <see cref="RequestRecord.ResponseErrorCode"/>
/// </remarks>
/// <remarks date="2019-Aug-29" author="T.Wiedebusch">
/// - User adjustable additional retry timeout.
/// </remarks>
/// <remarks date="2020-Jan-17/18" author="T.Wiedebusch">
/// - Response timeout message output,
/// - Initialize acknowledge code before communication to NoResponse.
/// </remarks>
/// <remarks date="2020-Jan-22" author="T.Wiedebusch">
/// - Response timeout deviated from system time.
/// </remarks>
/// <remarks date="2021-Feb-02" author="T.Wiedebusch">
/// - Avoid enqueue of _recordInProcess if retry counter is 0.
/// </remarks>
public RequestAcknowledgeState ProcessRecordList()
{
_logger.Trace(" 1 ProcessRecordList");
//The inter record send delay has to be hold before trying to communicate again
const Int32 interRecordSendDelayMs = CommunicationConfig.InterRecordSendDelayMs;
//check if FIFO is empty and get the actual record to process out of it
while (_recordsSendFifo.TryDequeue(out var internalRecord))
{
_logger.Trace(" 1 TryDequeue ProcessRecordList");
_recordInProcess = internalRecord;
do
{
_logger.Trace(" 1 TryDequeue DO ProcessRecordList");
//log retries
if (_recordInProcess.RetryCtr > 0)
{
_logger.Debug($"{_ident} Retry({_recordInProcess.RetryCtr})");
}
//log request protocol content
_logger.Debug(_recordInProcess.HideDataInLog
? $"{_ident} SentData(*********************************)"
: $"{_ident} SentData({BitConverter.ToString(_recordInProcess.RequestProtocolData.ToArray())})");
//remind time for keep-session-active test to deny automatic logout of meter
LastCommTime = DateTimeOffset.UtcNow;
//set timeout for one communication trial adding the transmit protocol specific timeout and
//increase the timeout with each retry
_timeOutMs = _recordInProcess.ResponseTimeoutMs + AdditionalRetryTimeoutMs +
CommunicationConfig.ResponseTimeoutMs * (_recordInProcess.RetryCtr + 1);
//start initial communication or retry
OnRecordReadyToSend?.Invoke(this, new ListBytePortDataEventArgs(_recordInProcess.EncodedRequestData));
var requestTimeUtc = DateTimeOffset.UtcNow;
Int32 responseTimeMs;
//wait for communication acknowledge or until timeout, this also handles the inter record send delay
do
{
//minimum delay is the inter record send delay
Thread.Sleep(interRecordSendDelayMs);
var timeoutTimeUtc = DateTimeOffset.UtcNow;
var timeSpan = timeoutTimeUtc - requestTimeUtc;
//avoid total milliseconds below zero at time overflow
if (timeSpan.TotalMilliseconds < 0)
{
requestTimeUtc = DateTimeOffset.UtcNow;
}
responseTimeMs = (Int32)timeSpan.TotalMilliseconds;
} while (_recordInProcess.Acknowledge != RequestAcknowledgeState.Ok &&
_recordInProcess.SkipRetryErrorCode != _recordInProcess.ResponseErrorCode &&
_timeOutMs > responseTimeMs);
//if timeout value reaches zero, the response hasn't been received or the delay until
//next communication needed to be hold
if (_timeOutMs <= responseTimeMs)
{
_logger.Error(_recordInProcess.Acknowledge == RequestAcknowledgeState.NoResponse
? $"{_ident} Response timeout({responseTimeMs}ms)"
: $"{_ident} Communication delay({responseTimeMs}ms)");
}
//skip loop to handle re-authorization
if (_recordInProcess.Acknowledge != RequestAcknowledgeState.AuthorizationRequired) continue;
//reset retry counter for this record, it will be dispatched after re-authorization
_recordInProcess.RetryCtr = 0;
if (CommunicationConfig.RequestRetries > 0)
{
//add the recordInProcess to the top of the FIFO
_recordsSendFifo.Enqueue(_recordInProcess);
}
//call login
return _recordInProcess.Acknowledge;
} while (_recordInProcess.Acknowledge != RequestAcknowledgeState.Ok &&
_recordInProcess.RetryCtr++ < CommunicationConfig.RequestRetries &&
_recordInProcess.SkipRetryErrorCode != _recordInProcess.ResponseErrorCode);
}
return _recordInProcess.Acknowledge;
}
/// <summary>
/// Assemble record with transmit protocol and put it to record-send-FIFO,
/// backup the ready-to-send, which is the dataEncodedWithTransmitProtocol,
/// to the RequestRecord object for sending including the retry capability.
/// </summary>
/// <param name="payload">Data package with all details like CRC, etc</param>
/// <param name="cmd">Command identifier <see cref="Commands" /></param>
/// <param name="register"><see cref="RegisterDefinition" /> which is intend</param>
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
/// <param name="skipRetryErrorCode">error mask to skip retries</param>
/// <returns>the assembled record for the send FIFO</returns>
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
/// - Logging of raw data (RequestProtocol) moved to ProcessRecordList.
/// </remarks>
/// <remarks date="2018-Dec-12" author="T.Wiedebusch">
/// - Added timeout from transmit protocol.
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Skip retries on specific error mask to speed up communication on functional errors
/// or informational feed backs (e.g FW not installed 0x0004)
/// <see cref="RequestRecord.ResponseErrorCode"/>
/// </remarks>
/// <remarks date="2020-Jan-18" author="T.Wiedebusch">
/// - Initial skip retry error code set to 0x0004 (e.g FW not installed 0x0004).
/// </remarks>
private RequestRecord AddRecordToSendFifo(Byte cmd, Byte[] payload, RegisterDefinition register = null,
Boolean hideDataInLog = false, UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
{
var requestProtocolData = new List<Byte> { cmd };
requestProtocolData.AddRange(payload);
//encode request protocol data with transmit protocol
var transmit = GetTransmitProtocol();
var dataEncodedWithTransmitProtocol = transmit.DecodeDataForPhysicalLayer(cmd, payload);
//remind port specific response timeout
var transmitPortSettings = transmit.GetTransmitPortSettings();
var recordForSendFifo = new RequestRecord(cmd, requestProtocolData, dataEncodedWithTransmitProtocol,
transmitPortSettings.ResponseTimeoutMs, register, hideDataInLog, skipRetryErrorCode);
_recordsSendFifo.Enqueue(recordForSendFifo);
return recordForSendFifo;
}
/// <summary>
/// Called after successful response.
/// Decode and check record, handle Errors and dispatch result.
/// Invokes <see cref="OnRecordIsDecoded" />if somebody is listening
/// </summary>
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
/// - First part reworked to extract the response protocol information
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Error code extraction changed for <see cref="RequestRecord.SkipRetryErrorCode"/>
/// </remarks>
/// <remarks date="2019-Dec-12/16" author="T.Wiedebusch">
/// - Wakeup-message will avoid further timeout (during FW update this is in the range
/// of 15000ms).
/// - Allow one additional retry on decoding error, which is often the wakeup-message.
/// </remarks>
/// <remarks date="2019-Jan-17" author="T.Wiedebusch">
/// - Send time reminder for timeout time calculation as output in log-file,
/// - OnRecordIsDecoded?.Invoke moved before return to assure that the Acknowledge status is set.
/// </remarks>
/// <remarks date="2019-Jan-22" author="T.Wiedebusch">
/// - Avoid activation of wakeup retry if record is meanwhile acknowledged.
/// </remarks>
public override void DecodeRecord(IPortDataEventArgs data)
{
//the request response record covers the entire request protocol
var responseRecord = GetTransmitProtocol().DecodeDataForLogicLayer((List<Byte>)data.GetData());
//if the current record is invalid or on protocol decoding failure or wakeup message
if (null == _recordInProcess || responseRecord.Count == 0)
{
//avoid wakeup retry on acknowledged record
if (null != _recordInProcess && RequestAcknowledgeState.Ok != _recordInProcess.Acknowledge)
{
_recordInProcess.Acknowledge = RequestAcknowledgeState.DecodingError;
//allow one more retry
if (_recordInProcess.RetryCtr == 0 && _recordInProcess.WakeupMessageRetryCtr == 0)
{
_logger.Debug($"{_ident} WakeupMessageRetry(1)");
_recordInProcess.WakeupMessageRetryCtr = 1;
_recordInProcess.RetryCtr--;
}
}
//reset timeout to a normal value if timeout is extremely high but response received
//even if it is a wakeup-message and cannot be decoded
if (_timeOutMs > CommunicationConfig.BusyTimeoutMs)
_timeOutMs = CommunicationConfig.BusyTimeoutMs;
return;
}
//log request protocol content
_logger.Debug(_recordInProcess.HideDataInLog
? $"{_ident} DecodedRecord(*********************************)"
: $"{_ident} DecodedRecord({BitConverter.ToString(responseRecord.ToArray())})");
//extract information command
var replyCmd = responseRecord[ResponseCommandIndex];
//extract position of error for multiple access
if (replyCmd == Commands.MultipleReadDataReply || replyCmd == Commands.MultipleWriteDataReply)
{
_recordInProcess.ResponseErrorPosition =
(UInt16)((responseRecord[ResponseMultiErrorPosition] & 0x00FF) |
(UInt16)((responseRecord[ResponseMultiErrorPosition + 1] << 8) & 0xFF00));
}
//extract the error codes from response record to _record in process
_recordInProcess.ResponseErrorBase = responseRecord[ResponseErrorCodeIndex + ErrorCodeBaseIndex];
_recordInProcess.ResponseErrorReason = responseRecord[ResponseErrorCodeIndex + ErrorCodeReasonIndex];
//combine error base and error reason
_recordInProcess.ResponseErrorCode = (UInt16)(_recordInProcess.ResponseErrorReason |
(_recordInProcess.ResponseErrorBase << 8));
_recordInProcess.ResponsePayload = new List<Byte>();
switch (replyCmd)
{
case Commands.MultipleReadDataReply:
_recordInProcess.ResponsePayload.AddRange(
responseRecord.GetRange(ResponseMultiDataIndex, responseRecord.Count - ResponseMultiHeaderSize));
break;
case Commands.ReadDataReply:
_recordInProcess.ResponsePayload.AddRange(responseRecord.GetRange(ResponseDataIndex, RegisterDefinition.ChunkSize));
break;
}
//Deny access if reply does not match
if (_recordInProcess.ResponseCommand != replyCmd)
{
_logger.Fatal($"{_ident} Wrong command received({replyCmd:X2})," +
$" expected command({_recordInProcess.ResponseCommand:X2})");
_recordInProcess.Acknowledge = RequestAcknowledgeState.CommandError;
OnRecordIsDecoded?.Invoke(this, new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
return;
}
//examine error code, if base (AppId) is not 0 the reason 0 may be an error!!!!
if (_recordInProcess.ResponseErrorBase != 0 || _recordInProcess.ResponseErrorReason != 0)
{
_logger.Warn($"{_ident} Error code received(0x{_recordInProcess.ResponseErrorBase:X2}" +
$"{_recordInProcess.ResponseErrorReason:X2})");
_recordInProcess.Acknowledge = RequestAcknowledgeState.MeterError;
CheckErrorCode();
OnRecordIsDecoded?.Invoke(this, new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
return;
}
//register password acknowledge
if (_recordInProcess.Register.GetIdent() == Register.Configexchange.Password)
{
OnAuthorizationGrant?.Invoke(null, null);
}
//dispatch data
if (_recordInProcess.ResponsePayload != null)
{
OnMeterRegisterUpdated?.Invoke(this, new RegisterUpdatedEventArgs(_recordInProcess.Register,
_recordInProcess.ResponsePayload.ToArray()));
}
_recordInProcess.Acknowledge = RequestAcknowledgeState.Ok;
OnRecordIsDecoded?.Invoke(this, new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
}
/// <summary>
/// Method to send basic Commands to Meter. Creates an integer of 4 bytes for the payload.
/// Supported Commands are: <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
/// Calculate CRCs and Command length and check if command is valid.
/// Use <see cref="AddRecordToSendFifo" /> to push data to Port/Meter.
/// </summary>
/// <param name="command">
/// Supported <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
/// </param>
/// <param name="meterRegister">Register to Read or Write, null for <see cref="Commands.QueryCaps" /></param>
/// <param name="payload">
/// Data to push into the <see cref="RegisterDefinition" />.
/// must be null on Read Commands ( <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />)
/// and not null on WriteData (<see cref="Commands.WriteData" /> and <see cref="Commands.MultipleWriteData" />)
/// </param>
/// <param name="expectedLength"></param>
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
/// <param name="skipRetryErrorCode">mask to skip reties on error</param>
/// <returns>an new command just send to port/meter </returns>
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
/// - Initial
/// </remarks>
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
/// - Reworked to zero pad payload with chunks of 4 bytes, the caller needn't take care of the size
/// </remarks>
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
/// - Corrected payload content in request protocol
/// </remarks>
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
/// - Hide data in logging for e.g. passwords
/// </remarks>
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
/// - Skip retries on specific error mask to speed up communication on functional errors
/// or informational feed backs (e.g FW not installed 0x0004)
/// <see cref="RequestRecord.ResponseErrorCode"/>
/// </remarks>
/// <remarks date="2019-Aug-28" author="R.Drabesch">
/// - Multiple read for string split to simple reads.
/// </remarks>
/// <remarks date="2019-Jan-18" author="T.Wiedebusch">
/// - Default <see cref="CommunicationConfig.SkipRetryErrorCode"/> set.
/// </remarks>
public RequestRecord CommandToMeter(Byte command, RegisterDefinition meterRegister = null,
Byte[] payload = null, Int32? expectedLength = null, Boolean hideDataInLog = false,
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
{
if (command == Commands.MultipleReadData && meterRegister != null && meterRegister.DataType == typeof(String))
{
var completeResponse = new List<Byte>();
while (true)
{
var cRecord = CommandToMeter(Commands.ReadData, meterRegister, payload, expectedLength,
hideDataInLog, skipRetryErrorCode);
var rest = ProcessRecordList();
if (rest == RequestAcknowledgeState.Ok && cRecord != null && cRecord.ResponsePayload != null)
{
completeResponse.AddRange(cRecord.ResponsePayload);
if (cRecord.ResponsePayload.Contains(0))
{
cRecord.ResponsePayload = completeResponse;
OnMeterRegisterUpdated?.Invoke(this, new RegisterUpdatedEventArgs(meterRegister, completeResponse.ToArray()));
if (expectedLength.HasValue && completeResponse.Count < expectedLength.Value)
{
_logger.Debug($"Too Short. Payload contains zero");
}
return cRecord;
}
}
else
{
if (expectedLength.HasValue && completeResponse.Any() && completeResponse.Count < expectedLength.Value)
{
if (rest != RequestAcknowledgeState.Ok)
{
_logger.Debug($"Too Short.Request Acknowledge State is {rest}");
}
else if (cRecord == null)
{
_logger.Debug($"Too Short.Record is empty or null");
}
else if (cRecord.ResponsePayload == null)
{
_logger.Debug($"Too Short.ResponsePayload is empty or null");
}
else
{
_logger.Debug($"Too Short.Unkown reason");
}
}
return cRecord;
}
}
}
//the minimum payload is one 4 byte chunk
var chunkSize = RegisterDefinition.ChunkSize;
if (payload == null)
{
payload = new Byte[chunkSize];
for (var i = 0; i < chunkSize; i++)
{
payload[i] = FillByte;
}
}
//real payload based on 4 byte chunks
var requestPayload = new List<Byte>();
requestPayload.AddRange(payload);
//fill-byte padding
if (payload.Length % chunkSize != 0)
{
for (var i = 0; i < chunkSize - payload.Length % chunkSize; i++)
{
requestPayload.Add(FillByte);
}
}
//request protocol excluding the command
var requestProtocol = new List<Byte>();
if (meterRegister != null)
{
requestProtocol.Add(meterRegister.RegisterAddress[1]);
requestProtocol.Add(meterRegister.RegisterAddress[0]);
if (command == Commands.MultipleWriteData)
{
//set counter to inform meter writes are expected
//the payload size is already padded to 4 byte chunks
var numberOfChunks = (UInt16)(requestPayload.Count / chunkSize);
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
}
if (command == Commands.MultipleReadData)
{
//TODO THW create expected size out of meterRegister.DataType
if (!expectedLength.HasValue)
{
if ((meterRegister.DataType == typeof(UInt64) || meterRegister.DataType == typeof(Int64)))
{
expectedLength = 8;
}
else
{
expectedLength = 24;
}
}
var numberOfChunks = (UInt16)(expectedLength.Value / chunkSize);
if (expectedLength % chunkSize != 0) numberOfChunks++;
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
}
//add always the payload to the request protocol
requestProtocol.AddRange(requestPayload);
}
return AddRecordToSendFifo(command, requestProtocol.ToArray(), meterRegister,
hideDataInLog, skipRetryErrorCode);
}
/// <summary>
/// Analyzes the error code
/// </summary>
public void CheckErrorCode()
{
if (_recordInProcess.ResponseErrorBase == (Byte)ConfigExErrors.Base)
{
if (_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.AuthenticationFail ||
_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.AccessDenied ||
_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.LockedOut)
{
_recordInProcess.Acknowledge = RequestAcknowledgeState.AuthorizationRequired;
}
}
}
/// <summary>
/// Reorders the record list so that first entry is the defined topRegister
/// </summary>
/// <param name="topRegister"></param>
public void ReorderRecordList(String topRegister)
{
_recordsSendFifo.Enqueue(_recordInProcess);
//reorder FIFO to bring login at first position
for (var i = 0; i < _recordsSendFifo.Count; i++)
{
RequestRecord tmpRecord;
_recordsSendFifo.TryPeek(out tmpRecord);
if (tmpRecord == null || tmpRecord.Register.GetIdent() == topRegister)
{
break;
}
_recordsSendFifo.TryDequeue(out tmpRecord);
_recordsSendFifo.Enqueue(tmpRecord);
}
}
/// <summary>
/// Checks the active register to access
/// </summary>
/// <param name="registerName"></param>
/// <returns></returns>
public Boolean ContainsRegisterIdent(String registerName)
{
return _recordsSendFifo.Any(a => a.Register.GetIdent().ToLower() == registerName.ToLower());
}
}
}
@@ -0,0 +1,275 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D32ED790-7E1E-435B-8619-9F906C992066}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol</RootNamespace>
<AssemblyName>Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>6</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<LangVersion>7.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'COM|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\COM\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'XP32bit|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\XP32bit\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PerformTest|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PerformTest\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<LangVersion>7.0</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\x86\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<LangVersion>6</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '_MANUAL_CONTROL|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\_MANUAL_CONTROL\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>bin\Debug\Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Request.xml</DocumentationFile>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\..\..\..\.shared\SharedAssemblyInfo.cs">
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Consts\Commands.cs" />
<Compile Include="Consts\ConfigExErrors.cs" />
<Compile Include="Consts\HighLevelErrors.cs" />
<Compile Include="Consts\LowLevelErrors.cs" />
<Compile Include="Consts\RequestAcknowledgeState.cs" />
<Compile Include="Exceptions\RequestProtocolException.cs" />
<Compile Include="RequestRecord.cs" />
<Compile Include="EventArguments\AuthorizationRequiredEventArgs.cs" />
<Compile Include="RequestProtocol.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\..\CommonCore\CommonCore.csproj">
<Project>{1b02c79e-0b19-43e2-8f6b-71ef0c786c97}</Project>
<Name>CommonCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\..\Utils\Logging\Logging.csproj">
<Project>{4B88B0D3-E791-4774-9521-EABEACDC420E}</Project>
<Name>Logging</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Ports\PortCore\PortCore.csproj">
<Project>{2E139F63-B6FE-4EA9-A098-85364FE9B26C}</Project>
<Name>PortCore</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\..\Interfaces\Protocols\TransmitProtocol\TransmitProtocol.csproj">
<Project>{63E0AC5E-B7C8-4363-A2FE-BE489E648FF0}</Project>
<Name>TransmitProtocol</Name>
</ProjectReference>
<ProjectReference Include="..\..\DataPackages\DataPackages.csproj">
<Project>{0E524D43-7B66-45CD-AA74-41D51AFC3D99}</Project>
<Name>DataPackages</Name>
</ProjectReference>
<ProjectReference Include="..\..\GenesisConfig\GenesisConfig.csproj">
<Project>{F013A56B-7BE6-4852-9CD8-B971CD1ADD0C}</Project>
<Name>GenesisConfig</Name>
</ProjectReference>
<ProjectReference Include="..\..\Registers\Registers.csproj">
<Project>{F4AAF7E6-7333-4284-B735-E32DEB076C64}</Project>
<Name>Registers</Name>
</ProjectReference>
<ProjectReference Include="..\ProtocolCore\ProtocolCore.csproj">
<Project>{8BDD2789-96D4-43EA-AAD7-0F49D0347998}</Project>
<Name>ProtocolCore</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\NLog.4.6.7\lib\net40-client\NLog.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.Registers;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.RequestProtocol
{
/// <summary>
/// Holds request commands with detail parameters to see processing state
/// </summary>
public class RequestRecord
{
/// <summary>
/// Indicates the base command <see cref="Commands" />
/// </summary>
public readonly Byte Command;
/// <summary>
/// Indicates the base command <see cref="Commands" />
/// </summary>
public readonly Byte ResponseCommand;
/// <summary>
/// the register the command refers to.
/// needed to set Register dictionary to link response with dictionary key
/// </summary>
public readonly RegisterDefinition Register;
/// <summary>
/// Command acknowledged
/// </summary>
public RequestAcknowledgeState Acknowledge = RequestAcknowledgeState.NoResponse;
/// <summary>
/// Retry counter
/// </summary>
public Int32 RetryCtr = 0;
/// <summary>
/// Wakeup-message retry counter
/// </summary>
public Int32 WakeupMessageRetryCtr = 0;
/// <summary>
/// indicates error base on <see cref="RequestRecord"/>
/// </summary>
public Byte ResponseErrorBase = 0xFF;
/// <summary>
/// indicates error reason on <see cref="RequestRecord"/>
/// </summary>
public Byte ResponseErrorReason = 0xFF;
/// <summary>
/// Combined error code of base and reason
/// </summary>
public UInt16 ResponseErrorCode = 0xFFFF;
/// <summary>
/// Chunk position of error on multiple read/write access
/// </summary>
public UInt16 ResponseErrorPosition = 0;
/// <summary>
/// Data containing the request protocol
/// </summary>
public List<Byte> RequestProtocolData;
/// <summary>
/// Encoded with transmit protocol, ready to stream to port as is
/// </summary>
public List<Byte> EncodedRequestData { get; internal set; }
/// <summary>
/// Extracted payload of response
/// </summary>
public List<Byte> ResponsePayload;
/// <summary>
/// Extracted payload of response
/// </summary>
public readonly Int32 ResponseTimeoutMs;
/// <summary>
/// avoid logging for e.g. password
/// </summary>
public Boolean HideDataInLog;
/// <summary>
/// error mask to skip retries for functional errors <see cref="ResponseErrorCode"/>
/// </summary>
public UInt16 SkipRetryErrorCode;
/// <summary>
/// Ctor for an base command <see cref="Commands" />
/// </summary>
/// <param name="command"><see cref="Commands" /> as an byte</param>
/// <param name="requestProtocolData">Request protocol data for logging</param>
/// <param name="encodedRequestData"> Ready to send data encoded with transmit protocol retries</param>
/// <param name="responseTimeoutMs">Timeout for response</param>
/// <param name="register"><see cref="RegisterDefinition"/> to do command with</param>
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
/// <param name="skipRetryErrorCode">error mask to skip retries <see cref="ResponseErrorCode"/></param>
public RequestRecord(Byte command, List<Byte> requestProtocolData = null, List<Byte> encodedRequestData = null,
Int32 responseTimeoutMs = 100, RegisterDefinition register = null, Boolean hideDataInLog = false,
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
{
Command = command;
ResponseCommand = (Byte)(command + 1);
Register = register;
RequestProtocolData = requestProtocolData;
EncodedRequestData = encodedRequestData;
ResponseTimeoutMs = responseTimeoutMs;
HideDataInLog = hideDataInLog;
SkipRetryErrorCode = skipRetryErrorCode;
}
}
}
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="4.6.7" targetFramework="net40" />
</packages>
@@ -0,0 +1,7 @@
using System.Runtime.InteropServices;
using System.Reflection;
[assembly: AssemblyTitle("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Streaming")]
[assembly: AssemblyDescription("Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.Streaming")]
[assembly: Guid("CCA84683-47FA-458F-B11C-AE9864DFC580")]
@@ -0,0 +1,446 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.MeasurementRecords;
using Xylem.Common.Utils.Crc16Ccitt;
// ReSharper disable UnusedMember.Local
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol
{
/// <summary>
/// Data fields and definitions for GENESIS streaming protocol
/// </summary>
public class StreamingDecoder
{
private const Double MilliLitersToQmFactor = 1.0E-6;
private const Double DefaultVolumeScaleRawPerMl = 1024.0;
private const Double DefaultVolumeFactorRawToQm = MilliLitersToQmFactor /
DefaultVolumeScaleRawPerMl;
private const Double MaxAccuVolumeRaw = 0x100000000; //2^32
private const Double DefaultAccuDutOverflowVolumeCm = MaxAccuVolumeRaw *
DefaultVolumeFactorRawToQm;
private const Double CpuTimeToSecondsFactor = 1.0 / 0x10000;
private const Double CpuTimeOverflowS = 0x100000000 * CpuTimeToSecondsFactor;
private const Double DisplayMlSetupDutOverflowVolumeCm = 1000.0; //overflow of LCD if set to ml
private const Double TofToSecondsFactor38Bit = 1.0 / 0x4000000000; // 2^38
private const Double AmplitudeToVoltFactor = 1.0 / 0x400000 / 1000.0; // 2^22
private const Double PulseWidthToRelFactor = 1.0 / 0x100; // 2^8
private const UInt16 IsValidProtG = 1;
private const UInt16 IsValidProtH = 0;
private CalibrationRecord _dataCalibRec;
/// <summary>
/// Default data for
/// </summary>
private readonly FlowTestRecord _dataDefault = new FlowTestRecord
{
VolumeCm = 0.0,
OverflowVolumeCm = DisplayMlSetupDutOverflowVolumeCm,
TimeS = 0.0,
OverflowTimeS = CpuTimeOverflowS,
Crc = 0xFFFF,
IsValid = false
};
private readonly CalibrationRecord _rawDataDefault = new CalibrationRecord
{
Channel = 0,
Validation = 0xFFFF,
TotalTimeOfFlightS = 0.0,
DeltaTimeOfFlightS = 0.0,
VolumeScaleRawPerMl = DefaultVolumeScaleRawPerMl,
VolumeFactorRawToQm = DefaultVolumeFactorRawToQm,
DeltaVolumeRaw = 0.0,
DeltaVolumeQm = 0.0,
AccuVolumeRaw = 0.0,
VolumeCm = 0.0,
OverflowVolumeCm = DefaultAccuDutOverflowVolumeCm,
SampleIntervalS = 0.0,
AmplitudeUpV = 0.0,
AmplitudeDownV = 0.0,
PulseWidthRatioUp = 0.0,
PulseWidthRatioDown = 0.0,
TemperatureRaw = 20.0,
TemperaturePowFactor = 1.0,
TemperatureDegC = 20.0,
TimeS = 0.0,
OverflowTimeS = CpuTimeOverflowS,
Crc = 0xFFFF,
IsValid = false
};
private readonly FlowTestRecord _dataFlowTestRec;
private String _rawMsgForCrc;
/// <summary>
/// Calibration data
/// </summary>
public CalibrationRecord DataCalib = new CalibrationRecord();
private readonly Boolean _ignoreCorruptedData;
/// <summary>
/// Constructor initializes all decoded members with default values
/// </summary>
public StreamingDecoder(Boolean ignoreCorruptedData = true)
{
_dataFlowTestRec = _dataDefault;
_dataCalibRec = _rawDataDefault;
_ignoreCorruptedData = ignoreCorruptedData;
}
/// <summary>
/// Flow test data
/// </summary>
public FlowTestRecord DataFlowTest { get; private set; }
/// <summary>
/// Decoding the raw message
/// </summary>
/// <param name="rawMsg">message received as one line delimited with LF</param>
/// <returns>true if decoding was successful and data has been validated</returns>
public Boolean DecodeMsg(String rawMsg)
{
var isSuccessfulDecoded = false;
try
{
// save the raw message for CRC calculation before separation to fields
_rawMsgForCrc = rawMsg;
// extract message and split it to fields
rawMsg = rawMsg.Replace('\n', ' ');
var field = rawMsg.Split(' ');
switch (field[0])
{
case "@f":
isSuccessfulDecoded = DecodeProtocolF(field);
if (isSuccessfulDecoded || !_ignoreCorruptedData)
DataFlowTest = _dataFlowTestRec;
break;
case "@g":
{
isSuccessfulDecoded = DecodeProtocolG(ref _dataCalibRec, field);
if (isSuccessfulDecoded || !_ignoreCorruptedData)
DataCalib = _dataCalibRec;
break;
}
case "@h":
{
isSuccessfulDecoded = DecodeProtocolH(ref _dataCalibRec, field);
if (isSuccessfulDecoded || !_ignoreCorruptedData)
DataCalib = _dataCalibRec;
break;
}
}
}
catch (Exception ex)
{
// ignored
}
return isSuccessfulDecoded;
}
/// <summary>
/// Extracting message from string fields for protocol 'f'
/// </summary>
/// <param name="field">Separated fields containing the measurement as string</param>
/// <returns></returns>
private Boolean DecodeProtocolF(IList<String> field)
{
_dataFlowTestRec.DecodedTime = DateTimeOffset.UtcNow;
_dataFlowTestRec.IsValid = true;
//extract bytes of raw message for CRC calculation each character removing CRC (4 char)
var byteArraySize = _rawMsgForCrc.Length - 4;
var byteArray = new Byte[byteArraySize];
for (var i = 0; i < byteArraySize; i++)
byteArray[i] = (Byte)_rawMsgForCrc[i];
_rawMsgForCrc = null;
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
//extract received CRC
_dataFlowTestRec.Crc = ushort.Parse(field[(Int32)ProtFsubString.Crc],
NumberStyles.HexNumber);
//compare received with calculated CRC
if (calculatedCrc != _dataFlowTestRec.Crc)
{
_dataFlowTestRec.IsValid = false;
return false;
}
_dataFlowTestRec.IsValid = true;
//build result values, the display volume can be positive or negative!
_dataFlowTestRec.VolumeCm = int.Parse(field[(Int32)ProtFsubString.DisplayVolume],
NumberStyles.AllowHexSpecifier) * MilliLitersToQmFactor;
_dataFlowTestRec.TimeS = uint.Parse(field[(Int32)ProtFsubString.CpuTime],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
return _dataFlowTestRec.IsValid;
}
/// <summary>
/// Extracting message from string fields to individual raw channel for protocol 'g'
/// </summary>
/// <param name="dataProtGRec">Reference to result structure for raw data for one channel</param>
/// <param name="field">Separated fields containing the measurement as string</param>
/// <returns>true if protocol is valid</returns>
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
/// </remarks>
private Boolean DecodeProtocolG(ref CalibrationRecord dataProtGRec, IList<String> field)
{
dataProtGRec.DecodedTime = DateTimeOffset.UtcNow;
dataProtGRec.IsValid = false;
//extract bytes of raw message for CRC calculation each character removing CRC (4 char)
var byteArraySize = _rawMsgForCrc.Length - 4;
var byteArray = new Byte[byteArraySize];
for (var i = 0; i < byteArraySize; i++)
byteArray[i] = (Byte)_rawMsgForCrc[i];
_rawMsgForCrc = null;
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
//extract received CRC
dataProtGRec.Crc = ushort.Parse(field[(Int32)ProtGsubString.Crc],
NumberStyles.HexNumber);
dataProtGRec.Channel = ushort.Parse(field[(Int32)ProtGsubString.ChanNo],
NumberStyles.HexNumber);
dataProtGRec.Validation = ushort.Parse(field[(Int32)ProtGsubString.Validation],
NumberStyles.HexNumber);
//compare received with calculated CRC
if (calculatedCrc != dataProtGRec.Crc || IsValidProtG != dataProtGRec.Validation)
return false;
//decode rest of message only if CRC and mode are valid
dataProtGRec.IsValid = true;
// Delta time of flight
dataProtGRec.DeltaTimeOfFlightS = int.Parse(field[(Int32)ProtGsubString.Dtof],
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
// Delta raw volume between last sample
dataProtGRec.DeltaVolumeRaw = uint.Parse(field[(Int32)ProtGsubString.RawDVolume],
NumberStyles.AllowHexSpecifier);
//volume scaling
var volumeRawScale = uint.Parse(field[(Int32)ProtGsubString.VolumeScale],
NumberStyles.AllowHexSpecifier);
dataProtGRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
dataProtGRec.VolumeFactorRawToQm = MilliLitersToQmFactor / dataProtGRec.VolumeScaleRawPerMl;
dataProtGRec.OverflowVolumeCm = MaxAccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
//Calculate volume in cubic meters out of the raw volume
dataProtGRec.DeltaVolumeQm = dataProtGRec.DeltaVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
//accumulated volume for each channel received from water meter scaled with volumeScale
//the volume can just be positive
dataProtGRec.AccuVolumeRaw = uint.Parse(field[(Int32)ProtGsubString.AccuVolume],
NumberStyles.AllowHexSpecifier);
dataProtGRec.VolumeCm = dataProtGRec.AccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
//Sample interval
dataProtGRec.SampleIntervalS = uint.Parse(field[(Int32)ProtGsubString.SampleInterval],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
//amplitude for high threshold in V
dataProtGRec.AmplitudeUpV = uint.Parse(field[(Int32)ProtGsubString.AmplitudeUp],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//amplitude for low threshold in V
dataProtGRec.AmplitudeDownV = uint.Parse(field[(Int32)ProtGsubString.AmplitudeDown],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//pulse width ratio high threshold
dataProtGRec.PulseWidthRatioUp = uint.Parse(field[(Int32)ProtGsubString.PulseWidthRatioUp],
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
//pulse width ratio low threshold
dataProtGRec.PulseWidthRatioDown = uint.Parse(field[(Int32)ProtGsubString.PulseWidthRatioDown],
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
//raw temperature
dataProtGRec.TemperatureRaw = int.Parse(field[(Int32)ProtGsubString.RawTemperature],
NumberStyles.AllowHexSpecifier);
//temperature scaling
dataProtGRec.TemperaturePowFactor = uint.Parse(field[(Int32)ProtGsubString.TemperatureScale],
NumberStyles.AllowHexSpecifier);
//Calculate temperature
dataProtGRec.TemperatureDegC = dataProtGRec.TemperatureRaw /
Math.Pow(2.0, dataProtGRec.TemperaturePowFactor);
//absolute CPU time, started at LED mode 3 activation
dataProtGRec.TimeS = uint.Parse(field[(Int32)ProtGsubString.CpuTime],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
return true;
}
/// <summary>
/// Extracting message from string fields to individual raw channel for protocol 'g'
/// </summary>
/// <param name="dataProtHRec">Reference to result structure for raw data for one channel</param>
/// <param name="field">Separated fields containing the measurement as string</param>
/// <returns>true if protocol is valid</returns>
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
/// </remarks>
private Boolean DecodeProtocolH(ref CalibrationRecord dataProtHRec, IList<String> field)
{
dataProtHRec.DecodedTime = DateTimeOffset.UtcNow;
dataProtHRec.IsValid = false;
//extract bytes of raw message for CRC calculation each character removing CRC (4 char)
var byteArraySize = _rawMsgForCrc.Length - 4;
var byteArray = new Byte[byteArraySize];
for (var i = 0; i < byteArraySize; i++)
byteArray[i] = (Byte)_rawMsgForCrc[i];
_rawMsgForCrc = null;
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
//extract received CRC
dataProtHRec.Crc = ushort.Parse(field[(Int32)ProtHsubString.Crc],
NumberStyles.HexNumber);
dataProtHRec.Channel = ushort.Parse(field[(Int32)ProtHsubString.ChanNo],
NumberStyles.HexNumber);
dataProtHRec.Validation = ushort.Parse(field[(Int32)ProtHsubString.Validation],
NumberStyles.HexNumber);
//compare received with calculated CRC
if (_ignoreCorruptedData &&
(calculatedCrc != dataProtHRec.Crc || IsValidProtH != dataProtHRec.Validation))
{
return false;
}
// decode rest of message only if CRC and mode are valid
dataProtHRec.IsValid = true;
// Total time of flight
dataProtHRec.TotalTimeOfFlightS = int.Parse(field[(Int32)ProtHsubString.Ttof],
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
//Delta time of flight
dataProtHRec.DeltaTimeOfFlightS = int.Parse(field[(Int32)ProtHsubString.Dtof],
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
dataProtHRec.RAWTotalTimeOfFlight =
int.Parse(field[(Int32) ProtHsubString.Ttof], NumberStyles.AllowHexSpecifier);
dataProtHRec.RAWDeltaTimeOfFlight = int.Parse(field[(Int32) ProtHsubString.Dtof], NumberStyles.AllowHexSpecifier);
//Delta raw volume between two samples
dataProtHRec.DeltaVolumeRaw = uint.Parse(field[(Int32)ProtHsubString.RawDVolume],
NumberStyles.AllowHexSpecifier);
//volume scaling
var volumeRawScale = uint.Parse(field[(Int32)ProtHsubString.VolumeScale],
NumberStyles.AllowHexSpecifier);
dataProtHRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
dataProtHRec.VolumeFactorRawToQm = MilliLitersToQmFactor / dataProtHRec.VolumeScaleRawPerMl;
//newdataProtHRec.OverflowVolumeCm = MaxAccuVolumeRaw * newdataProtHRec.VolumeFactorRawToQm;
//Calculate volume in cubic meters out of the raw volume
dataProtHRec.DeltaVolumeQm = dataProtHRec.DeltaVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
//accumulated volume for each channel received from water meter scaled with volumeScale
//the volume can just be positive
dataProtHRec.AccuVolumeRaw = uint.Parse(field[(Int32)ProtHsubString.AccuVolume],
NumberStyles.AllowHexSpecifier);
dataProtHRec.VolumeCm = dataProtHRec.AccuVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
//Sample interval
dataProtHRec.SampleIntervalS = uint.Parse(field[(Int32)ProtHsubString.SampleInterval],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
//amplitude for high threshold in V
dataProtHRec.AmplitudeUpV = uint.Parse(field[(Int32)ProtHsubString.AmplitudeUp],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//amplitude for low threshold in V
dataProtHRec.AmplitudeDownV = uint.Parse(field[(Int32)ProtHsubString.AmplitudeDown],
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
//raw temperature
dataProtHRec.TemperatureRaw = int.Parse(field[(Int32)ProtHsubString.RawTemperature],
NumberStyles.AllowHexSpecifier);
//temperature scaling
dataProtHRec.TemperaturePowFactor = uint.Parse(field[(Int32)ProtHsubString.TemperatureScale],
NumberStyles.AllowHexSpecifier);
//Calculate temperature
dataProtHRec.TemperatureDegC = dataProtHRec.TemperatureRaw / (
Math.Pow(2.0, dataProtHRec.TemperaturePowFactor));
//absolute CPU time, started at LED mode 3 activation
dataProtHRec.TimeS = uint.Parse(field[(Int32)ProtHsubString.CpuTime],
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
return true;
}
/// field position in protocol 'f'
private enum ProtFsubString
{
//do not remove this needed for position in record
ProtType,
DisplayVolume,
CpuTime,
Crc
}
/// field position in protocol 'g'
private enum ProtGsubString
{
//do not remove this needed for position in record
ProtType,
ChanNo,
Validation,
Dtof,
RawDVolume,
AccuVolume,
VolumeScale,
SampleInterval,
AmplitudeUp,
AmplitudeDown,
PulseWidthRatioUp,
PulseWidthRatioDown,
RawTemperature,
TemperatureScale,
CpuTime,
Crc
}
/// field position in protocol 'h'
private enum ProtHsubString
{
//do not remove this needed for position in record
ProtType,
ChanNo,
Validation,
Ttof,
Dtof,
RawDVolume,
AccuVolume,
VolumeScale,
SampleInterval,
AmplitudeUp,
AmplitudeDown,
RawTemperature,
TemperatureScale,
CpuTime,
Crc
}
}
}
@@ -0,0 +1,85 @@
using System;
using Xylem.Common.Hardware.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.DataPackages.EventArguments;
using Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.ProtocolCore;
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol
{
/// <inheritdoc />
/// <summary>
/// Layer between <see cref="T:Xylem.Common.Hardware.WaterMeter.Genesis.Protocols.StreamingProtocol.StreamingDecoder" /> CRC16CCITT handler and Events
/// block trashy telegrams
/// </summary>
public class StreamingProtocol : BaseProtocol
{
/// <summary>
/// Decode data and hold results
/// </summary>
private StreamingDecoder _streamingDecode;
private readonly Boolean _ignoreCorruptedData;
/// <inheritdoc />
public override event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
/// <inheritdoc />
public override void DecodeRecord(IPortDataEventArgs dataArgs)
{
var data = (String)dataArgs.GetData();
//Data decoding
_streamingDecode = new StreamingDecoder(_ignoreCorruptedData);
_streamingDecode.DecodeMsg(data);
//Event for new data
if (_streamingDecode.DataFlowTest != null)
{
_streamingDecode.DataFlowTest.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
_streamingDecode.DataFlowTest.ReceivedTime = dataArgs.GetReceivedTime();
if (_streamingDecode.DataFlowTest.IsValid)
{
OnRecordIsDecoded?.Invoke(this,
new FlowDataEventArgs { NewData = _streamingDecode.DataFlowTest, RawData = data });
}
else if (!_ignoreCorruptedData)
{
OnRecordIsDecoded?.Invoke(this,
new FlowDataEventArgs { NewData = _streamingDecode.DataFlowTest, RawData = data });
}
}
else if (_streamingDecode.DataCalib != null)
{
_streamingDecode.DataCalib.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
_streamingDecode.DataCalib.ReceivedTime = dataArgs.GetReceivedTime();
if (_streamingDecode.DataCalib.IsValid)
{
OnRecordIsDecoded?.Invoke(this, new CalibDataEventArgs
{
CalibChl = _streamingDecode.DataCalib,
RawData = data
});
}
else if (!_ignoreCorruptedData)
{
OnRecordIsDecoded?.Invoke(this, new CalibDataEventArgs
{
CalibChl = _streamingDecode.DataCalib,
RawData = data
});
}
}
//toDo: handle trashy telegrams
}
/// <inheritdoc />
public StreamingProtocol(String ident, Boolean ignoreCorruptedData = true) : base(ident)
{
_ignoreCorruptedData = ignoreCorruptedData;
}
}
}
@@ -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>