Compare commits

...
Author SHA1 Message Date
michal cf41a26a0a Add LocalWebRequest helper class for managing Genesis web requests:
- Introduce methods for HTTP POST, GET, DELETE operations with JSON, binary, and custom headers support.
- Add response handling for status codes, exceptions, content retrieval, and asynchronous operations.
- Include string and object extension methods for HTTP requests.
- Provide initial implementation for uploading binary data and error handling stubbed for future enhancement.
2026-03-26 07:30:54 +01:00
michal 2e4741b929 Add Genesis RegisterReaders namespace, resource handling, constants, and application definitions:
- Add new classes and enums (`ApplicationDefinition`, `MeterApplications`, `FileApplications`, `ProgrammingParameters`, etc.) for Genesis application management and definition handling.
- Introduce resource localization with `Resources.Designer.cs`.
- Implement constants for error states, alarms, LED modes, and metrology definitions.
- Add `ServiceUrlsBackUp_New` for managing Genesis-related service endpoints.
2026-03-26 07:20:28 +01:00
michal 18cef617da Enhance Genesis SmartReader with channel-specific volume and timestamp tracking:
- Add per-channel `VolumeStart`, `VolumeEnd`, `TimeStart`, and `TimeEnd` processing for improved multi-channel support.
- Introduce `ChannelAverages` for centralized average calculations across channels.
- Refactor `GenesisSmartReader` to improve data decoding and logging for calibration and flow test records.
- Add telemetry unwrapping helpers for precise volume and timestamp calculations.
- Update `OptoTelegramRaw` to support channel-specific updates and robust data processing.
- Update `AssemblyVersion` and `AssemblyFileVersion` to `3.9.3013.1`.
2026-03-26 07:19:22 +01:00
michal 4ee736cff5 Add advanced multi-channel processing for Genesis SmartReader:
- Refactor `VolumeLtrStart` and `VolumeLtrEnd` to compute channel-specific averages at test boundaries.
- Implement `ProcessOptoLine_ShouldInsertTelegrams` tests for validating telegram insertion and channel updates.
- Add `VolumeLtrEnd_ShouldAverageOnlyAvailableChannels` to handle partial channel data gracefully.
- Introduce `VolumeFromChannelsAtStart` and `VolumeFromChannelsAtEnd` helpers for refined channel data handling.
- Update Opto telegram processing logic to support robust volume and timestamp unwrapping.
- Enhance `GenesisSmartReader.ToString()` for clearer configuration representation.
- Add `OptoTelegramRawTest` to validate telegram behavior, flag handling, and delta computations.
- Update `AssemblyVersion` to `3.9.3012.1`.
2026-03-24 15:23:06 +01:00
michal 4e40dc8601 Comment out Detect_BaudRate_By_ViewFactoryId test method in TouchReadBaudRateDetectionTests. 2026-03-24 10:07:24 +01:00
michal a5a68ec457 Refactor Genesis serial communication handling (ISerialDriver interface compatible with SerialPort):
- Replace method-based `IsOpen()` checks with property-based `IsOpen` usage in `ISerialDriver` and related classes.
- Simplify `Open()` and add `Close()` methods to unify connection management.
- Introduce `InitializeThreeChannelState()` helper in `GenesisSmartReaderTest` for multi-channel state initialization.
- Adjust calculations in `CalculateTimeByChannels()` and `CalculateVolumeByChannels()` for clarity.
- Add new tests for multi-channel processing and validation of updated telegram handling logic.
2026-03-24 10:04:30 +01:00
michal e26d88d437 Refactor Genesis use 3 chanels reader structure:
- Replace `Xylem.Common` namespace references with `TBF.Rig.RegisterReaders`.
- Introduce `GenesisSmartReaderTest` and `FakeSerialDriver` for unit testing.
- Revamp `FlowDirectionDetection` to support multiple channels.
- Make constants in `StreamingDecoder` public for enhanced accessibility.
- Update `AssemblyVersion` and `AssemblyFileVersion` to `3.9.3010.1`.
2026-03-24 08:53:16 +01:00
michal 2522d1da9f Add Genesis radio/opto communication and configuration components
- Replace `OpthoHeadService` with `RadioService` to enhance Genesis communication handling via serial interface.
- Introduce `OptoReceivedEventArgs` to manage event-driven communication.
- Add foundational protocol implementations (`BaseProtocol`, `IProtocol`, etc.) for Genesis configuration and processing.
- Implement `SirtConfig`, `MeterConfig`, and `ProcessConfig` classes for flexible Genesis configuration management.
- Expand Genesis framework to support diagnostics, activity modes, and version retrieval.
2026-03-18 15:57:04 +01:00
michal 8983c01591 Add integration tests for IperlHat communication and diagnostic LED parsing
- Implement `IperlHatIntegrationTests` to validate serial communication, diagnostic LED states, and Opto data handling.
- Add `HexFormatterTest` for verifying Hex formatting utilities.
- Add `DiagnosticLedParserTest` to test parsing for all diagnostic LED states.
2026-03-17 12:15:52 +01:00
michal 61b1320d4f Add Genesis COM port configuration and update Genesis meter setup logic
- Introduce `OptoComPortNr` and `HeadComPortNr` properties in `GenesisHead`.
- Update `SetupGenesisMeter` to use new COM port configurations.
- Add reference to `Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig`.
2026-03-16 15:48:41 +01:00
michal 2c8a0922d1 Genesis dialog with Com ports 2026-03-16 15:47:12 +01:00
michal b078d9586d Add detailed logging for Genesis operations in FlyingStartMassCollectionSeq and FlyingStartSeq.
- Introduce debug and info logs for Genesis initialization, calibration, measurement, and data storage.
- Ensure proper logging during Genesis operations' start and stop phases.
2026-03-16 15:42:45 +01:00
michal 6e35de5f4d Update AssemblyVersion and AssemblyFileVersion to 3.9.3001.1 2026-03-16 15:36:25 +01:00
michal 2dbe4cded4 Genesis slot max count 10, Add DB writer component
Integrate `DatabaseWriter` component and update GenesisHead configuration:

- Add `DatabaseWriter` factory and related classes (`WriteDBCfgCtrl`, `WriterCfg`, `WritingToDB`).
- Extend GenesisHead slot number range to 1-10 and adjust UI layout accordingly.
2026-03-16 15:36:02 +01:00
michal 10865c6878 Update AssemblyVersion and AssemblyFileVersion to 3.9.3000.1 2026-03-16 15:35:06 +01:00
michal ee1353fe54 Add DatabaseWriter component:
- Implement `DatabaseWriter` factory and configuration logic.
- Add `WriteDbCfgCtrl` for component configuration UI.
- Introduce database connection handling, network adapter selection, and tracing record management.
2026-03-16 15:31:58 +01:00
michal 5cefd7fc3a Integrate Genesis meter support into FlyingStartMassCollectionSeq:
- Add initialization, calibration, and measurement logic for Genesis meters.
- Extend result handling to capture Genesis-specific data (timestamps, volumes, errors).
- Ensure proper cleanup of Genesis meters at test conclusion.
2026-03-16 15:31:49 +01:00
michal ab0e5ea82a Genesis heap 2026-03-16 15:17:56 +01:00
michal b2276c086c Merge branch 'develop/SLM-PT50' into develop/Mexico 2026-03-16 13:33:21 +01:00
marekf 0c41c750e8 Add parameter and management IsOffline for components: Modbus>TempMeter>Groch, Meret and Modbus>PressureMeter>Meret and Rig>Keithley>TempMeter and add indicator redColor itemValue for SchematicDrawing 2026-03-04 18:25:28 +01:00
445 changed files with 315258 additions and 518 deletions
@@ -13,5 +13,6 @@ namespace SchematicDrawing
Common.Unit MsrdUnit { get; }
double MsrdValLimLo { get; }
double MsrdValLimHi { get; }
bool IsOffline { get; set; }
}
}
+5 -1
View File
@@ -672,7 +672,11 @@ namespace SchematicDrawing
SizeF size = e.Graphics.MeasureString(strVal, measuredValueFont);
Rectangle rect = new Rectangle(itm.X + itm.MsrdX, itm.Y + itm.MsrdY, (int)Math.Round(size.Width) + 4, (int)Math.Round(size.Height) + 2);
ItemElementRectangles.Add(new ItemElementRect(item, Element.MeasuredVal, rect));
if (measuredValues[msrdValIx] >= itm.MsrdValLimLo && measuredValues[msrdValIx] <= itm.MsrdValLimHi)
if ((item as IDrawingItemWithMeasuredVal).IsOffline)
{
e.Graphics.FillRectangle(measuredValueErrorBackColor, rect);
}
else if (measuredValues[msrdValIx] >= itm.MsrdValLimLo && measuredValues[msrdValIx] <= itm.MsrdValLimHi)
{
e.Graphics.FillRectangle(measuredValueBackColor, rect);
}
+3 -3
View File
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Sensus")]
[assembly: AssemblyProduct("TestBenchFramework")]
[assembly: AssemblyCopyright("Copyright © 2013 - 2023 Sensus Slovensko, a.s.")]
[assembly: AssemblyCopyright("Copyright © 2013 - 2025 Sensus Slovensko, a.s.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
// Build Number
// Revision
//
[assembly: AssemblyVersion("3.9.2149.4")]
[assembly: AssemblyFileVersion("3.9.2149.4")]
[assembly: AssemblyVersion("3.9.3013.1")]
[assembly: AssemblyFileVersion("3.9.3013.1")]
+3 -3
View File
@@ -1,5 +1,5 @@
## Version History
| Version | Target Environment | Title | Description |
|------------|--------------------------------------------------------------|-----------------------------------------------------|------------------------------------|
| 3.9.2149.0 | HeatMeters, Heat meter sensors, Procedure Dilog, Tab Process | Excanged columns value 'Sensor' and 'Heat meter sensor' | Fix in code ProcedureDlg, row 1851 |
| Version | Source of change | Target Environment | Title | Description |
|------------|----------------------|--------------------------------------------------------------|-----------------------------------------------------|------------------------------------|
| 3.9.2149.0 | Michal databse error | HeatMeters, Heat meter sensors, Procedure Dilog, Tab Process | Excanged columns value 'Sensor' and 'Heat meter sensor' | Fix in code ProcedureDlg, row 1851 |
+3
View File
@@ -72,6 +72,9 @@ namespace TBF.Rig.Ambient.Greco
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
AmbientCfg()
{
+3
View File
@@ -66,6 +66,9 @@ namespace TBF.Rig.ControlBoard.Uni
[XmlIgnore]
public IList<GEdge> EdgesToSet { get; set; }
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
UniCBCfg()
{
+71 -35
View File
@@ -1,18 +1,19 @@
///
using Common;
using log4net;
using SchematicDrawing;
///
/// Copyright (c) 2016-2020 Sensus Metering Systems
///
using System;
using System.Collections.Generic;
using Common;
using log4net;
using TBF.Boxes;
using TBF.Rig.Keithley.Multimeter_2010_RS232;
using TBF.Resources;
using TBF.Rig.Keithley.Multimeter_2010_RS232;
namespace TBF.Rig.Keithley.TempMeter
{
public class TempMeter : ComponentBase, GenericDevices.ITempMeter, GenericDevices.IHasCalendarEvents, SchematicDrawing.IDrawingItCmpnt
{
public class TempMeter : ComponentBase, GenericDevices.ITempMeter, GenericDevices.IHasCalendarEvents, IDrawingItCmpntWithMeasuredVal
{
private static readonly ILog log = LogManager.GetLogger(typeof(TempMeter));
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
@@ -36,9 +37,17 @@ namespace TBF.Rig.Keithley.TempMeter
/// Dictionaries to maintain separate operations for each FloatBox argument
Dictionary<DoubleBox, IOperation> operations;
Dictionary<DoubleBox, IOperation> operations2;
public bool IsOffline
{
get { return tempMtrCfg.IsOffline; }
set { tempMtrCfg.IsOffline = value; }
}
int invalidStreak = 0;
const int OfflineAfterInvalid = 30;
public TempMeter() { }
public TempMeter() { }
public TempMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
@@ -93,39 +102,66 @@ namespace TBF.Rig.Keithley.TempMeter
public double ReadTemperature()
{
double resistance = Multimeter.ReadResistance(tempMtrCfg.Channel);
// In non-Normal modes do not perform offline detection here
if (Cfg.DebugLevel != DebugMode.Normal)
{
if (Cfg.DebugLevel == DebugMode.Simulate)
return 20.0;
return 0;
}
double temperature = 0;
if (resistance > 0 && resistance < 2000)
{
if (tempMtrCfg.UseITS90)
{
temperature = Formulas.PlatinumResistanceTM_ITS90_R2T(resistance,
tempMtrCfg.R001C,
tempMtrCfg.a7,
tempMtrCfg.b7,
tempMtrCfg.c7);
}
else
{
temperature = Formulas.PlatinumResistanceTM_ITS27_R2T(resistance,
tempMtrCfg.R0,
tempMtrCfg.A,
tempMtrCfg.B);
}
double resistance;
log.DebugFormat("Ch = {0}, R = {1} Ohm, T = {2} °C", Channel, resistance.ToString("F3"), temperature.ToString("F3"));
}
try
{
resistance = Multimeter.ReadResistance(tempMtrCfg.Channel);
}
catch (Exception ex)
{
invalidStreak++;
return temperature;
if (invalidStreak >= OfflineAfterInvalid)
IsOffline = true;
log.WarnFormat("Keithley TempMeter {0} ch={1}: ReadResistance failed ({2}/{3}): {4}",
Name, Channel, invalidStreak, OfflineAfterInvalid, ex.Message);
return 0; // or keep last value, or double.NaN
}
// Valid resistance => online
invalidStreak = 0;
IsOffline = false;
double temperature;
if (tempMtrCfg.UseITS90)
{
temperature = Formulas.PlatinumResistanceTM_ITS90_R2T(resistance,
tempMtrCfg.R001C,
tempMtrCfg.a7,
tempMtrCfg.b7,
tempMtrCfg.c7);
}
else
{
temperature = Formulas.PlatinumResistanceTM_ITS27_R2T(resistance,
tempMtrCfg.R0,
tempMtrCfg.A,
tempMtrCfg.B);
}
log.DebugFormat("Ch = {0}, R = {1} Ohm, T = {2} °C",
Channel, resistance.ToString("F3"), temperature.ToString("F3"));
return temperature;
}
/// <summary>
/// Events: TempDone, Error
/// </summary>
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
public IOperation ReadTempOp(ref DoubleBox temp)
/// <summary>
/// Events: TempDone, Error
/// </summary>
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
public IOperation ReadTempOp(ref DoubleBox temp)
{
IOperation operation;
if (operations.TryGetValue(temp, out operation))
+5 -2
View File
@@ -62,8 +62,11 @@ namespace TBF.Rig.Keithley.TempMeter
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
TempMeterCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
TempMeterCfg()
{
GNodes = new List<GNode>();
}
+5 -2
View File
@@ -81,9 +81,12 @@ namespace TBF.Rig.MettlerToledo.Standard
[XmlIgnore]
public double MsrdValLimHi { get { return Capacity; } set { } }
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
BalanceCfg()
/// Private parameterless constructor invoked by all other (public) constructors
BalanceCfg()
{
GNodes = new List<GNode>();
}
+5 -2
View File
@@ -68,8 +68,11 @@ namespace TBF.Rig.Modbus.Ambient.Comet
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
AmbientCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
AmbientCfg()
{
GNodes = new List<GNode>();
Format = string.Empty;
@@ -61,8 +61,11 @@ namespace TBF.Rig.Modbus.Meret.AdjustableScale
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
AdjustableMeterCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
AdjustableMeterCfg()
{
GNodes = new List<GNode>();
}
@@ -1,15 +1,17 @@
///
using AppDiagnostic;
using Common;
using Config.Entities;
using log4net;
using SchematicDrawing;
using SharedComponents;
///
/// Copyright (c) 2016-2020 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using log4net;
using Common;
using Config.Entities;
using SchematicDrawing;
using TBF.Rig.Generic;
using TBF.Boxes;
using TBF.Resources;
using TBF.Rig.Generic;
using TBF.Rig.GenericDevices;
namespace TBF.Rig.Modbus.PressureMeter.Meret
@@ -35,9 +37,17 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
public string AltString { get { return string.Empty; } }
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
public bool IsOffline
{
get { return pressureMtrCfg.IsOffline; }
set { pressureMtrCfg.IsOffline = value; }
}
int silentCycles = 0;
const int OfflineAfterSilentCycles = 30;
public PressureMeter() { }
public PressureMeter() { }
public PressureMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
@@ -59,6 +69,9 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
modbus.ComponentNames[pressureMtrCfg.ModbusAddress] = Name;
ticketNumber = modbus.RegisterForPolling();
log.FatalFormat("{0} initialized: {1}", Name, this);
IsOffline = true;
silentCycles = OfflineAfterSilentCycles;
}
else
{
@@ -68,25 +81,85 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
public void RunDeviceBefore()
{
if (DebugLevel == DebugMode.Normal && modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Dequeue();
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
{
/// Swap byte order
byte t1 = telegram[3];
byte t2 = telegram[4];
byte t3 = telegram[5];
byte t4 = telegram[6];
telegram[3] = t4;
telegram[4] = t3;
telegram[5] = t2;
telegram[6] = t1;
if (DebugLevel != DebugMode.Normal)
return;
receivedPressure = Units.ConvertFrom(Unit.kPa, System.BitConverter.ToSingle(telegram, 3));
bool gotValidResponse = false;
if (DebugLevel == DebugMode.Normal && modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[pressureMtrCfg.ModbusAddress].Dequeue();
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
{
/// Swap byte order
byte t1 = telegram[3];
byte t2 = telegram[4];
byte t3 = telegram[5];
byte t4 = telegram[6];
telegram[3] = t4;
telegram[4] = t3;
telegram[5] = t2;
telegram[6] = t1;
receivedPressure = Units.ConvertFrom(Unit.kPa, System.BitConverter.ToSingle(telegram, 3));
log.WarnFormat("Pressure: {0}={1}bar", Name, receivedPressure.ToString("F3"));
gotValidResponse = true;
}
}
else
{
string telHex = telegram == null ? "<null>" : BitConverter.ToString(telegram);
string reason;
if (telegram == null) reason = "null telegram";
else if (telegram.Length != 9) reason = $"unexpected length (expected 9, got {telegram.Length})";
else if (telegram.Length > 1 && telegram[1] != 4) reason = $"unexpected function (expected 0x04, got 0x{telegram[1]:X2})";
else if (telegram.Length > 2 && telegram[2] != 4) reason = $"unexpected bytecount (expected 0x04, got 0x{telegram[2]:X2})";
else reason = "unknown mismatch";
//LiveLogCache.Instance.AddLog(string.Format(
// ">> RunDeviceBefore() >> component: {0} bad received telegram: {1} data={2}",
// Name,
// reason,
// telHex));
}
}
else
{
bool debugOk = (DebugLevel == DebugMode.Normal);
int addr = pressureMtrCfg.ModbusAddress;
try
{
var q = modbus.ReceivedTelegrams[addr];
int qCount = (q == null) ? -1 : q.Count;
//LiveLogCache.Instance.AddLog(string.Format(
// ">> RunDeviceBefore() >> component: {0} condition failed | DebugOk={1} | Addr={2} | QueueNull={3} | QueueCount={4} | silentCycles={5}",
// Name, debugOk, addr, (q == null), qCount, silentCycles));
}
catch (Exception ex)
{
//LiveLogCache.Instance.AddLog(string.Format(
// ">> RunDeviceBefore() >> component: {0} condition failed | DebugOk={1} | Addr={2} | silentCycles={3} | EXCEPTION={4}",
// Name, debugOk, addr, silentCycles, ex.Message));
}
}
if (gotValidResponse)
{
silentCycles = 0;
IsOffline = false;
}
else
{
silentCycles++;
if (silentCycles >= OfflineAfterSilentCycles)
{
IsOffline = true;
//LiveLogCache.Instance.AddLog(string.Format(">> RunDeviceBefore() >> component: {0} IsOffline", Name));
}
}
}
public void RunDeviceAfter()
@@ -57,8 +57,11 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
PressureMeterCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
PressureMeterCfg()
{
GNodes = new List<GNode>();
}
@@ -93,7 +96,7 @@ namespace TBF.Rig.Modbus.PressureMeter.Meret
"Modbus address", /// 0
"Default pressure", /// 1
"Display format", /// 2
"Unit of pressure", /// 3
"Units of pressure", /// 3
"Pressure limit Lo", /// 4
"Pressure limit Hi", /// 5
};
@@ -59,8 +59,11 @@ namespace TBF.Rig.Modbus.TempControl.Easytherm
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// <summary> Procedure parameters </summary>
[XmlIgnore]
[XmlIgnore]
public bool IsOffline { get; set; }
/// <summary> Procedure parameters </summary>
[XmlIgnore]
public ProcParams ProcParams;
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
@@ -68,9 +68,12 @@ namespace TBF.Rig.Modbus.TempControl.Novus
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
[XmlIgnore]
public bool IsOffline { get; set; }
/// <summary> Procedure parameters </summary>
[XmlIgnore]
/// <summary> Procedure parameters </summary>
[XmlIgnore]
public ProcParams ProcParams;
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
+199 -14
View File
@@ -1,14 +1,15 @@
///
using Common;
using Config.Entities;
using log4net;
///
/// Copyright (c) 2021 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using log4net;
using Common;
using Config.Entities;
using TBF.Rig.Generic;
using System.Xml.Serialization;
using TBF.Boxes;
using TBF.Resources;
using TBF.Rig.Generic;
namespace TBF.Rig.Modbus.TempMeter.Groch
{
@@ -46,6 +47,24 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
//[XmlIgnore]
public bool IsOffline
{
get { return tempMtrCfg.IsOffline; }
set { tempMtrCfg.IsOffline = value; }
}
// Shared device/channel health tracking (updated by Channel 0)
static int grochSilentCycles = 0;
static bool grochOffline = true;
// Per-channel consecutive invalid-value counters
static int[] invalidStreak = new int[TempMeterCfg.ChannelsCount];
// Thresholds
const int GrochOfflineAfterCycles = 30; // No valid telegram N cycles => Groch offline
const int ChannelOfflineAfterInvalid = 30; // Invalid value N times => channel offline
public TempMeter() { }
@@ -57,9 +76,17 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
public override void Initialize()
{
// Ensure shared static health tracking is initialized only once
EnsureSharedInitialized();
if (DebugLevel == DebugMode.Normal)
{
/// Only TempMeter with Channel == 0 communicates via the parent Modbus component
// Each instance starts from shared states (will become online automatically after valid data)
bool channelOffline = invalidStreak[Channel] >= ChannelOfflineAfterInvalid;
IsOffline = grochOffline || channelOffline;
// Only TempMeter with Channel == 0 communicates via the parent Modbus component
if (Channel == 0)
{
modbus = TbfComponents.FindComponent(tempMtrCfg.ParentName) as Common.Modbus;
@@ -68,10 +95,13 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
modbus.ComponentNames[0] = Name;
ticketNumber = modbus.RegisterForPolling();
}
log.FatalFormat("{0} initialized: {1}", Name, this);
}
else
{
// In simulation you can decide whether to keep IsOffline false
IsOffline = false;
log.FatalFormat("{0} simulated: {1}", Name, this);
}
}
@@ -110,10 +140,11 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
public void RunDeviceBefore()
{
if (DebugLevel == DebugMode.Normal && Channel == 0)
/*if (DebugLevel == DebugMode.Normal && Channel == 0)
{
/// Only TempMeter with Channel == 0 communicates via the parent Modbus component
///
if (modbus.ReceivedTelegrams[0].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[0].Dequeue();
@@ -147,7 +178,20 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
}
}
}
}*/
if (DebugLevel != DebugMode.Normal)
return;
if (Channel == 0)
{
// update shared state first (process telegrams)
UpdateSharedStateFromGroch();
}
// now every instance (including ch0) computes its offline state from fresh shared values
bool channelOffline = invalidStreak[Channel] >= ChannelOfflineAfterInvalid;
IsOffline = grochOffline || channelOffline;
}
public void RunDeviceAfter()
@@ -182,12 +226,21 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
}
}
/// <summary>
/// Events: TempDone, Error
/// </summary>
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
public IOperation ReadTempOp(ref DoubleBox temp)
// Decide whether the raw value looks valid.
// Adjust this to match real device behavior.
static bool IsValidRaw(int raw)
{
if (raw == 0) return false;
if (raw == 0xFFFF) return false;
return true;
}
/// <summary>
/// Events: TempDone, Error
/// </summary>
/// <param name="temp">Reference to a variable for the temperature in Celsius</param>
/// <returns>ReadTempOp instance reference casted to IOperaton</returns>
public IOperation ReadTempOp(ref DoubleBox temp)
{
return new ReadTempOp(this, ref temp);
}
@@ -202,5 +255,137 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
{
return new ReadTempOp(this, ref temp, tempDone);
}
}
// Add to class TempMeter
static readonly object initLock = new object();
static bool sharedInitialized = false;
static void EnsureSharedInitialized()
{
if (sharedInitialized) return;
lock (initLock)
{
if (sharedInitialized) return;
// Start as offline until we receive the first valid telegram
grochSilentCycles = GrochOfflineAfterCycles;
grochOffline = true;
for (int i = 0; i < TempMeterCfg.ChannelsCount; i++)
{
invalidStreak[i] = ChannelOfflineAfterInvalid; // start channels as offline until first valid value
tempRaw[i] = 0; // optional: reset raw buffer
}
sharedInitialized = true;
}
}
void UpdateSharedStateFromGroch()
{
bool gotValidTelegram = false;
// Process all queued telegrams to avoid backlog accumulation
while (modbus.ReceivedTelegrams[0].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[0].Dequeue();
if (telegram.Length != 28)
continue;
byte checksum = 0;
for (int i = 0; i < 27; i++)
checksum += telegram[i];
bool isValidGroch =
telegram[0] == '\r' &&
telegram[1] == '\n' &&
telegram[2] == GrochAddress &&
telegram[3] == 28 &&
telegram[5] == 2 &&
telegram[26] == 3 &&
telegram[27] == checksum;
if (!isValidGroch)
continue;
// At least one valid telegram received this cycle
gotValidTelegram = true;
// Extract raw channel values
if (TempMeterCfg.ChannelsCount > 0)
{
int raw = telegram[10] + 256 * telegram[11];
tempRaw[0] = raw;
invalidStreak[0] = IsValidRaw(raw) ? 0 : invalidStreak[0] + 1;
}
if (TempMeterCfg.ChannelsCount > 1)
{
int raw = telegram[12] + 256 * telegram[13];
tempRaw[1] = raw;
invalidStreak[1] = IsValidRaw(raw) ? 0 : invalidStreak[1] + 1;
}
if (TempMeterCfg.ChannelsCount > 2)
{
int raw = telegram[14] + 256 * telegram[15];
tempRaw[2] = raw;
invalidStreak[2] = IsValidRaw(raw) ? 0 : invalidStreak[2] + 1;
}
if (TempMeterCfg.ChannelsCount > 3)
{
int raw = telegram[16] + 256 * telegram[17];
tempRaw[3] = raw;
invalidStreak[3] = IsValidRaw(raw) ? 0 : invalidStreak[3] + 1;
}
if (TempMeterCfg.ChannelsCount > 4)
{
int raw = telegram[18] + 256 * telegram[19];
tempRaw[4] = raw;
invalidStreak[4] = IsValidRaw(raw) ? 0 : invalidStreak[4] + 1;
}
if (TempMeterCfg.ChannelsCount > 5)
{
int raw = telegram[20] + 256 * telegram[21];
tempRaw[5] = raw;
invalidStreak[5] = IsValidRaw(raw) ? 0 : invalidStreak[5] + 1;
}
if (TempMeterCfg.ChannelsCount > 6)
{
int raw = telegram[22] + 256 * telegram[23];
tempRaw[6] = raw;
invalidStreak[6] = IsValidRaw(raw) ? 0 : invalidStreak[6] + 1;
}
if (TempMeterCfg.ChannelsCount > 7)
{
int raw = telegram[24] + 256 * telegram[25];
tempRaw[7] = raw;
invalidStreak[7] = IsValidRaw(raw) ? 0 : invalidStreak[7] + 1;
}
}
// Device-level offline detection
if (gotValidTelegram)
{
grochSilentCycles = 0;
grochOffline = false;
}
else
{
grochSilentCycles++;
if (grochSilentCycles >= GrochOfflineAfterCycles)
{
grochOffline = true;
}
}
}
}
}
+12 -9
View File
@@ -14,9 +14,9 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
{
public class TempMeterCfg : ComponentCfgBase, IChildComponentCfg, GenericDevices.ICalibInfoCfg, IParamsProvider,
IDrawingItemWithMeasuredVal
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TempMeterCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(TempMeterCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities)
{
@@ -26,9 +26,9 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
public const int ChannelsCount = 8;
///
/// Serialized parameters
///
///
/// Serialized parameters
///
public int Address; /// 0 0 .. 255
public int Channel; /// 1 0 .. 7 (7 = TempMeterCfg.ChannelsCount-1)
public double A1; /// 2
@@ -65,8 +65,11 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
TempMeterCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
TempMeterCfg()
{
GNodes = new List<GNode>();
}
@@ -113,7 +116,7 @@ namespace TBF.Rig.Modbus.TempMeter.Groch
"A5", /// 6
"Default temperature", /// 7
"Display format", /// 8
"Unit of temperature", /// 9
"Units of temperature", /// 9
"Temperature limit Lo", /// 10
"Temperature limit Hi", /// 11
};
+63 -19
View File
@@ -33,9 +33,17 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
public string AltString { get { return string.Empty; } }
int ticketNumber; /// 0 .. number of devices registered for regular polling - 1
public bool IsOffline
{
get { return tempMtrCfg.IsOffline; }
set { tempMtrCfg.IsOffline = value; }
}
int silentCycles = 0;
const int OfflineAfterSilentCycles = 30; // e.g. 5 cycles without valid response
public TempMeter() { }
public TempMeter() { }
public TempMeter(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
@@ -50,6 +58,11 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
modbus.ComponentNames[tempMtrCfg.ModbusAddress] = Name;
ticketNumber = modbus.RegisterForPolling();
// Start offline until first valid response arrives (optional but consistent)
IsOffline = true;
silentCycles = OfflineAfterSilentCycles;
log.FatalFormat("{0} initialized: {1}", Name, this);
}
@@ -59,25 +72,45 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
/// <summary>Run this device</summary>
public void RunDeviceBefore()
{
if (modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Dequeue();
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
{
/// Swap byte order
byte t1 = telegram[3];
byte t2 = telegram[4];
byte t3 = telegram[5];
byte t4 = telegram[6];
telegram[3] = t4;
telegram[4] = t3;
telegram[5] = t2;
telegram[6] = t1;
if (Cfg.DebugLevel != DebugMode.Normal)
return;
receivedTemp = System.BitConverter.ToSingle(telegram, 3);
log.WarnFormat("Temperature: {0}={1}°C", Name, receivedTemp.ToString("F1"));
}
}
bool gotValidResponse = false;
while (modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[tempMtrCfg.ModbusAddress].Dequeue();
// Presence = valid response frame
if (telegram.Length == 9 && telegram[1] == 4 && telegram[2] == 4)
{
gotValidResponse = true;
// Optional: still parse temperature for display/use (does not affect presence)
byte t1 = telegram[3];
byte t2 = telegram[4];
byte t3 = telegram[5];
byte t4 = telegram[6];
telegram[3] = t4;
telegram[4] = t3;
telegram[5] = t2;
telegram[6] = t1;
receivedTemp = BitConverter.ToSingle(telegram, 3);
}
}
if (gotValidResponse)
{
silentCycles = 0;
IsOffline = false;
}
else
{
silentCycles++;
if (silentCycles >= OfflineAfterSilentCycles)
IsOffline = true;
}
}
public void RunDeviceAfter()
@@ -116,6 +149,17 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
}
}
static bool IsValidTemperature(float t)
{
// Reject NaN/Infinity
if (float.IsNaN(t) || float.IsInfinity(t)) return false;
// Put a reasonable physical range here (adjust to your real use case)
if (t < -50.0f || t > 200.0f) return false;
return true;
}
/// <summary>
/// Events: PressureDone, Error
/// </summary>
@@ -58,8 +58,11 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
TempMeterCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
TempMeterCfg()
{
GNodes = new List<GNode>();
}
@@ -94,7 +97,7 @@ namespace TBF.Rig.Modbus.TempMeter.Meret
"Modbus address", /// 0
"Default temperature", /// 1
"Display format", /// 2
"Unit of temperature", /// 3
"Units of temperature", /// 3
"Temperature limit Lo", /// 4
"Temperature limit Hi", /// 5
};
@@ -49,8 +49,11 @@ namespace TBF.Rig.Modbus.UltrasoundLevelMeter
[XmlIgnore]
public IList<GNode> GNodes { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
LevelMeterCfg()
[XmlIgnore]
public bool IsOffline { get; set; }
/// Private parameterless constructor invoked by all other (public) constructors
LevelMeterCfg()
{
GNodes = new List<GNode>();
}
@@ -0,0 +1,33 @@
///
/// Copyright (c) 2018 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using TBF.Rig.Generic;
namespace TBF.Rig.Output.DB.DatabaseWriter
{
/// <summary>
/// Factory component 'DatabaseWriter' implements more storing modules
/// Mosules:
/// * store whole results in database
/// * store to specific mexico database structure - not implemented yet
/// * store configurable data SQL template to a specific database - not implemented yet
/// </summary>
public class Factory : IComponentFactory
{
public string ClassName { get { return GetType().Namespace.Substring(8); } } /// For backward compatibility
public override string ToString() { return ClassName; }
public IComponent DummyComponent() { return new WritingToDb(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new WritingToDb(cfg); }
public IComponentCfg DefaultConfig() { return new WriterCfg("DatabaseWriter", this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(WriterCfg.Serializer, component, this);
}
}
}
@@ -0,0 +1,148 @@
///
/// Copyright (c) 2018-2019 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Net;
using Common;
using TBF.Rig.Generic;
namespace TBF.Rig.Output.DB.DatabaseWriter
{
public partial class WriteDbCfgCtrl : Configs.ConfigCtrlUtils, IComponentCfgCtrl
{
public bool ShowMore { get { return false; } }
WriterCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as WriterCfg;
Redraw();
}
}
public WriteDbCfgCtrl()
{
InitializeComponent();
}
private void WriterCfgCtrl_Load(object sender, EventArgs e)
{
if (config == null) return;
Redraw();
}
public void Closing()
{
}
void Redraw()
{
if (config == null) return; /// Control was not loaded, settings were not changed
classNameLabel.Text = config.Factory.ClassName;
nameTextBox.Text = config.Name;
connStrTextBox.Text = config.ConnStr;
connStr2TextBox.Text = config.ConnStr2;
checkPreviousRecordsCheckBox.Checked = config.CheckPreviousRecords;
saveTracingRecordsCheckBox.Checked = config.SaveTracingRecords;
Network.AdapterInfo.RefreshNetAdaptersInfo();
IList<Network.AdapterInfo> adapters = Network.AdapterInfo.NetAdapters;
string cfgAdapter = string.Empty;
foreach (Network.AdapterInfo ai in adapters)
{
string record;
if (ai.IPAddress == null)
{
/// This happens with network adapters that currently have no IP address,
/// for instance not connected wireless or dial-up adapters
record = "???.???.???.???";
}
else
{
/// Do not consider IPv6 adapters as well as "Any", "Broadcast", "Loopback" or "None" addresses
if ((ai.IPAddress.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6) ||
ai.IPAddress.Equals(IPAddress.Any) ||
ai.IPAddress.Equals(IPAddress.Broadcast) ||
ai.IPAddress.Equals(IPAddress.IPv6Any) ||
ai.IPAddress.Equals(IPAddress.IPv6Loopback) ||
ai.IPAddress.Equals(IPAddress.IPv6None) ||
ai.IPAddress.Equals(IPAddress.Loopback) ||
ai.IPAddress.Equals(IPAddress.None))
{
continue;
}
record = ai.IPAddress.ToString();
}
record += " - " + ai.Description;
int i = netAdapterComboBox.Items.Add(record);
if (ai.Description == config.NetAdapter)
{
/// Select the adapter currently in the configuration
netAdapterComboBox.SelectedIndex = i;
}
}
/// Select the first one if the adapter from the configuration does not exist on the system
if (netAdapterComboBox.SelectedIndex < 0 && netAdapterComboBox.Items.Count > 0)
{
netAdapterComboBox.SelectedIndex = 0;
}
}
public void Unlock()
{
nameTextBox.Enabled = true;
connStrTextBox.Enabled = true;
connStr2TextBox.Enabled = true;
netAdapterComboBox.Enabled = true;
checkPreviousRecordsCheckBox.Enabled = true;
saveTracingRecordsCheckBox.Enabled = true;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
return CfgUpdateFlags.None;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
if (config.Name != nameTextBox.Text)
{
config.Name = nameTextBox.Text;
flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
}
flags |= UpdateDifferent(ref config.ConnStr, connStrTextBox.Text, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
flags |= UpdateDifferent(ref config.ConnStr2, connStr2TextBox.Text, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
flags |= UpdateDifferent(ref config.CheckPreviousRecords, checkPreviousRecordsCheckBox.Checked, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
flags |= UpdateDifferent(ref config.SaveTracingRecords, saveTracingRecordsCheckBox.Checked, CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
/// Network adapter
string strAdapter = netAdapterComboBox.Text;
int iDash = strAdapter.IndexOf(" - ");
string netAdapter = (iDash < 0) ? "" : strAdapter.Substring(iDash + 3);
if (config.NetAdapter != netAdapter)
{
config.NetAdapter = netAdapter;
flags |= (CfgUpdateFlags.AnyChange | CfgUpdateFlags.RestartRqrd);
}
return flags;
}
}
}
@@ -0,0 +1,184 @@
///
/// Copyright (c) 2018 Sensus Slovensko a.s.
///
namespace TBF.Rig.Output.DB.DatabaseWriter
{
partial class WriteDbCfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.connStrTextBox = new System.Windows.Forms.TextBox();
this.connStrLabel = new System.Windows.Forms.Label();
this.saveTracingRecordsCheckBox = new System.Windows.Forms.CheckBox();
this.checkPreviousRecordsCheckBox = new System.Windows.Forms.CheckBox();
this.netAdapterLabel = new System.Windows.Forms.Label();
this.netAdapterComboBox = new System.Windows.Forms.ComboBox();
this.connStr2TextBox = new System.Windows.Forms.TextBox();
this.connStr2Label = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(117, 32);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(326, 20);
this.nameTextBox.TabIndex = 2;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(5, 35);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(38, 13);
this.nameLabel.TabIndex = 1;
this.nameLabel.Text = "Name:";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(114, 10);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 0;
this.classNameLabel.Text = "ComonentName";
//
// connStrTextBox
//
this.connStrTextBox.Enabled = false;
this.connStrTextBox.Location = new System.Drawing.Point(8, 74);
this.connStrTextBox.Name = "connStrTextBox";
this.connStrTextBox.Size = new System.Drawing.Size(435, 20);
this.connStrTextBox.TabIndex = 4;
//
// connStrLabel
//
this.connStrLabel.AutoSize = true;
this.connStrLabel.Location = new System.Drawing.Point(5, 58);
this.connStrLabel.Name = "connStrLabel";
this.connStrLabel.Size = new System.Drawing.Size(92, 13);
this.connStrLabel.TabIndex = 3;
this.connStrLabel.Text = "Connection string:";
//
// saveTracingRecordsCheckBox
//
this.saveTracingRecordsCheckBox.AutoSize = true;
this.saveTracingRecordsCheckBox.Enabled = false;
this.saveTracingRecordsCheckBox.Location = new System.Drawing.Point(117, 221);
this.saveTracingRecordsCheckBox.Name = "saveTracingRecordsCheckBox";
this.saveTracingRecordsCheckBox.Size = new System.Drawing.Size(177, 17);
this.saveTracingRecordsCheckBox.TabIndex = 10;
this.saveTracingRecordsCheckBox.Text = "Save production tracing records";
this.saveTracingRecordsCheckBox.UseVisualStyleBackColor = true;
//
// checkPreviousRecordsCheckBox
//
this.checkPreviousRecordsCheckBox.AutoSize = true;
this.checkPreviousRecordsCheckBox.Enabled = false;
this.checkPreviousRecordsCheckBox.Location = new System.Drawing.Point(117, 199);
this.checkPreviousRecordsCheckBox.Name = "checkPreviousRecordsCheckBox";
this.checkPreviousRecordsCheckBox.Size = new System.Drawing.Size(138, 17);
this.checkPreviousRecordsCheckBox.TabIndex = 9;
this.checkPreviousRecordsCheckBox.Text = "Check previous records";
this.checkPreviousRecordsCheckBox.UseVisualStyleBackColor = true;
//
// netAdapterLabel
//
this.netAdapterLabel.AutoSize = true;
this.netAdapterLabel.Location = new System.Drawing.Point(5, 147);
this.netAdapterLabel.Name = "netAdapterLabel";
this.netAdapterLabel.Size = new System.Drawing.Size(89, 13);
this.netAdapterLabel.TabIndex = 7;
this.netAdapterLabel.Text = "Network adapter:";
//
// netAdapterComboBox
//
this.netAdapterComboBox.Enabled = false;
this.netAdapterComboBox.FormattingEnabled = true;
this.netAdapterComboBox.Location = new System.Drawing.Point(8, 163);
this.netAdapterComboBox.Name = "netAdapterComboBox";
this.netAdapterComboBox.Size = new System.Drawing.Size(435, 21);
this.netAdapterComboBox.TabIndex = 8;
//
// connStr2TextBox
//
this.connStr2TextBox.Enabled = false;
this.connStr2TextBox.Location = new System.Drawing.Point(8, 118);
this.connStr2TextBox.Name = "connStr2TextBox";
this.connStr2TextBox.Size = new System.Drawing.Size(434, 20);
this.connStr2TextBox.TabIndex = 6;
//
// connStr2Label
//
this.connStr2Label.AutoSize = true;
this.connStr2Label.Location = new System.Drawing.Point(5, 102);
this.connStr2Label.Name = "connStr2Label";
this.connStr2Label.Size = new System.Drawing.Size(101, 13);
this.connStr2Label.TabIndex = 5;
this.connStr2Label.Text = "Connection string 2:";
//
// TracingCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.connStr2TextBox);
this.Controls.Add(this.connStr2Label);
this.Controls.Add(this.netAdapterComboBox);
this.Controls.Add(this.netAdapterLabel);
this.Controls.Add(this.saveTracingRecordsCheckBox);
this.Controls.Add(this.checkPreviousRecordsCheckBox);
this.Controls.Add(this.connStrTextBox);
this.Controls.Add(this.connStrLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Name = "WriteDbCfgCtrl";
this.Size = new System.Drawing.Size(450, 250);
this.Load += new System.EventHandler(this.WriterCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.TextBox connStrTextBox;
private System.Windows.Forms.Label connStrLabel;
private System.Windows.Forms.CheckBox saveTracingRecordsCheckBox;
private System.Windows.Forms.CheckBox checkPreviousRecordsCheckBox;
private System.Windows.Forms.Label netAdapterLabel;
private System.Windows.Forms.ComboBox netAdapterComboBox;
private System.Windows.Forms.TextBox connStr2TextBox;
private System.Windows.Forms.Label connStr2Label;
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,57 @@
///
/// Copyright (c) 2018 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using System.Xml.Serialization;
using TBF.Rig.Generic;
namespace TBF.Rig.Output.DB.DatabaseWriter
{
///
/// Class and file name is preserved for backward compatibility
///
public class WriterCfg : ComponentCfgBase, Generic.IComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(WriterCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Config.Entities.Component> cmpntEntities) { return new WriteDbCfgCtrl(); }
public string ConnStr;
public string ConnStr2;
public string NetAdapter;
public bool CheckPreviousRecords;
public bool SaveTracingRecords;
/// Private parameterless constructor invoked by all other (public) constructors
WriterCfg()
{
ParentName = string.Empty;
NetAdapter = string.Empty;
ConnStr = "server=10.42.128.24; database=sledovanie2020; uid=vyroba2020; pwd=qwerty; charset=utf8;";
ConnStr2 = "server=10.42.128.24; database=sledovanie2019; uid=vyroba2019; pwd=qwerty; charset=utf8;";
CheckPreviousRecords = true;
SaveTracingRecords = true;
}
public WriterCfg(string name, IComponentFactory factory)
: this()
{
this.Name = name;
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("Name={0}, CheckPrev.={1}, SaveRecords={2}, ConnStr={3}, , ConnStr2={4}",
Name,
CheckPreviousRecords,
SaveTracingRecords,
ConnStr,
ConnStr2);
}
}
}
@@ -0,0 +1,577 @@
///
/// Copyright (c) 2018-2023 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Common;
using log4net;
using NHibernate;
using SharedDatabase;
using SharedDatabase.Entities;
using TBF.Rig.Sequences;
namespace TBF.Rig.Output.DB.DatabaseWriter
{
public enum Retv
{
OK,
Error,
}
class DateTimeComparer : IComparer<DateTime>
{
public int Compare(DateTime x, DateTime y)
{
return DateTime.Compare(x, y);
}
}
public class WritingToDb : ComponentBase, IOperation, GenericDevices.IResultsWriter, GenericDevices.IStartInfoReader, Generic.IDevice
{
private static readonly ILog log = LogManager.GetLogger(typeof(WritingToDb));
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); }
public const string WorkstepName = "Test_bench";
public OrderInfo DefaultOrder;
WriterCfg tracingCfg;
string workplace
{
get
{
TBF.Rig.GenericDevices.IBenchInfo benchInfo = TBF.Rig.Sequences.ProcessData.BenchInfo;
return (benchInfo != null) ? benchInfo.TestBenchName : "TestBench";
}
}
IPAddress ipAddress;
IPAddress netMask;
public IPAddress IPAddress { get { return ipAddress; } }
public IPAddress NetMask { get { return netMask; } }
enum OpState
{
None,
CheckPreviousRecordsScheduled,
CheckPreviousRecordsRunning,
SaveTracingRecordsScheduled,
SaveTracingRecordsRunning,
}
///
OpState currentOpState;
bool opCompleted;
bool anyError;
/// <summary>
/// Watermeters to check at the beginning of the cycle
/// </summary>
IList<Results.Entities.WaterMeter> waterMeters;
/// <summary>
/// Data (tracing records) to write at the end of cycle
/// </summary>
Results.Entities.Batch batch;
public ISessionFactory SessionFactory; /// Factory to create database sessions that is initialized in the constructor
public WritingToDb() {}
public WritingToDb(Generic.IComponentCfg cfg)
: base(cfg)
{
tracingCfg = cfg as WriterCfg;
if (tracingCfg == null) throw new ArgumentException("tracingCfg");
Network.AdapterInfo.RefreshNetAdaptersInfo();
Network.AdapterInfo netadapter = Network.AdapterInfo.GetNetAdapter(tracingCfg.NetAdapter);
ipAddress = netadapter.IPAddress;
netMask = netadapter.NetMask;
currentOpState = OpState.None;
log.Warn(this.ToString());
DefaultOrder = null;
}
///
/// IDevice interface implementation
///
public override void Initialize()
{
if (tracingCfg.DebugLevel == DebugMode.Simulate) return;
string ipAddress = GetIPAddress();
///
/// Session factory is used to create database sessions
///
SharedDatabase.TracingDB.SessionFactory = this.SessionFactory =
FluentNHibernate.Cfg.Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.MySQLConfiguration.Standard.ConnectionString(tracingCfg.ConnStr))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Process>())
.ExposeConfiguration(SharedDatabase.TracingDB.BuildSchema)
.BuildSessionFactory();
try
{
using (var session = SessionFactory.OpenSession())
{
///
/// Initialize DefaultOrder
///
var dfltOrders = session.QueryOver<OrderInfo>()
.Where(x => x.POName == "0000001")
.And(x => (x.POState == (sbyte)OrderState.New || x.POState == (sbyte)OrderState.Active))
.List();
if (dfltOrders.Count > 0) DefaultOrder = dfltOrders[0];
#if !DEBUG
///
/// Register this test bench in the tracing DB for approx. 2 weeks (RELEASE version only)
///
SharedDatabase.TracingDB.RegisterWorkplaceObsolete(session,
workplace,
CurrentUser.UserName(),
GetIPAddress(),
"<multiple>",
WorkstepName,
DateTime.Now + new TimeSpan(15, 0, 0, 0));
session.Flush();
#endif
session.Close();
}
}
catch (Exception ex)
{
log.ErrorFormat("Cannot load a default order from a database or regster this workplace: {0}", ex);
DefaultOrder = null;
}
}
///
string GetIPAddress()
{
return (ipAddress != null) ? ipAddress.ToString() : "1.2.3.4";
}
///
public void RunDeviceBefore() { }
public void RunDeviceAfter() { }
///
public void StopDevice()
{
if (tracingCfg.DebugLevel != DebugMode.Normal) return;
using (ISession session = SessionFactory.OpenSession())
{
SharedDatabase.TracingDB.UnregisterWorkplaceObsolete(session, workplace);
session.Flush();
}
}
public void StopDevice2() {}
public IList<OrderInfo> ReadOrders()
{
try
{
using (var session = SessionFactory.OpenSession())
{
var result = session.QueryOver<OrderInfo>()
.Where(x => (x.POState == (sbyte)OrderState.New || x.POState == (sbyte)OrderState.Active))
.List();
session.Close();
return result;
}
}
catch (Exception ex)
{
log.ErrorFormat("ReadOrders() failed: {0}", ex.Message);
return new List<OrderInfo>();
}
}
/// <summary>
/// Read reference records belonging to a specified order.
/// Used when retrieving housing S/N-s belonging to obtained eRegister numbers.
/// </summary>
/// <param name="session">DB sesson</param>
/// <param name="order">Order</param>
/// <returns>List of reference records-s</returns>
public IList<ReferenceRecord> ReadRefRecords(ISession session, OrderInfo order, OrderInfo dfltOrder = null, Process dfltWFlow = null)
{
try
{
var refRecords = session.QueryOver<ReferenceRecord>()
.Where(x => (x.POName == order.POName))
.List();
refRecords.Reverse();
if (dfltOrder != null && dfltWFlow != null)
{
var moreRecords = session.QueryOver<ReferenceRecord>()
.Where(x => (x.POName == dfltOrder.POName))
.And(x => (x.Workflow == dfltWFlow.Name))
.List();
for (int i = moreRecords.Count - 1; i >= 0; i--) refRecords.Add(moreRecords[i]);
}
return refRecords;
}
catch (Exception exc)
{
log.ErrorFormat("Cannot read ref.records from the tracing DB: {0}", exc.Message);
return new List<ReferenceRecord>();
}
}
/// <summary>
/// Read records belonging to a specified order
/// </summary>
/// <param name="session">DB sesson</param>
/// <param name="order">Order</param>
/// <returns>List of records</returns>
public IList<Record> ReadRecords(ISession session, OrderInfo order, OrderInfo dfltOrder = null, Process dfltWFlow = null)
{
try
{
var records = session.QueryOver<Record>()
.JoinQueryOver<ReferenceRecord>(rec => rec.ReferenceRecord)
.Where(rr => rr.POName == order.POName)
.List();
records.Reverse();
if (dfltOrder != null && dfltWFlow != null)
{
var moreRecords = session.QueryOver<Record>()
.JoinQueryOver<ReferenceRecord>(rec => rec.ReferenceRecord)
.Where(rr => (rr.POName == dfltOrder.POName))
.And(rr => (rr.Workflow == dfltWFlow.Name))
.List();
for (int i = moreRecords.Count - 1; i >= 0; i--) records.Add(moreRecords[i]);
}
return records;
}
catch (Exception exc)
{
log.ErrorFormat("Cannot read records from the tracing DB: {0}", exc.Message);
return new List<Record>();
}
}
/// <summary>
/// Reads information on start of a cycle, Events: Event.InfoRead
/// </summary>
/// <param name="waterMeters">Results of water meters</param>
/// <returns>Reference to the operation</returns>
public IOperation ReadStartInfoOp(IList<Results.Entities.WaterMeter> waterMeters)
{
if (!tracingCfg.CheckPreviousRecords)
{
return null;
}
else if ((currentOpState == OpState.CheckPreviousRecordsRunning) || (currentOpState == OpState.SaveTracingRecordsRunning))
{
throw new Exception("Sequence error");
}
else
{
this.waterMeters = waterMeters;
currentOpState = OpState.CheckPreviousRecordsScheduled;
return this;
}
}
/// <summary>
/// Writes the test cycle results into a file, Events: Event.ResultsWritten
/// </summary>
/// <param name="procedure">Procedure to print the results of</param>
/// <param name="unsortedResults">Results to write into the file</param>
/// <returns>Reference to the operation</returns>
public IOperation ProcessResultsOp(Results.Entities.Batch batch)
{
if (!tracingCfg.SaveTracingRecords)
{
return null;
}
else if ((currentOpState == OpState.CheckPreviousRecordsRunning) || (currentOpState == OpState.SaveTracingRecordsRunning))
{
throw new Exception("Sequence error");
}
else
{
this.batch = batch;
currentOpState = OpState.SaveTracingRecordsScheduled;
return this;
}
}
/// <summary>Start this operation</summary>
public void Start()
{
if (currentOpState == OpState.CheckPreviousRecordsScheduled)
{
currentOpState = OpState.CheckPreviousRecordsRunning;
}
else if (currentOpState == OpState.SaveTracingRecordsScheduled)
{
currentOpState = OpState.SaveTracingRecordsRunning;
}
opCompleted = false;
anyError = false;
}
/// <summary>Run this operation</summary>
/// <returns>Event.ResultsWritten or Event.Error</returns>
public Event Run()
{
log.WarnFormat("{0} : Run() : currentOp = {1}", Name, currentOpState);
if (currentOpState == OpState.CheckPreviousRecordsRunning)
{
if (tracingCfg.DebugLevel == DebugMode.Simulate) return Event.InfoRead;
if (opCompleted) return anyError ? Event.InfoNotRead : Event.InfoRead;
/// Run once
opCompleted = true;
if (CheckPreviousRecords(waterMeters) != Retv.OK) anyError = true;
return anyError ? Event.InfoNotRead : Event.InfoRead;
}
if (currentOpState == OpState.SaveTracingRecordsRunning)
{
if (tracingCfg.DebugLevel == DebugMode.Simulate || batch.WaterMeters.Count == 0) return Event.ResultsWritten;
if (opCompleted) return anyError ? Event.ErrorProcessingResults : Event.ResultsWritten;
/// Run once
opCompleted = true;
if (SaveTracingRecords(batch) != Retv.OK) anyError = true;
if (anyError && !string.IsNullOrEmpty(SharedDatabase.EventsDB.ConnectionString))
{
try
{
NHibernate.ISession session = SharedDatabase.EventsDB.CreateSession();
SharedDatabase.EventsDB.LoadSubscribers(session);
TBF.UiBridge.Bridge.TriggerEvent(session, Name, EventClass.ComputerResources, Severity.Error,
string.Format("Nepodarilo sa uložiť výsledky do sledovacej DB: Číslo dávky={0}", batch.BatchNr),
string.Format("Nepodarilo sa uložiť výsledky do sledovacej DB\r\nČíslo dávky = {0}", batch.BatchNr),
SubscriberGroup.Metrology | SubscriberGroup.Maintenance | SubscriberGroup.Production);
}
catch (Exception exc)
{
log.ErrorFormat("Triggering event(s) failed: {0}", exc.Message);
}
}
return anyError ? Event.ErrorProcessingResults : Event.ResultsWritten;
}
return Event.None;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
currentOpState = OpState.None;
}
Retv CheckPreviousRecords(IList<Results.Entities.WaterMeter> waterMeters)
{
Retv retVal = Retv.Error;
var sampleWM = waterMeters.FirstOrDefault<Results.Entities.WaterMeter>(x => x != null && x.Disabled == false);
if (sampleWM != null)
{
using (ISession session = SessionFactory.OpenSession())
{
for (int i = 0; i < waterMeters.Count; i++)
{
Results.Entities.WaterMeter wm = waterMeters[i];
if ((wm != null) && !wm.Disabled)
{
CheckPreviousRecordOfSingleWM(session, wm);
}
}
retVal = Retv.OK; /// Successfully completed (regardless of wm.LastRecordIsNok)
}
}
return retVal; /// Returns Retv.Error if checking not completed successfully
}
Retv CheckPreviousRecordOfSingleWM(ISession session, Results.Entities.WaterMeter wm)
{
if (!string.IsNullOrEmpty(wm.SerialNr) && ProcessData.WorkflowSummary != null)
{
/// Get all reference records with Code == SerialNr from all worksteps different from 'Test_bench'
var refRecords = session.QueryOver<ReferenceRecord>()
.Where(rr => (rr.Code1 == wm.SerialNr))
.List();
if (refRecords.Count == 1)
{
ReferenceRecord refRecord = refRecords[0];
StepRecord previousStep = string.IsNullOrEmpty(ProcessData.WorkflowSummary.PreviousWorkstepName) ? null
: refRecord.StepRecords.FirstOrDefault(x => x.Workstep == ProcessData.WorkflowSummary.PreviousWorkstepName);
if (refRecord.Name1 == ProcessData.WorkflowSummary.Part1Name &&
refRecord.Name2 == ProcessData.WorkflowSummary.Part2Name &&
(ProcessData.WorkflowSummary.PreviousWorkstepName == null ||
(previousStep != null && previousStep.Workstep == ProcessData.WorkflowSummary.PreviousWorkstepName)))
{
wm.Workflow = ProcessData.WorkflowSummary.Workflow.Name;
wm.LastRecordIsNok = false; /// OK
return Retv.OK;
}
}
}
/// S/N is missing OR no records found OR workflows do not match OR previous StepRecord is missing
wm.Workflow = string.Empty;
wm.LastRecordIsNok = true; /// NOK
return Retv.OK;
}
/// <summary>
/// Write results of a batch of water meters to the DB
/// </summary>
/// <param name="session">DB session</param>
/// <param name="batch">Batch entity</param>
Retv SaveTracingRecords(Results.Entities.Batch batch)
{
ITransaction transaction = null;
ISession session = null;
var order = ProcessData.OrderInfo as SharedDatabase.Entities.OrderInfo;
if (order != null && ProcessData.WorkflowSummary != null && !string.IsNullOrEmpty(ProcessData.WorkflowSummary.WorkstepName))
{
///
/// Save to regular tracing DB
///
try
{
session = SessionFactory.OpenSession();
transaction = session.BeginTransaction();
string workplace = (ProcessData.BenchInfo != null) ? ProcessData.BenchInfo.TestBenchName : "TestBench";
int writtenRecordsCount = 0;
foreach (var wm in batch.WaterMeters)
{
if (!wm.Disabled && !string.IsNullOrEmpty(wm.SerialNr))
{
writtenRecordsCount += SaveSingleWM2DB(session, wm, order.POName, ProcessData.WorkflowSummary, workplace);
}
}
transaction.Commit();
session.Flush();
log.WarnFormat("Batch {0}: {1} of {2} watermeters written to the regular production tracing DB",
batch.BatchNr, writtenRecordsCount, batch.WaterMeters.Count);
}
catch (Exception exc)
{
if (transaction != null && !transaction.WasCommitted) transaction.Rollback();
log.ErrorFormat("Failed to write results of batch {0} to production tracing DB: {1}",
batch.BatchNr, exc.Message);
return Retv.Error;
}
finally
{
if (session != null)
{
session.Close();
session.Dispose();
}
}
}
return Retv.OK;
}
/// <summary>
/// Write one meter results to the DB
/// </summary>
/// <param name="session">DB session</param>
/// <param name="wm">Watermeter entity</param>
int SaveSingleWM2DB(ISession session, Results.Entities.WaterMeter wm, string poName, WorkflowSummary wflowSummary, string worplace)
{
if (string.IsNullOrEmpty(wflowSummary.WorkstepName)) return 0;
IList<ReferenceRecord> existingRecords = session.QueryOver<ReferenceRecord>()
.Where(x => (x.Code1 == wm.SerialNr))
.List();
if (existingRecords.Count > 1)
{
/// Unexpected error
log.ErrorFormat("Multiple reference records exist: Code1 = {0}", wm.SerialNr);
return 0;
}
///
/// Reference record
///
ReferenceRecord refRecord = null;
if (existingRecords.Count == 1)
{
/// TODO: Check if the selected worflow is the same as in the found record
refRecord = existingRecords[0];
refRecord.POName = poName;
refRecord.Workflow = wflowSummary.Workflow.Name;
refRecord.Code2 = wm.SerialNrAux;
refRecord.Code3 = wm.CompleteSerialNr;
refRecord.Code4 = wm.RadioAddress;
}
else
{
refRecord = new ReferenceRecord(poName,
wflowSummary.Workflow.Name,
wflowSummary.Part1Name, /// PCB (iPERL) or housing (620/640) SAP number
wm.SerialNr, /// PcbNumber (iPERL) or housing S/N (620/640)
wflowSummary.Part2Name, /// Flowtube SAP nummber (iPERL) or "eRegister#" (640) or empty
wm.SerialNrAux, /// Flowtube S/N (iPERL) or eRegister number (640) or empty
wm.CompleteSerialNr, /// Complete assigned S/N
wm.RadioAddress); /// Radio address (iPERL and 640)
}
///
/// Step record
///
StepRecord stepRecord = new StepRecord(refRecord,
wflowSummary.WorkstepName,
workplace,
Common.CurrentUser.UserName(),
wm.PassedFromTests() ? 0 : 1);
if (refRecord.StepRecords == null)
{
refRecord.StepRecords = new List<StepRecord> { stepRecord };
}
else
{
refRecord.StepRecords.Add(stepRecord);
}
session.SaveOrUpdate(refRecord);
session.SaveOrUpdate(stepRecord);
return 1;
}
}
}
@@ -0,0 +1,25 @@
using System.Collections.Generic;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.RegisterReaders.iPerlASICReader.implementations;
namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
public class Factory: IComponentFactory
{
public string ClassName { get { return this.GetType().Namespace.Substring(8); } }
public override string ToString() { return ClassName; }
public IComponent DummyComponent() { return new GenesisSmartReader(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new GenesisSmartReader(cfg); }
public IComponentCfg DefaultConfig() { return new GenesisCfg(this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(GenesisCfg.Serializer, component, this);
}
}
}
@@ -0,0 +1,73 @@
using System.Collections.Generic;
using System.Xml.Serialization;
using Common;
using Config.Entities;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
public class GenesisCfg : ComponentCfgBase, Generic.IComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(GenesisCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl(IList<Component> cmpntEntities)
{
return new GenesisCfgCtrl();
}
///
/// Serialized parameters
///
public bool UseTcpIP;
public string OptoIPAddress;
public ushort OptoTcpipPortNr;
public int HeadCommunicationComPortNr;
public int OptoComPortNr;
public int RfidComPortNr; /// 0 = use MuxBoardNr
public int MuxBoardNr; /// 0 = use RfidComPort(Nr), otherwise mux. board nr. 1 .. 4
public int Group; /// Number written to QuidoRS to connct the watermeter to RfidComPort, 1 .. 10
public CommunicationInterface CommunicationInterface; /// Communication Interface: RFID or NFC
///
/// <summary> Procedure parameters </summary>
[XmlIgnore]
public ProcParams ProcParams;
public override IParamsProvider GetRuntimeProcParamsProvider() { return ProcParams; }
public override IParamsProvider CreateProcParamsProvider() { return new ProcParams(true); }
[XmlIgnore]
public MeterType MeterType { get { return (ProcParams != null) ? ProcParams.MeterType : MeterType.AutoDetect; } }
/// Private parameterless constructor invoked by all other (public) constructors
GenesisCfg()
{
Name = "Genesis";
ParentName = string.Empty;
OptoComPortNr = 10;
RfidComPortNr = 0; /// = use mux. board
MuxBoardNr = 1;
ProcParams = CreateProcParamsProvider() as ProcParams;
CommunicationInterface = CommunicationInterface.RFID;
HeadCommunicationComPortNr = 0;
}
public GenesisCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return $"{Name} Group1 (mux#)={MuxBoardNr}, Group2={Group}, Opto=Com{OptoComPortNr}, {CommunicationInterface}=Com{RfidComPortNr}";
}
}
}
@@ -0,0 +1,165 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
using System;
using System.Net;
using System.Windows.Forms;
using Common;
using TBF.Resources;
using TBF.Rig.Generic;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
public partial class GenesisCfgCtrl : UserControl, IComponentCfgCtrl
{
public bool ShowMore { get { return false; } }
GenesisCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as GenesisCfg;
Redraw();
}
}
public GenesisCfgCtrl()
{
InitializeComponent();
}
private void WaterMeterCfgCtrl_Load(object sender, EventArgs e)
{
nameLabel.Text = Strings.Name;
classNameLabel.Text = config.Factory.ClassName;
Redraw();
}
public void Closing()
{
}
void Redraw()
{
if (config == null) return; /// Control was not loaded, settings were not changed
nameTextBox.Text = config.Name;
radioButton1.Checked = config.UseTcpIP;
radioButton2.Checked = !config.UseTcpIP;
ipAddressTextBox.Text = (config.OptoIPAddress != null) ? config.OptoIPAddress : "0.0.0.0";
tcpipPortTextBox.Text = config.OptoTcpipPortNr.ToString();
headPortNrTextBox.Text = config.HeadCommunicationComPortNr.ToString();
optoSerialPortTextBox.Text = config.OptoComPortNr.ToString();
rfidPortNrTextBox.Text = config.RfidComPortNr.ToString();
muxBoardNrTextBox.Text = config.MuxBoardNr.ToString();
groupTextBox.Text = config.Group.ToString();
comboBoxCommunicationInterface.SelectedItem = config.CommunicationInterface.ToString();
tabPage2.Controls.Add(new GenesisHeadTestCtrl(config));
}
public void Unlock()
{
nameTextBox.Enabled = true;
radioButton1.Enabled = true;
radioButton2.Enabled = true;
ipAddressTextBox.Enabled = true;
tcpipPortTextBox.Enabled = true;
optoSerialPortTextBox.Enabled = true;
rfidPortNrTextBox.Enabled = true;
headPortNrTextBox.Enabled = true;
muxBoardNrTextBox.Enabled = true;
groupTextBox.Enabled = true;
comboBoxCommunicationInterface.Enabled = true;
}
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (radioButton1.Checked)
{
IPAddress dummyIPAddress;
if (!IPAddress.TryParse(ipAddressTextBox.Text, out dummyIPAddress))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'IP address' is not valid";
}
ushort sdummy;
if (!ushort.TryParse(tcpipPortTextBox.Text, out sdummy))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'TCP/IP port nr.' is not valid";
}
}
else
{
if (!int.TryParse(optoSerialPortTextBox.Text, out dummy) || dummy < 1 || dummy > 999)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Opto serial port nr.' is not valid";
}
}
if (!int.TryParse(rfidPortNrTextBox.Text, out dummy) || dummy < 0 || dummy > 999)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'RFID serial port nr.' is not valid";
}
if (!int.TryParse(headPortNrTextBox.Text, out dummy) || dummy < 0 || dummy > 999)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Head communication serial port nr.' is not valid";
}
if (!int.TryParse(muxBoardNrTextBox.Text, out dummy) || dummy < 1 || dummy > 4)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + string.Format(Strings.Invalid_0, muxBoardNrLabel.Text);
}
if (!int.TryParse(groupTextBox.Text, out dummy) || dummy < 1 || dummy > 10)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + string.Format(Strings.Invalid_0, groupLabel.Text);
}
return flags;
}
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
config.Name = nameTextBox.Text;
if (radioButton1.Checked)
{
config.UseTcpIP = true;
config.OptoIPAddress = ipAddressTextBox.Text;
config.OptoTcpipPortNr = ushort.Parse(tcpipPortTextBox.Text);
}
else
{
config.UseTcpIP = false;
config.OptoComPortNr = int.Parse(optoSerialPortTextBox.Text);
}
config.RfidComPortNr = int.Parse(rfidPortNrTextBox.Text);
config.MuxBoardNr = int.Parse(muxBoardNrTextBox.Text);
config.Group = int.Parse(groupTextBox.Text);
config.CommunicationInterface = (CommunicationInterface)comboBoxCommunicationInterface.SelectedIndex;
config.HeadCommunicationComPortNr = int.Parse(headPortNrTextBox.Text);
return flags;
}
}
}
@@ -0,0 +1,441 @@
///
/// Copyright (c) 2015-2017 Sensus Metering Systems
///
namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
partial class GenesisCfgCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.tabControl1 = new System.Windows.Forms.TabControl();
this.tabPage1 = new System.Windows.Forms.TabPage();
this.label4 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this.comboBoxCommunicationInterface = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.rfidPortNrTextBox = new System.Windows.Forms.TextBox();
this.rfidSerialPortNrLabel = new System.Windows.Forms.Label();
this.optoDataGroupBox = new System.Windows.Forms.GroupBox();
this.tcpipPortLabel = new System.Windows.Forms.Label();
this.tcpipPortTextBox = new System.Windows.Forms.TextBox();
this.ipAddressLabel = new System.Windows.Forms.Label();
this.ipAddressTextBox = new System.Windows.Forms.TextBox();
this.radioButton1 = new System.Windows.Forms.RadioButton();
this.radioButton2 = new System.Windows.Forms.RadioButton();
this.optoSerialPortLabel = new System.Windows.Forms.Label();
this.optoSerialPortTextBox = new System.Windows.Forms.TextBox();
this.groupTextBox = new System.Windows.Forms.TextBox();
this.groupLabel = new System.Windows.Forms.Label();
this.muxBoardNrTextBox = new System.Windows.Forms.TextBox();
this.muxBoardNrLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.nameLabel = new System.Windows.Forms.Label();
this.classNameLabel = new System.Windows.Forms.Label();
this.tabPage2 = new System.Windows.Forms.TabPage();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this.label2 = new System.Windows.Forms.Label();
this.headPortNrTextBox = new System.Windows.Forms.TextBox();
this.tabControl1.SuspendLayout();
this.tabPage1.SuspendLayout();
this.groupBox1.SuspendLayout();
this.optoDataGroupBox.SuspendLayout();
this.groupBox2.SuspendLayout();
this.SuspendLayout();
//
// tabControl1
//
this.tabControl1.Controls.Add(this.tabPage1);
this.tabControl1.Controls.Add(this.tabPage2);
this.tabControl1.Location = new System.Drawing.Point(3, 3);
this.tabControl1.Name = "tabControl1";
this.tabControl1.SelectedIndex = 0;
this.tabControl1.Size = new System.Drawing.Size(611, 432);
this.tabControl1.TabIndex = 0;
//
// tabPage1
//
this.tabPage1.Controls.Add(this.groupBox2);
this.tabPage1.Controls.Add(this.label4);
this.tabPage1.Controls.Add(this.label3);
this.tabPage1.Controls.Add(this.groupBox1);
this.tabPage1.Controls.Add(this.optoDataGroupBox);
this.tabPage1.Controls.Add(this.groupTextBox);
this.tabPage1.Controls.Add(this.groupLabel);
this.tabPage1.Controls.Add(this.muxBoardNrTextBox);
this.tabPage1.Controls.Add(this.muxBoardNrLabel);
this.tabPage1.Controls.Add(this.nameTextBox);
this.tabPage1.Controls.Add(this.nameLabel);
this.tabPage1.Controls.Add(this.classNameLabel);
this.tabPage1.Location = new System.Drawing.Point(4, 25);
this.tabPage1.Name = "tabPage1";
this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
this.tabPage1.Size = new System.Drawing.Size(603, 403);
this.tabPage1.TabIndex = 0;
this.tabPage1.Text = "Config";
this.tabPage1.UseVisualStyleBackColor = true;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(208, 101);
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(40, 16);
this.label4.TabIndex = 25;
this.label4.Text = "1 .. 10";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(208, 72);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(33, 16);
this.label3.TabIndex = 24;
this.label3.Text = "1 .. 4";
//
// groupBox1
//
this.groupBox1.Controls.Add(this.comboBoxCommunicationInterface);
this.groupBox1.Controls.Add(this.label1);
this.groupBox1.Controls.Add(this.rfidPortNrTextBox);
this.groupBox1.Controls.Add(this.rfidSerialPortNrLabel);
this.groupBox1.Location = new System.Drawing.Point(10, 259);
this.groupBox1.Margin = new System.Windows.Forms.Padding(4);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Padding = new System.Windows.Forms.Padding(4);
this.groupBox1.Size = new System.Drawing.Size(552, 68);
this.groupBox1.TabIndex = 23;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "RFID / NFC communication (in case mux. board is not used)";
//
// comboBoxCommunicationInterface
//
this.comboBoxCommunicationInterface.Enabled = false;
this.comboBoxCommunicationInterface.FormattingEnabled = true;
this.comboBoxCommunicationInterface.Items.AddRange(new object[] {
"RFID",
"NFC"});
this.comboBoxCommunicationInterface.Location = new System.Drawing.Point(201, 27);
this.comboBoxCommunicationInterface.Name = "comboBoxCommunicationInterface";
this.comboBoxCommunicationInterface.Size = new System.Drawing.Size(71, 24);
this.comboBoxCommunicationInterface.TabIndex = 9;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(41, 30);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(153, 16);
this.label1.TabIndex = 8;
this.label1.Text = "Communication Interface";
//
// rfidPortNrTextBox
//
this.rfidPortNrTextBox.Enabled = false;
this.rfidPortNrTextBox.Location = new System.Drawing.Point(439, 26);
this.rfidPortNrTextBox.Margin = new System.Windows.Forms.Padding(4);
this.rfidPortNrTextBox.Name = "rfidPortNrTextBox";
this.rfidPortNrTextBox.Size = new System.Drawing.Size(44, 22);
this.rfidPortNrTextBox.TabIndex = 7;
//
// rfidSerialPortNrLabel
//
this.rfidSerialPortNrLabel.AutoSize = true;
this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(321, 30);
this.rfidSerialPortNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel";
this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(88, 16);
this.rfidSerialPortNrLabel.TabIndex = 6;
this.rfidSerialPortNrLabel.Text = "Serial port nr.:";
//
// optoDataGroupBox
//
this.optoDataGroupBox.Controls.Add(this.tcpipPortLabel);
this.optoDataGroupBox.Controls.Add(this.tcpipPortTextBox);
this.optoDataGroupBox.Controls.Add(this.ipAddressLabel);
this.optoDataGroupBox.Controls.Add(this.ipAddressTextBox);
this.optoDataGroupBox.Controls.Add(this.radioButton1);
this.optoDataGroupBox.Controls.Add(this.radioButton2);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortLabel);
this.optoDataGroupBox.Controls.Add(this.optoSerialPortTextBox);
this.optoDataGroupBox.Location = new System.Drawing.Point(10, 131);
this.optoDataGroupBox.Margin = new System.Windows.Forms.Padding(4);
this.optoDataGroupBox.Name = "optoDataGroupBox";
this.optoDataGroupBox.Padding = new System.Windows.Forms.Padding(4);
this.optoDataGroupBox.Size = new System.Drawing.Size(552, 119);
this.optoDataGroupBox.TabIndex = 18;
this.optoDataGroupBox.TabStop = false;
this.optoDataGroupBox.Text = "Opto-data";
//
// tcpipPortLabel
//
this.tcpipPortLabel.AutoSize = true;
this.tcpipPortLabel.Location = new System.Drawing.Point(41, 87);
this.tcpipPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.tcpipPortLabel.Name = "tcpipPortLabel";
this.tcpipPortLabel.Size = new System.Drawing.Size(54, 16);
this.tcpipPortLabel.TabIndex = 4;
this.tcpipPortLabel.Text = "Port nr..:";
//
// tcpipPortTextBox
//
this.tcpipPortTextBox.Enabled = false;
this.tcpipPortTextBox.Location = new System.Drawing.Point(143, 84);
this.tcpipPortTextBox.Margin = new System.Windows.Forms.Padding(4);
this.tcpipPortTextBox.Name = "tcpipPortTextBox";
this.tcpipPortTextBox.Size = new System.Drawing.Size(51, 22);
this.tcpipPortTextBox.TabIndex = 5;
//
// ipAddressLabel
//
this.ipAddressLabel.AutoSize = true;
this.ipAddressLabel.Location = new System.Drawing.Point(41, 59);
this.ipAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.ipAddressLabel.Name = "ipAddressLabel";
this.ipAddressLabel.Size = new System.Drawing.Size(78, 16);
this.ipAddressLabel.TabIndex = 2;
this.ipAddressLabel.Text = "IP address.:";
//
// ipAddressTextBox
//
this.ipAddressTextBox.Enabled = false;
this.ipAddressTextBox.Location = new System.Drawing.Point(143, 55);
this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4);
this.ipAddressTextBox.Name = "ipAddressTextBox";
this.ipAddressTextBox.Size = new System.Drawing.Size(129, 22);
this.ipAddressTextBox.TabIndex = 3;
//
// radioButton1
//
this.radioButton1.AutoSize = true;
this.radioButton1.Checked = true;
this.radioButton1.Enabled = false;
this.radioButton1.Location = new System.Drawing.Point(29, 23);
this.radioButton1.Margin = new System.Windows.Forms.Padding(4);
this.radioButton1.Name = "radioButton1";
this.radioButton1.Size = new System.Drawing.Size(99, 20);
this.radioButton1.TabIndex = 0;
this.radioButton1.TabStop = true;
this.radioButton1.Text = "Use TCP/IP";
this.radioButton1.UseVisualStyleBackColor = true;
//
// radioButton2
//
this.radioButton2.AutoSize = true;
this.radioButton2.Enabled = false;
this.radioButton2.Location = new System.Drawing.Point(312, 23);
this.radioButton2.Margin = new System.Windows.Forms.Padding(4);
this.radioButton2.Name = "radioButton2";
this.radioButton2.Size = new System.Drawing.Size(115, 20);
this.radioButton2.TabIndex = 1;
this.radioButton2.Text = "Use serial port";
this.radioButton2.UseVisualStyleBackColor = true;
//
// optoSerialPortLabel
//
this.optoSerialPortLabel.AutoSize = true;
this.optoSerialPortLabel.Location = new System.Drawing.Point(321, 55);
this.optoSerialPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.optoSerialPortLabel.Name = "optoSerialPortLabel";
this.optoSerialPortLabel.Size = new System.Drawing.Size(88, 16);
this.optoSerialPortLabel.TabIndex = 6;
this.optoSerialPortLabel.Text = "Serial port nr.:";
//
// optoSerialPortTextBox
//
this.optoSerialPortTextBox.Enabled = false;
this.optoSerialPortTextBox.Location = new System.Drawing.Point(439, 52);
this.optoSerialPortTextBox.Margin = new System.Windows.Forms.Padding(4);
this.optoSerialPortTextBox.Name = "optoSerialPortTextBox";
this.optoSerialPortTextBox.Size = new System.Drawing.Size(44, 22);
this.optoSerialPortTextBox.TabIndex = 7;
//
// groupTextBox
//
this.groupTextBox.Enabled = false;
this.groupTextBox.Location = new System.Drawing.Point(153, 97);
this.groupTextBox.Margin = new System.Windows.Forms.Padding(4);
this.groupTextBox.Name = "groupTextBox";
this.groupTextBox.Size = new System.Drawing.Size(44, 22);
this.groupTextBox.TabIndex = 22;
//
// groupLabel
//
this.groupLabel.AutoSize = true;
this.groupLabel.Location = new System.Drawing.Point(6, 101);
this.groupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.groupLabel.Name = "groupLabel";
this.groupLabel.Size = new System.Drawing.Size(54, 16);
this.groupLabel.TabIndex = 21;
this.groupLabel.Text = "Group 2";
//
// muxBoardNrTextBox
//
this.muxBoardNrTextBox.Enabled = false;
this.muxBoardNrTextBox.Location = new System.Drawing.Point(153, 69);
this.muxBoardNrTextBox.Margin = new System.Windows.Forms.Padding(4);
this.muxBoardNrTextBox.Name = "muxBoardNrTextBox";
this.muxBoardNrTextBox.Size = new System.Drawing.Size(44, 22);
this.muxBoardNrTextBox.TabIndex = 20;
//
// muxBoardNrLabel
//
this.muxBoardNrLabel.AutoSize = true;
this.muxBoardNrLabel.Location = new System.Drawing.Point(6, 72);
this.muxBoardNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.muxBoardNrLabel.Name = "muxBoardNrLabel";
this.muxBoardNrLabel.Size = new System.Drawing.Size(131, 16);
this.muxBoardNrLabel.TabIndex = 19;
this.muxBoardNrLabel.Text = "Group 1 (mux. board)";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(153, 40);
this.nameTextBox.Margin = new System.Windows.Forms.Padding(4);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(160, 22);
this.nameTextBox.TabIndex = 17;
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(6, 44);
this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(44, 16);
this.nameLabel.TabIndex = 16;
this.nameLabel.Text = "Name";
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(149, 11);
this.classNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(78, 16);
this.classNameLabel.TabIndex = 15;
this.classNameLabel.Text = "ClassName";
//
// tabPage2
//
this.tabPage2.Location = new System.Drawing.Point(4, 25);
this.tabPage2.Name = "tabPage2";
this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
this.tabPage2.Size = new System.Drawing.Size(603, 403);
this.tabPage2.TabIndex = 1;
this.tabPage2.Text = "Test";
this.tabPage2.UseVisualStyleBackColor = true;
//
// groupBox2
//
this.groupBox2.Controls.Add(this.headPortNrTextBox);
this.groupBox2.Controls.Add(this.label2);
this.groupBox2.Location = new System.Drawing.Point(10, 335);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(552, 50);
this.groupBox2.TabIndex = 26;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Head Communication";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(321, 18);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(88, 16);
this.label2.TabIndex = 7;
this.label2.Text = "Serial port nr.:";
//
// headPortNrTextBox
//
this.headPortNrTextBox.Enabled = false;
this.headPortNrTextBox.Location = new System.Drawing.Point(439, 15);
this.headPortNrTextBox.Margin = new System.Windows.Forms.Padding(4);
this.headPortNrTextBox.Name = "headPortNrTextBox";
this.headPortNrTextBox.Size = new System.Drawing.Size(44, 22);
this.headPortNrTextBox.TabIndex = 8;
//
// IperlHeadCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tabControl1);
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "GenesisCfgCtrl";
this.Size = new System.Drawing.Size(617, 438);
this.Load += new System.EventHandler(this.WaterMeterCfgCtrl_Load);
this.tabControl1.ResumeLayout(false);
this.tabPage1.ResumeLayout(false);
this.tabPage1.PerformLayout();
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.optoDataGroupBox.ResumeLayout(false);
this.optoDataGroupBox.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TabControl tabControl1;
private System.Windows.Forms.TabPage tabPage1;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.ComboBox comboBoxCommunicationInterface;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox rfidPortNrTextBox;
private System.Windows.Forms.Label rfidSerialPortNrLabel;
private System.Windows.Forms.GroupBox optoDataGroupBox;
private System.Windows.Forms.Label tcpipPortLabel;
private System.Windows.Forms.TextBox tcpipPortTextBox;
private System.Windows.Forms.Label ipAddressLabel;
private System.Windows.Forms.TextBox ipAddressTextBox;
private System.Windows.Forms.RadioButton radioButton1;
private System.Windows.Forms.RadioButton radioButton2;
private System.Windows.Forms.Label optoSerialPortLabel;
private System.Windows.Forms.TextBox optoSerialPortTextBox;
private System.Windows.Forms.TextBox groupTextBox;
private System.Windows.Forms.Label groupLabel;
private System.Windows.Forms.TextBox muxBoardNrTextBox;
private System.Windows.Forms.Label muxBoardNrLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.TabPage tabPage2;
private System.Windows.Forms.GroupBox groupBox2;
private System.Windows.Forms.TextBox headPortNrTextBox;
private System.Windows.Forms.Label label2;
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,137 @@
namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
partial class GenesisHeadTestCtrl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.optoTestGroupBox = new System.Windows.Forms.GroupBox();
this.optoListBox = new System.Windows.Forms.ListBox();
this.rfidOutputListBox = new System.Windows.Forms.ListBox();
this.RfidTestGroupBox = new System.Windows.Forms.GroupBox();
this.label2 = new System.Windows.Forms.Label();
this.rfidCommandComboBox = new System.Windows.Forms.ComboBox();
this.commandTestButton = new System.Windows.Forms.Button();
this.optoTestGroupBox.SuspendLayout();
this.RfidTestGroupBox.SuspendLayout();
this.SuspendLayout();
//
// optoTestGroupBox
//
this.optoTestGroupBox.Controls.Add(this.optoListBox);
this.optoTestGroupBox.Location = new System.Drawing.Point(5, 4);
this.optoTestGroupBox.Name = "optoTestGroupBox";
this.optoTestGroupBox.Size = new System.Drawing.Size(591, 161);
this.optoTestGroupBox.TabIndex = 2;
this.optoTestGroupBox.TabStop = false;
this.optoTestGroupBox.Text = "Opto-data";
//
// optoListBox
//
this.optoListBox.FormattingEnabled = true;
this.optoListBox.ItemHeight = 16;
this.optoListBox.Location = new System.Drawing.Point(7, 22);
this.optoListBox.Name = "optoListBox";
this.optoListBox.Size = new System.Drawing.Size(573, 132);
this.optoListBox.TabIndex = 0;
//
// rfidOutputListBox
//
this.rfidOutputListBox.FormattingEnabled = true;
this.rfidOutputListBox.ItemHeight = 16;
this.rfidOutputListBox.Location = new System.Drawing.Point(5, 54);
this.rfidOutputListBox.Name = "rfidOutputListBox";
this.rfidOutputListBox.SelectionMode = System.Windows.Forms.SelectionMode.None;
this.rfidOutputListBox.Size = new System.Drawing.Size(575, 164);
this.rfidOutputListBox.TabIndex = 3;
//
// RfidTestGroupBox
//
this.RfidTestGroupBox.Controls.Add(this.rfidOutputListBox);
this.RfidTestGroupBox.Controls.Add(this.label2);
this.RfidTestGroupBox.Controls.Add(this.rfidCommandComboBox);
this.RfidTestGroupBox.Controls.Add(this.commandTestButton);
this.RfidTestGroupBox.Location = new System.Drawing.Point(5, 171);
this.RfidTestGroupBox.Name = "RfidTestGroupBox";
this.RfidTestGroupBox.Size = new System.Drawing.Size(591, 224);
this.RfidTestGroupBox.TabIndex = 3;
this.RfidTestGroupBox.TabStop = false;
this.RfidTestGroupBox.Text = "RFID / NFC data";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(2, 25);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(69, 16);
this.label2.TabIndex = 2;
this.label2.Text = "Command";
//
// rfidCommandComboBox
//
this.rfidCommandComboBox.FormattingEnabled = true;
this.rfidCommandComboBox.Location = new System.Drawing.Point(86, 19);
this.rfidCommandComboBox.Name = "rfidCommandComboBox";
this.rfidCommandComboBox.Size = new System.Drawing.Size(341, 24);
this.rfidCommandComboBox.TabIndex = 1;
//
// commandTestButton
//
this.commandTestButton.Location = new System.Drawing.Point(449, 19);
this.commandTestButton.Name = "commandTestButton";
this.commandTestButton.Size = new System.Drawing.Size(126, 24);
this.commandTestButton.TabIndex = 0;
this.commandTestButton.Text = "Send command";
this.commandTestButton.UseVisualStyleBackColor = true;
this.commandTestButton.MouseClick += new System.Windows.Forms.MouseEventHandler(this.CommandTestButtonClick);
//
// IperlHeadTestCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.optoTestGroupBox);
this.Controls.Add(this.RfidTestGroupBox);
this.Name = "GenesisHeadTestCtrl";
this.Size = new System.Drawing.Size(611, 432);
this.Load += new System.EventHandler(this.UserControl_Load);
this.optoTestGroupBox.ResumeLayout(false);
this.RfidTestGroupBox.ResumeLayout(false);
this.RfidTestGroupBox.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox optoTestGroupBox;
private System.Windows.Forms.ListBox rfidOutputListBox;
private System.Windows.Forms.GroupBox RfidTestGroupBox;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.ComboBox rfidCommandComboBox;
private System.Windows.Forms.Button commandTestButton;
private System.Windows.Forms.ListBox optoListBox;
}
}
@@ -0,0 +1,90 @@
using System;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using TBF.Rig.RegisterReaders.GenesisRegReader.implementations;
using TBF.Rig.RegisterReaders.iPerlReaderUNI.common;
using TBF.Rig.Sequences;
using TBF.Rig.Uni.SharedDialogs.SmartMetersCommunication;
namespace TBF.Rig.RegisterReaders.GenesisRegReader
{
public partial class GenesisHeadTestCtrl : UserControl
{
private IUniHeadTestCtrl<OptoReceivedEventArgs> _ctrl;
private IUniHeadTestCtrl<OptoReceivedEventArgs> Ctrl { get => _ctrl; }
private GenesisCfg _genesisCfg;
private GenesisSmartReader _genesisSmartReader;
Thread optoThread;
private bool stopWorkerThread;
public GenesisHeadTestCtrl(GenesisCfg config)
{
this._ctrl = new GenesisImplHeadTestCtrl();
_genesisCfg = config;
Ctrl.config = config;
InitializeComponent();
if (config == null) return;
//SmartCommunicationForm.TestMethodCfg = new TestMethodCfg(null); // default values for iPerlCommunication
foreach(var head in ProcessData.SmartHeadsUni)
{
if (head != null && head.Name == config.Name) { Ctrl.ISmartReader = head; }
}
rfidCommandComboBox.DisplayMember = "Name";
rfidCommandComboBox.ValueMember = "Value";
var items = Ctrl.GetComboOperationsPairs();
/*
// CTRL+ALT+double click - hidden poweruser menu
if (((Keyboard.ModifierKeys & Keys.Control) == Keys.Control) && ((Keyboard.ModifierKeys & Keys.Alt) == Keys.Alt) && Users.CurrentUser.AuthorizedAs == AuthorizedAs.PowerUser)
{
Array.Resize(ref items, items.Length + 1);
items[items.Length - 1] = new { Name = "Kluc", Value = "Kluc" };
}
*/
rfidCommandComboBox.DataSource = items.Select(i => i.Name).ToArray();
Ctrl.Initialize();
Ctrl.OptoReceivedHandler += (EventHandler<OptoReceivedEventArgs>)((sndr, args) =>
{
if (this.InvokeRequired)
this.Invoke((Delegate)new EventHandler<OptoReceivedEventArgs>(this.OnOptoReceived2), sndr, (object)args);
else
this.OnOptoReceived2(sndr, args);
});
}
public void OnOptoReceived2(object sender, OptoReceivedEventArgs args)
{
optoListBox.Items.Insert(0,args.Data);
}
private void CommandTestButtonClick(object sender, MouseEventArgs e)
{
Ctrl.CommandTestButtonClick(sender, e, new Arguments()
{
ISmartReader = Ctrl.ISmartReader,
OptoListBox = optoListBox,
RfidCommandComboBox = rfidCommandComboBox,
RfidOutputListBox = rfidOutputListBox
});
}
private void UserControl_Load(object sender, EventArgs e)
{
this.ParentForm.FormClosing += new FormClosingEventHandler(ParentForm_FormClosing);
}
void ParentForm_FormClosing(object sender, FormClosingEventArgs e)
{
//OnHandleDestroyed(new EventArgs());
Ctrl.Destroy();
}
}
}
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,175 @@
using System;
using System.Globalization;
using System.Linq;
using System.Text;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
{
public static class HexFormatter
{
/// <summary>
/// Byte to hex string.
/// Formats a single byte as 0xNN.
/// Example: 0x0D
/// </summary>
public static string ToHex(byte value)
{
return "0x" + value.ToString("X2");
}
/// <summary>
/// int to byte - securely
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static byte ToHexByte(int value)
{
if (value < 0 || value > 255)
throw new ArgumentOutOfRangeException(nameof(value),
"Value must be between 0 and 255.");
return (byte)value;
}
/// <summary>
/// Formats a byte array as 0xNN 0xNN ...
/// </summary>
public static string ToHex(byte[] data)
{
if (data == null || data.Length == 0)
return "<empty>";
var sb = new System.Text.StringBuilder();
for (int i = 0; i < data.Length; i++)
{
if (i > 0)
sb.Append(' ');
sb.Append("0x");
sb.Append(data[i].ToString("X2"));
}
return sb.ToString();
}
/// <summary>
/// Formats a byte array exactly as shown in serial terminals.
/// Example: "0D 04 08 01 00 1A"
/// </summary>
public static string ToSerialHex(byte[] data)
{
if (data == null || data.Length == 0)
return string.Empty;
var sb = new System.Text.StringBuilder();
for (int i = 0; i < data.Length; i++)
{
if (i > 0)
sb.Append(' ');
sb.Append(data[i].ToString("X2"));
}
return sb.ToString();
}
public static string ToHexWithAscii(byte value)
{
char c = (value >= 32 && value <= 126) ? (char)value : '.';
return $"0x{value:X2} ('{c}')";
}
public static string ToSerialHexWithAscii(byte[] data)
{
if (data == null || data.Length == 0)
return string.Empty;
var hex = new StringBuilder(data.Length * 3);
var ascii = new StringBuilder(data.Length);
foreach (byte b in data)
{
hex.Append(b.ToString("X2")).Append(' ');
// Printable ASCII range
if (b >= 32 && b <= 126)
{
ascii.Append((char)b);
}
// Binary numbers 09 -> show digit
else if (b <= 9)
{
ascii.Append((char)('0' + b));
}
else
{
ascii.Append('.');
}
}
// remove last trailing space in hex
if (hex.Length > 0)
hex.Length--;
return $"{hex} | {ascii}";
}
public static string ToHex(int value)
{
return $"0x{(byte)value:X2}";
}
public static byte[] IntToBytesBE(int value, int byteCount)
{
var result = new byte[byteCount];
for (int i = 0; i < byteCount; i++)
result[byteCount - 1 - i] = (byte)(value >> (8 * i));
return result;
}
public static byte[] IntToBytesLE(int value, int byteCount)
{
var result = new byte[byteCount];
for (int i = 0; i < byteCount; i++)
result[i] = (byte)(value >> (8 * i));
return result;
}
public static byte[] AsciiToBytes(string text)
{
return string.IsNullOrEmpty(text)
? Array.Empty<byte>()
: System.Text.Encoding.ASCII.GetBytes(text);
}
/// <summary>
/// Converts a hex string to a byte array.
/// Like: string hex = "3F 76 65 72 73 3A 20 48 61 72 72 79 20 54 3A 42 38 30 30 2C 20";
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static byte[] HexStringToByteArray(string hex)
{
if (hex == null)
throw new ArgumentNullException(nameof(hex));
return hex
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(b => byte.Parse(b, NumberStyles.HexNumber, CultureInfo.InvariantCulture))
.ToArray();
}
}
}
@@ -0,0 +1,353 @@
///
/// Copyright (c) 2015-2021 Sensus Metering Systems
///
using System;
using System.Globalization;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.common
{
public enum OptoTelegramFlags : byte
{
OK = 0,
OK_TestStart,
OK_TestEnd,
InvalidTelegram, /// Wrong telegram format of checksum error
SyncError,
}
public class OptoTelegramRaw
{
public static readonly int Length = 42;
private static CultureInfo culture;
///
/// Strobed value
///
public static decimal TestStartTimestampDec;
///
/// Stored values
///
public OptoTelegramFlags Flags;
public DateTime DateTime;
/// From PC
public float RefFlow;
/// [m3/h]
public int Counter;
public Int32 EmfRaw;
/// Signed EMF from iPerl opto data
public Int16 MagneticFieldRaw;
public Int16 FlowRaw;
public double VolumeRaw;
public double VolumeRawExt;
public Int16 Impedance;
public double Timestamp;
public double TimestampExt;
public byte CheckSum;
public int iChannel = -1;
public int IChannel()
{
return iChannel;
}
///
/// Calculated values
///
public double EMF()
{
return 0.000000333 * (double)EmfRaw;
}
public double MagneticField()
{
return (double)MagneticFieldRaw;
}
public double Flow(double scalingFactor)
{
return 0.225 * scalingFactor * (double)FlowRaw;
}
public double Volume(double scalingFactor)
{
return 0.0000625 * scalingFactor * (double)VolumeRawExt;
}
public Int32 FlipTime()
{
return Impedance;
}
public decimal TimestampDec()
{
return (decimal)TimestampExt / (decimal)8192;
}
public double VolumeDelta(double scalingFactor, OptoTelegramRaw previous)
{
return (previous == null) ? 0 : Volume(scalingFactor) - previous.Volume(scalingFactor);
}
public decimal TimeDelta()
{
return TimestampDec() - TestStartTimestampDec;
}
public string Label()
{
if (Flags == OptoTelegramFlags.OK_TestStart) return "#### start test ####";
else if (Flags == OptoTelegramFlags.OK_TestEnd) return "#### end of test ####";
else return string.Empty;
}
static OptoTelegramRaw()
{
culture = CultureInfo.CreateSpecificCulture("DE"); /// This is to use comma as decimal number separator
}
public OptoTelegramRaw()
{
}
// -------- TIMESTAMP (seconds) --------
private const double TS_RANGE = StreamingDecoder.CpuTimeOverflowS;
// -------- VOLUME (liters) --------
private const double VOL_RANGE = StreamingDecoder.DefaultAccuDutOverflowVolumeCm * 1000;
/// <summary>
/// update data by CalibrationRecord
/// </summary>
/// <param name="data"></param>
/// <param name="counter"></param>
/// <param name="refFlow"></param>
/// <param name="volumeRawExtLast"></param>
/// <param name="timestampExtLast"></param>
/// <exception cref="ArgumentNullException"></exception>
public void UpdateFromSmart(
CalibrationRecord data,
int counter,
float refFlow,
ref double volumeRawExtLast,
ref double timestampExtLast)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
}
/// <summary>
/// /// update data by FlowTestRecord
/// </summary>
/// <param name="data"></param>
/// <param name="counter"></param>
/// <param name="refFlow"></param>
/// <param name="volumeRawExtLast"></param>
/// <param name="timestampExtLast"></param>
/// <exception cref="ArgumentNullException"></exception>
public void UpdateFromSmart(
FlowTestRecord data,
int counter,
float refFlow,
ref double volumeRawExtLast,
ref double timestampExtLast)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
UpdateData( data.Channel, data.VolumeCm, data.TimeS, counter, refFlow, ref volumeRawExtLast, ref timestampExtLast);
}
/// <summary>
/// update data by OptoTelegramRaw
/// </summary>
/// <param name="channel"></param>
/// <param name="volumeCm"></param>
/// <param name="timeS"></param>
/// <param name="counter"></param>
/// <param name="refFlow"></param>
/// <param name="volumeRawExtLast"></param>
/// <param name="timestampExtLast"></param>
private void UpdateData(
int channel,
double volumeCm,
double timeS,
int counter,
float refFlow,
ref double volumeRawExtLast,
ref double timestampExtLast)
{
iChannel = channel - 1;
DateTime = DateTime.Now;
Counter = counter;
RefFlow = refFlow;
Flags = OptoTelegramFlags.OK;
FlowRaw = 0;
VolumeRaw = volumeCm * 1000.0; // liters
CheckSum = 0;
Impedance = 0;
EmfRaw = 0;
MagneticFieldRaw = 0;
double ts = NormalizeTimestamp(timeS);
Timestamp = ts;
VolumeRawExt = UnwrapVolume(VolumeRaw, ref volumeRawExtLast);
TimestampExt = UnwrapTimestamp(ts, ref timestampExtLast);
}
private static double NormalizeTimestamp(double timeS)
{
double ts = timeS % TS_RANGE;
if (ts < 0)
ts += TS_RANGE;
return ts;
}
private static double UnwrapVolume(double currentVolume, ref double volumeRawExtLast)
{
double result;
if (double.IsNaN(volumeRawExtLast))
{
result = currentVolume;
}
else
{
result = currentVolume < volumeRawExtLast
? currentVolume + VOL_RANGE
: currentVolume;
}
volumeRawExtLast = result;
return result;
}
private static double UnwrapTimestamp(double currentTimestamp, ref double timestampExtLast)
{
double result;
if (double.IsNaN(timestampExtLast))
{
result = currentTimestamp;
}
else
{
double lastMod = timestampExtLast % TS_RANGE;
if (lastMod < 0)
lastMod += TS_RANGE;
double delta = currentTimestamp - lastMod;
if (delta < -TS_RANGE / 2.0)
delta += TS_RANGE;
else if (delta > TS_RANGE / 2.0)
delta -= TS_RANGE;
result = timestampExtLast + delta;
}
timestampExtLast = result;
return result;
}
/// <summary>
/// Alternative to UpdateFromString(...) when data are flushed
/// </summary>
public bool UpdateFromStringDummy(string telegram)
{
DateTime = DateTime.Now;
RefFlow = 0;
if ((telegram == null) || (telegram.Length < Length) ||
(telegram[6] != '\t') || (telegram[11] != '\t') || (telegram[16] != '\t') ||
(telegram[23] != '\t') || (telegram[28] != '\t') || (telegram[37] != '\t') ||
(telegram[40] != '\r') || (telegram[41] != '\n'))
{
Flags = OptoTelegramFlags.InvalidTelegram;
return false;
}
bool f7 = byte.TryParse(telegram.Substring(38, 2), NumberStyles.HexNumber, CultureInfo.CurrentCulture,
out CheckSum);
byte calculatedCheckSum = 0;
for (int i = 0; i < Length - 4; i++)
{
calculatedCheckSum += (byte)telegram[i];
}
bool allOk = f7 && (calculatedCheckSum == CheckSum);
Flags = allOk ? OptoTelegramFlags.OK : OptoTelegramFlags.InvalidTelegram;
return allOk;
}
public void SetFlags(OptoTelegramFlags flags)
{
this.Flags = flags;
}
public string ToString(double scalingFactor, OptoTelegramRaw previous)
{
if (Flags == OptoTelegramFlags.SyncError)
{
return "Sychronization error";
}
else if (Flags == OptoTelegramFlags.InvalidTelegram)
{
return "Invalid telegram";
}
else /// if (flags == OptoTelegramFlags.OK / OptoTelegramFlags.OK_TestStart / OptoTelegramFlags.OK_TestEnd)
{
return string.Format(
"{0}:{1}:{2}.{3}\t{4} :\t{5}\t{6}\t{7}\t{8}\t{9}\t{10}\t{11}\t{12}\t{13}\t{14}\t{15}\t{16}\t{17}\t{18}\t{19}\t{20}\t{21}\t{22}",
DateTime.Hour.ToString("D2"),
DateTime.Minute.ToString("D2"),
DateTime.Second.ToString("D2"),
DateTime.Millisecond.ToString("D4"),
Counter,
(EmfRaw & 0x00FFFFFF).ToString("X6"),
MagneticFieldRaw.ToString("X4"),
FlowRaw.ToString("X4"),
((int)VolumeRaw).ToString("X6"),
Impedance.ToString("X4"),
((int)Timestamp).ToString("X8"),
CheckSum.ToString("X2"),
EMF().ToString("F4", culture),
MagneticField().ToString("F0", culture),
Flow(scalingFactor).ToString("F2", culture),
Volume(scalingFactor).ToString("F4", culture),
FlipTime().ToString("F0", culture),
TimestampDec().ToString("F4", culture),
(RefFlow * 1000).ToString("F2", culture),
VolumeDelta(scalingFactor, previous).ToString("F4", culture),
TimeDelta().ToString("F3", culture),
scalingFactor.ToString("F1", culture),
Label());
}
}
}
}
@@ -0,0 +1,28 @@
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication
{
public class ChannelAverages
{
public double?[] VolumeStart { get; } = new double?[3];
public double?[] VolumeEnd { get; } = new double?[3];
public double?[] TimeStart { get; } = new double?[3];
public double?[] TimeEnd { get; } = new double?[3];
public static double Average(double?[] values)
{
double sum = 0;
int count = 0;
foreach (var v in values)
{
if (v.HasValue)
{
sum += v.Value;
count++;
}
}
return count > 0 ? sum / count : 0;
}
}
}
@@ -0,0 +1,43 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications
{
/// <summary>
/// describe a genesis register.
/// need for read and write
/// all request protocols uses RegisterDefinition
/// </summary>
public class ApplicationDefinition
{
/// <summary>
/// name of the application
/// </summary>
public String AppName;
/// <summary>
/// address of application
/// </summary>
/// <remarks date="2025-Aug-05" author="Thomas Wiedebusch">
/// - AppId from Byte to UInt16 including typecast for Byte[] return. To external, it will be used as Byte
/// as before this change. But being able to parse the configuration.json with OPTICALINTERFACE using the
/// AppId 256, which exceeds the byte range as this isn't a real application, but will be used to identify
/// the interface (configuration.json) version.
/// </remarks>
public UInt16 AppId;
/// <summary>
/// version of the application
/// </summary>
public Int32? AppVersion;
/// <summary>
/// Get application name
/// </summary>
/// <returns></returns>
public String GetIdent()
{
return $"{AppName}";
}
}
}
@@ -0,0 +1,78 @@
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications.Const
{
/// <summary>
/// Genesis meter application state
/// </summary>
public enum MeterAppState
{
/// <summary>
/// This file application is not installed in the meter
/// </summary>
MeterAppNotInstalled,
/// <summary>
/// Meter version is equal to file version - it is up to date
/// </summary>
MeterAppUpToDate,
/// <summary>
/// Installation of application is required
/// </summary>
MeterAppInstallationRequired,
/// <summary>
/// Meter application is different from file application
/// </summary>
MeterAppVersionOutdated,
/// <summary>
/// CRC does not match even if meter and file application version are
/// shown as being identical!
/// </summary>
InvalidCrc,
/// <summary>
/// Meter applications has to be erased
/// </summary>
MeterAppErasureRequired,
/// <summary>
/// Application successfully downloaded to meter
/// </summary>
MeterAppDownloadSucceeded,
/// <summary>
/// Application download to meter suspicious
/// </summary>
MeterAppDownloadSuspicious,
/// <summary>
/// Application successfully erased from meter
/// </summary>
MeterAppSuccessfulErased,
/// <summary>
/// Application successfully updated in meter
/// </summary>
MeterAppSuccessfulUpdated,
/// <summary>
/// Application download to meter failed
/// </summary>
MeterAppDownloadFailed,
/// <summary>
/// Application download to meter active ongoing
/// </summary>
MeterAppDownloadActive,
/// <summary>
/// This file application is invalid
/// </summary>
FileAppInvalid,
/// <summary>
/// State is unknown because package file not loaded
/// </summary>
Unknown,
/// <summary>
/// Meter application detected but not checked
/// </summary>
MeterAppInstalledUnchecked,
/// <summary>
/// Meter application is not required
/// </summary>
MeterAppNotRequired,
/// <summary>
/// Delimiter for list
/// </summary>
MeterAppStateListDelimiter
}
}
@@ -0,0 +1,92 @@
using System;
using System.Linq;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications.Const
{
/// <summary>
/// Concatenation of state and string
/// </summary>
public class MeterAppStateInfo
{
/// <summary>
/// Build state information
/// </summary>
private readonly MeterAppStateText[] _stateInfos;
/// <summary>
/// Ctor
/// </summary>
public MeterAppStateInfo()
{
_stateInfos = new MeterAppStateText[(Int32)MeterAppState.MeterAppStateListDelimiter];
_stateInfos[(Int32)MeterAppState.MeterAppNotInstalled].State = MeterAppState.MeterAppNotInstalled;
_stateInfos[(Int32)MeterAppState.MeterAppNotInstalled].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppNotInstalled;
_stateInfos[(Int32)MeterAppState.MeterAppNotRequired].State = MeterAppState.MeterAppNotRequired;
_stateInfos[(Int32)MeterAppState.MeterAppNotRequired].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppNotRequired;
_stateInfos[(Int32)MeterAppState.MeterAppUpToDate].State = MeterAppState.MeterAppUpToDate;
_stateInfos[(Int32)MeterAppState.MeterAppUpToDate].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppUpToDate;
_stateInfos[(Int32)MeterAppState.MeterAppInstallationRequired].State = MeterAppState.MeterAppInstallationRequired;
_stateInfos[(Int32)MeterAppState.MeterAppInstallationRequired].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppInstallationRequired;
_stateInfos[(Int32)MeterAppState.MeterAppVersionOutdated].State = MeterAppState.MeterAppVersionOutdated;
_stateInfos[(Int32)MeterAppState.MeterAppVersionOutdated].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppVersionOutdated;
_stateInfos[(Int32)MeterAppState.InvalidCrc].State = MeterAppState.InvalidCrc;
_stateInfos[(Int32)MeterAppState.InvalidCrc].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrInvalidCrc;
_stateInfos[(Int32)MeterAppState.MeterAppErasureRequired].State = MeterAppState.MeterAppErasureRequired;
_stateInfos[(Int32)MeterAppState.MeterAppErasureRequired].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppErasureRequired;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadSucceeded].State = MeterAppState.MeterAppDownloadSucceeded;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadSucceeded].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppDownloadSucceeded;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadSuspicious].State = MeterAppState.MeterAppDownloadSuspicious;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadSuspicious].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppDownloadSuspicious;
_stateInfos[(Int32)MeterAppState.MeterAppSuccessfulErased].State = MeterAppState.MeterAppSuccessfulErased;
_stateInfos[(Int32)MeterAppState.MeterAppSuccessfulErased].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppSuccessfulErased;
_stateInfos[(Int32)MeterAppState.MeterAppSuccessfulUpdated].State = MeterAppState.MeterAppSuccessfulUpdated;
_stateInfos[(Int32)MeterAppState.MeterAppSuccessfulUpdated].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppSuccessfulUpdated;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadFailed].State = MeterAppState.MeterAppDownloadFailed;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadFailed].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppDownloadFailed;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadActive].State = MeterAppState.MeterAppDownloadActive;
_stateInfos[(Int32)MeterAppState.MeterAppDownloadActive].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppDownloadActive;
_stateInfos[(Int32)MeterAppState.FileAppInvalid].State = MeterAppState.FileAppInvalid;
_stateInfos[(Int32)MeterAppState.FileAppInvalid].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrFileAppInvalid;
_stateInfos[(Int32)MeterAppState.Unknown].State = MeterAppState.Unknown;
_stateInfos[(Int32)MeterAppState.Unknown].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppStateUnknown;
_stateInfos[(Int32)MeterAppState.MeterAppInstalledUnchecked].State = MeterAppState.MeterAppInstalledUnchecked;
_stateInfos[(Int32)MeterAppState.MeterAppInstalledUnchecked].Message = Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources.StrMeterAppDetected;
}
/// <summary>
/// Get the text of this state
/// </summary>
/// <param name="state">state to search for information text</param>
public String GetTextFromState(MeterAppState state)
{
return (from stateInfo in _stateInfos
where stateInfo.State == state select stateInfo.Message).Last();
}
/// <summary>
/// Get the state of the text
/// </summary>
/// <param name="text">information text to search if state is assigned</param>
public MeterAppState GetStateFromText(String text)
{
return (from stateInfo in _stateInfos
where stateInfo.Message == text select stateInfo.State).Last();
}
}
}
@@ -0,0 +1,19 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications.Const
{
/// <summary>
/// Concatenation between meter application state and text
/// </summary>
public struct MeterAppStateText
{
/// <summary>
/// The state of the firmware update
/// </summary>
public MeterAppState State;
/// <summary>
/// The message text of the meter application state
/// </summary>
public String Message;
}
}
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications
{
/// <summary>
/// File FW and application information
/// </summary>
public class FileApplications
{
/// <summary>
/// Name of application
/// </summary>
public String AppName;
/// <summary>
/// File name of the file system where the binaries are stored
/// </summary>
public String BinaryFilename;
/// <summary>
/// File name of the meter including the "1\\upg" string and the AppId as hexadecimal 2 digits
/// </summary>
public String MeterFilename;
/// <summary>
/// Ctor file applications
/// </summary>
/// <param name="binaryFilename"></param>
public FileApplications(String binaryFilename)
{
BinaryFilename = binaryFilename;
}
/// <summary>
/// Identification number of application
/// </summary>
public Byte AppId;
/// <summary>
/// Version of application
/// </summary>
public UInt32 Version;
/// <summary>
/// Version string of application because of FLEXNETVERSION
/// does return a different layout, instead of decimals it
/// used hexadecimals. Therefore the version has to be saved as
/// string at time of receiving for the FW-Update comparison
/// </summary>
public String StrVersion;
/// <summary>
/// Check of application for verification
/// </summary>
public UInt16 Crc;
/// <summary>
/// Validation of application
/// </summary>
public Boolean IsValid;
/// <summary>
/// Binary file content
/// </summary>
public List<Byte> BinData = new List<Byte>();
/// <summary>
/// Actual part if file is going to be partitioned
/// </summary>
public Int32 Part = 0;
/// <summary>
/// Number of parts for entire file if partitioned
/// </summary>
public Int32 Parts = 1;
/// <summary>
/// Meter file offset if file is partitioned
/// </summary>
public Int32 MeterFileOffset = 0;
}
}
@@ -0,0 +1,65 @@
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications.Const;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications
{
/// <summary>
/// Meter FW and application information
/// </summary>
public class MeterApplications
{
/// <summary>
/// Name of application
/// </summary>
public String AppName;
/// <summary>
/// Identification number of application
/// </summary>
/// <remarks date="2025-Aug-05" author="Thomas Wiedebusch">
/// - AppId from Byte to UInt16 including typecast for Byte[] return. To external, it will be used as Byte
/// as before this change. But being able to parse the configuration.json with OPTICALINTERFACE using the
/// AppId 256, which exceeds the byte range as this isn't a real application, but will be used to identify
/// the interface (configuration.json) version.
/// </remarks>
public UInt16 AppId;
/// <summary>
/// Version of application
/// </summary>
public UInt32 Version;
/// <summary>
/// Version string of application because of FLEXNETVERSION does return a different layout, instead of decimals
/// it uses hexadecimals. Therefore, the version has to be saved as string at time of receiving for the FW-Update
/// comparison.
/// </summary>
public String StrVersion;
/// <summary>
/// Check of application for verification
/// </summary>
public UInt16 Crc;
/// <summary>
/// Check if application is installed
/// </summary>
public Boolean IsInstalled;
/// <summary>
/// Update this version
/// </summary>
public Boolean Update;
/// <summary>
/// Erase this version
/// </summary>
public Boolean Erase;
/// <summary>
/// Status text
/// </summary>
public MeterAppState Status;
}
}
@@ -0,0 +1,207 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Xylem.Common.Hardware.WaterMeter.Genesis.Applications.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to INVALID UPDATE FILE.
/// </summary>
public static string StrFileAppInvalid {
get {
return ResourceManager.GetString("StrFileAppInvalid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to CRC ERROR.
/// </summary>
public static string StrInvalidCrc {
get {
return ResourceManager.GetString("StrInvalidCrc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installed.
/// </summary>
public static string StrMeterAppDetected {
get {
return ResourceManager.GetString("StrMeterAppDetected", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DOWNLOAD....
/// </summary>
public static string StrMeterAppDownloadActive {
get {
return ResourceManager.GetString("StrMeterAppDownloadActive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Download failed.
/// </summary>
public static string StrMeterAppDownloadFailed {
get {
return ResourceManager.GetString("StrMeterAppDownloadFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Burn - Trigger upgrade required.
/// </summary>
public static string StrMeterAppDownloadSucceeded {
get {
return ResourceManager.GetString("StrMeterAppDownloadSucceeded", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Verification required.
/// </summary>
public static string StrMeterAppDownloadSuspicious {
get {
return ResourceManager.GetString("StrMeterAppDownloadSuspicious", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Erase - Trigger upgrade required.
/// </summary>
public static string StrMeterAppErasureRequired {
get {
return ResourceManager.GetString("StrMeterAppErasureRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installation required.
/// </summary>
public static string StrMeterAppInstallationRequired {
get {
return ResourceManager.GetString("StrMeterAppInstallationRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Not installed.
/// </summary>
public static string StrMeterAppNotInstalled {
get {
return ResourceManager.GetString("StrMeterAppNotInstalled", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Not required.
/// </summary>
public static string StrMeterAppNotRequired {
get {
return ResourceManager.GetString("StrMeterAppNotRequired", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Installation state unknown.
/// </summary>
public static string StrMeterAppStateUnknown {
get {
return ResourceManager.GetString("StrMeterAppStateUnknown", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Erasure succeeded.
/// </summary>
public static string StrMeterAppSuccessfulErased {
get {
return ResourceManager.GetString("StrMeterAppSuccessfulErased", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update succeeded.
/// </summary>
public static string StrMeterAppSuccessfulUpdated {
get {
return ResourceManager.GetString("StrMeterAppSuccessfulUpdated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Okay.
/// </summary>
public static string StrMeterAppUpToDate {
get {
return ResourceManager.GetString("StrMeterAppUpToDate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Update required.
/// </summary>
public static string StrMeterAppVersionOutdated {
get {
return ResourceManager.GetString("StrMeterAppVersionOutdated", resourceCulture);
}
}
}
}
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="StrMeterAppNotInstalled" xml:space="preserve">
<value>Nicht installiert</value>
</data>
<data name="StrMeterAppUpToDate" xml:space="preserve">
<value>Okay</value>
</data>
<data name="StrMeterAppInstallationRequired" xml:space="preserve">
<value>Installation notwendig</value>
</data>
<data name="StrMeterAppVersionOutdated" xml:space="preserve">
<value>Update erforderlich</value>
</data>
<data name="StrInvalidCrc" xml:space="preserve">
<value>Prüfsummenfehler (CRC)</value>
</data>
<data name="StrMeterAppErasureRequired" xml:space="preserve">
<value>Lösche beim Abschlußvorgang</value>
</data>
<data name="StrMeterAppDownloadSucceeded" xml:space="preserve">
<value>Übernehme beim Abschlußvorgang</value>
</data>
<data name="StrMeterAppDownloadSuspicious" xml:space="preserve">
<value>Überprüfung notwendig</value>
</data>
<data name="StrMeterAppSuccessfulErased" xml:space="preserve">
<value>Löschen erforderlich</value>
</data>
<data name="StrMeterAppSuccessfulUpdated" xml:space="preserve">
<value>Aktualisierung war erfolgreich</value>
</data>
<data name="StrMeterAppDownloadFailed" xml:space="preserve">
<value>Dateiübertragung fehlgeschlagen</value>
</data>
<data name="StrMeterAppDownloadActive" xml:space="preserve">
<value>EINSPIELEN...</value>
</data>
<data name="StrFileAppInvalid" xml:space="preserve">
<value>Ungültige Aktualisierungsdatei</value>
</data>
<data name="StrMeterAppDetected" xml:space="preserve">
<value>Installiert</value>
</data>
<data name="StrMeterAppStateUnknown" xml:space="preserve">
<value>Installationszustand unbekannt</value>
</data>
<data name="StrMeterAppNotRequired" xml:space="preserve">
<value>Nicht eforderlich</value>
</data>
</root>
@@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="StrMeterAppNotInstalled" xml:space="preserve">
<value>Not installed</value>
</data>
<data name="StrMeterAppUpToDate" xml:space="preserve">
<value>Okay</value>
</data>
<data name="StrMeterAppInstallationRequired" xml:space="preserve">
<value>Installation required</value>
</data>
<data name="StrMeterAppVersionOutdated" xml:space="preserve">
<value>Update required</value>
</data>
<data name="StrInvalidCrc" xml:space="preserve">
<value>CRC ERROR</value>
</data>
<data name="StrMeterAppErasureRequired" xml:space="preserve">
<value>Erase - Trigger upgrade required</value>
</data>
<data name="StrMeterAppDownloadSucceeded" xml:space="preserve">
<value>Burn - Trigger upgrade required</value>
</data>
<data name="StrMeterAppDownloadSuspicious" xml:space="preserve">
<value>Verification required</value>
</data>
<data name="StrMeterAppSuccessfulErased" xml:space="preserve">
<value>Erasure succeeded</value>
</data>
<data name="StrMeterAppSuccessfulUpdated" xml:space="preserve">
<value>Update succeeded</value>
</data>
<data name="StrMeterAppDownloadFailed" xml:space="preserve">
<value>Download failed</value>
</data>
<data name="StrMeterAppDownloadActive" xml:space="preserve">
<value>DOWNLOAD...</value>
</data>
<data name="StrFileAppInvalid" xml:space="preserve">
<value>INVALID UPDATE FILE</value>
</data>
<data name="StrMeterAppDetected" xml:space="preserve">
<value>Installed</value>
</data>
<data name="StrMeterAppStateUnknown" xml:space="preserve">
<value>Installation state unknown</value>
</data>
<data name="StrMeterAppNotRequired" xml:space="preserve">
<value>Not required</value>
</data>
</root>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
@@ -0,0 +1,19 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis
{
/// <summary>
/// Constants for inter-program exchange
/// </summary>
public static class Constants
{
/// <summary>
/// Marker for lack of information in strings e.g. for region, size, LutCrc, ...
/// </summary>
public const String StrUnknown = "?";
/// <summary>
/// Marker for wildcard in search operations
/// </summary>
public const String StrWildcard = "*";
}
}
@@ -0,0 +1,227 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis
{
/// <summary>
/// Class for CRC16 CCITT calculation
/// </summary>
public static class Crc16Ccitt
{
/// <summary>
/// Initial CRC value
/// </summary>
private const UInt16 CrcSeedFfff = 0xFFFF;
/// <summary>
/// Initial CRC value
/// </summary>
private const UInt16 CrcSeed3791 = 0x3791;
/// <summary>
/// Generator polynomial MagFlux - Modbus RTU
/// </summary>
private const UInt16 CrcGpA001 = 0xA001;
/// <summary>
/// Generator polynomial GENESIS
/// </summary>
private const UInt16 CrcGp1021 = 0x1021;
/// <summary>
/// Generator polynomial RFID
/// </summary>
private const UInt16 CrcGp0408 = 0x0408;
/// <summary>
/// Generator polynomial for reversed IrDA
/// </summary>
private const UInt16 CrcGp8408 = 0x8408;
/// <summary>
/// Calculates the CRC (LSB first, reversed, CRC CCITT 0x8408) from a data array of bytes.
/// </summary>
/// <param name="data">data to be processed</param>
/// <returns>Calculated CRC</returns>
public static UInt16 CalculateReversedLsb8408(Byte[] data)
{
var crc = CrcSeedFfff;
foreach (var t in data)
{
//copy data byte to lower word because of LSB will be XORed
var dataWord = (UInt16)(t & 0x00FF);
for (var i = 0; i < 8; i++)
{
//test if CRC lowest bit is XORed set to one
if (0x0001 == ((crc ^ dataWord) & 0x0001))
{
crc >>= 1;
//apply generator polynomial
crc ^= CrcGp8408;
}
else
{
crc >>= 1;
}
dataWord >>= 1;
}
}
return ((UInt16)~crc);
}
/// <summary>
/// Calculates the bitwise inverted CRC (MSB first, CRCCCITT 0x1021)
/// from a data array of bytes.
/// </summary>
/// <param name="data">data to be processed</param>
/// <returns>Calculated CRC</returns>
public static UInt16 CalculateInvertedMsb1021(Byte[] data)
{
return (UInt16)~CalculateMsb1021(data);
}
/// <summary>
/// Calculates the Modbus RTU CRC (LSB first, CRCCCITT 0xA001)
/// from a data array of bytes.
/// </summary>
/// <param name="data">data to be processed</param>
/// <returns>Calculated CRC</returns>
public static UInt16 ModbusRtuLsbA001(Byte[] data)
{
var crc = CrcSeedFfff;
for (var t = 0; t < data.Length; t++)
{
crc ^= data[t]; // XOR byte into least sig. byte of crc
for (var i = 8; i != 0; i--)
{
if ((crc & 0x0001) != 0)
{
crc >>= 1;
crc ^= CrcGpA001;
}
else
{
crc >>= 1;
}
}
}
return crc;
}
/// <summary>
/// Calculates the CRC (MSB first, CRCCCITT 0x1021) from a data array of bytes.
/// <para>18.07.2024 - optional parameters start and length added - for more flexibility by calculating a CRC of a message.</para>
/// <para>18.07.2024 - length checks: if length less or equal 0 or grater than the data length: the data length is taken as length.</para>
/// </summary>
/// <param name="start">optional, default = 0</param>
/// <param name="length">optional, default = 0</param>
/// <param name="data">data to be processed</param>
/// <returns>Calculated CRC</returns>
public static UInt16 CalculateMsb1021(Byte[] data, Int32 start = 0, Int32 length = 0)
{
var dataLength = data.Length;
if (length <= 0 || length > dataLength)
{
length = dataLength;
}
var crc = CrcSeedFfff;
for (var b = start; b < length; b++)
{
var t = data[b];
//copy data byte to higher word because of MSB will be XORed
var dataWord = (UInt16)((t << 8) & 0xFF00);
for (var i = 0; i < 8; i++)
{
//test if CRC highest bit or data input highest bit is XORed set to one
if (0x8000 == ((crc ^ dataWord) & 0x8000)) //shifted in MSB first
{
//shift CRC high bit out
crc <<= 1;
//apply generator polynomial
crc ^= CrcGp1021;
}
else //CRC highest bit and data input highest bit is equal
{
crc <<= 1;
}
dataWord <<= 1; //MSB first shifted out
}
}
//foreach (var t in data)
//{
// //copy data byte to higher word because of MSB will be XORed
// var dataWord = (UInt16)((t << 8) & 0xFF00);
// for (var i = 0; i < 8; i++)
// {
// //test if CRC highest bit or data input highest bit is XORed set to one
// if (0x8000 == ((crc ^ dataWord) & 0x8000)) //shifted in MSB first
// {
// //shift CRC high bit out
// crc <<= 1;
// //apply generator polynomial
// crc ^= CrcGp1021;
// }
// else //CRC highest bit and data input highest bit is equal
// {
// crc <<= 1;
// }
// dataWord <<= 1; //MSB first shifted out
// }
// }
return crc;
}
public static Byte[] CalculateCRC1021LSBFirst(Byte[] bytes, Int32 start, Int32 length)
{
var crc = CalculateMsb1021(bytes, start, length);
return BitConverter.GetBytes(crc);
}
/// <summary>
/// Calculates the CRC (LSB first, CRCCCITT 0x0408) from a data array of bytes.
/// </summary>
/// <param name="data">data to be processed</param>
/// <returns>Calculated CRC</returns>
public static UInt16 CalculateLsb0408(Byte[] data)
{
var crc = CrcSeed3791;
foreach (var t in data)
{
var dataByte = t;
for (var i = 0; i < 8; i++)
{
//test if CRC lowest bit is set to one
if (1 == (crc & 1))
{
//shift CRC low bit out
crc >>= 1;
//test lowest bit of data word
if (1 == (dataByte & 1))
crc |= 0x8000;
//invert CRC MSB
crc ^= 0x8000;
}
else //CRC lowest bit not set
{
//shift CRC low bit out
crc >>= 1;
//test lowest bit of data word
if (1 == (dataByte & 1))
crc |= 0x8000;
}
if (0x8000 == (crc & 0x8000))
//apply generator polynomial
crc ^= CrcGp0408;
dataByte >>= 1; //LSB first shifted out
}
}
return crc;
}
}
}
@@ -0,0 +1,33 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
/// <summary>
/// abstract for set structure for BaseDataEventArgs
/// </summary>
public abstract class BaseDataEventArgs : EventArgs
{
/// <summary>
/// 'Base' get event record form real child.
/// </summary>
/// <returns></returns>
public Object GetData()
{
return GetEventData();
}
/// <summary>
/// get real Event record
/// </summary>
/// <returns></returns>
public abstract Object GetEventData();
/// <summary>
/// Holds the record before decoding, for logging
/// </summary>
public String RawData;
}
}
@@ -0,0 +1,20 @@
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class BendDetectDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// new record from Stream
/// </summary>
public BendDetectionRecord NewData;
/// <inheritdoc />
public override Object GetEventData()
{
return NewData;
}
}
}
@@ -0,0 +1,20 @@
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class CalibDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// Stream record for calibration on channel
/// </summary>
public CalibrationRecord CalibChl;
/// <inheritdoc />
public override Object GetEventData()
{
return CalibChl;
}
}
}
@@ -0,0 +1,20 @@
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class FlowDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// new record from Stream
/// </summary>
public FlowTestRecord NewData;
/// <inheritdoc />
public override Object GetEventData()
{
return NewData;
}
}
}
@@ -0,0 +1,31 @@
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
public class RegisterUpdatedEventArgs : EventArgs
{
/// <inheritdoc />
/// <summary>
/// Ctor with base record
/// </summary>
/// <param name="register">base record <see cref="F:Register" /></param>
/// <param name="value">base record <see cref="F:Value" /></param>
public RegisterUpdatedEventArgs(RegisterDefinition register, Byte[] value)
{
Register = register;
Value = value;
}
/// <summary>
/// Register witch has updated
/// </summary>
public RegisterDefinition Register;
/// <summary>
/// New Value in Register
/// </summary>
public Byte[] Value;
}
}
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments
{
/// <inheritdoc />
/// <summary>
/// EventArgs for Request Responses
/// </summary>
public class RequestResponseDataEventArgs : BaseDataEventArgs
{
/// <summary>
/// Request response from meter
/// </summary>
public List<Byte> RequestResponseData;
/// <inheritdoc />
public override Object GetEventData()
{
return RequestResponseData;
}
}
}
@@ -0,0 +1,114 @@
using System;
using System.Text;
using Xylem.Common.Metrology.Measurements;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords
{
/// <inheritdoc />
/// <summary>
/// Streaming record for protocol M (information about bend detection and correction)
/// </summary>
public class BendDetectionRecord : MeasurementRecord
{
/// <summary>
/// The status of the U0 detection for this measurement
/// </summary>
public enum StatusBendU0Enum
{
/// <summary>
/// Status okay
/// </summary>
OKAY = 0,
/// <summary>
/// Error code as defined by field name
/// </summary>
ERROR_GENESISFLOW_INSTALLATION_HIGH_TIME_DIFF = 0x0F41,
/// <summary>
/// Error code as defined by field name
/// </summary>
ERROR_GENESISFLOW_INSTALLATION_LOW_FLOW_IGNORE = 0x0F42,
/// <summary>
/// Error code as defined by field name
/// </summary>
ERROR_GENESISFLOW_INSTALLATION_BAD_CHANNELS = 0x0F43,
/// <summary>
/// Error code as defined by field name
/// </summary>
ERROR_GENESISFLOW_INSTALLATION_FIXED = 0x0F44
}
/// <summary>
/// An enum describing the installation type detected
/// </summary>
public enum InstallationTypeEnum
{
/// <summary>
/// Installation type code as defined by field name
/// </summary>
INSTALLATION_U0_180_360 = 0,
/// <summary>
/// Installation type code as defined by field name
/// </summary>
INSTALLATION_U0_90_270 = 1,
/// <summary>
/// Installation type code as defined by field name
/// </summary>
INSTALLATION_UNDISTURBED = 2
}
/// <summary>
/// Status of U0 Bend detection
/// </summary>
public StatusBendU0Enum StatusBendU0;
/// <summary>
/// Installation type code
/// </summary>
public InstallationTypeEnum InstallationType;
/// <summary>
/// The proportion of the installation correction to apply based on the detected
/// installation. 100% == 0x8000
/// </summary>
public Double CorrectionFactor_percent;
/// <summary>
/// The default proportion of the installation correction to apply based on the
/// detected installation. 100% == 0x8000
/// </summary>
private const UInt32 DefaultCorrectionFactor = 0x8000;
/// <summary>
/// Scale for correction factor to apply to convert it to percentage value 100% == 0x8000
/// </summary>
public const Double CorrectionFactorScale = 100.0 / DefaultCorrectionFactor;
/// <summary>
/// The volume in internal units before correction applied
/// </summary>
public Double PreCorrectionVolumeRaw;
/// <summary>
/// The volume in internal units after correction applied
/// </summary>
public Double PostCorrectionVolumeRaw;
/// <summary>
/// Get result as string
/// </summary>
/// <returns></returns>
public override String ToString()
{
var sb = new StringBuilder();
sb.Append($"Status={StatusBendU0}").Append(",")
.Append($"Installation={InstallationType}").Append(",")
.Append($"Factor={CorrectionFactor_percent}").Append(",")
.Append($"PreVolume={PreCorrectionVolumeRaw}").Append(",")
.Append($"PostVolume={PostCorrectionVolumeRaw}");
return sb.ToString();
}
}
}
@@ -0,0 +1,152 @@
using System;
using System.Text;
using Xylem.Common.Metrology.Measurements;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords
{
/// <inheritdoc />
/// <summary>
/// hold streaming record for protocol H (contains calibration record)
/// </summary>
public class CalibrationRecord : MeasurementRecord
{
/// <summary>
/// record validation
/// </summary>
public UInt16 Validation;
/// <summary>
/// total time of flight in seconds
/// </summary>
public Double TotalTimeOfFlightS;
/// <summary>
/// delta time of flight in seconds
/// </summary>
public Double DeltaTimeOfFlightS;
/// <summary>
/// total time of flight in cordonel units
/// </summary>
public Int32 RawTotalTimeOfFlight;
/// <summary>
/// delta time of flight in cordonel units
/// </summary>
public Int32 RawDeltaTimeOfFlight;
/// <summary>
/// volume scale (default: 1024) means
/// 1024digits = 1ml
/// </summary>
public Double VolumeScaleRawPerMl;
/// <summary>
/// volume factor to convert raw to m³
/// uses 1E-6 (ml to m³) / VolumeScaleRawPerMl
/// </summary>
public Double VolumeFactorRawToQm;
/// <summary>
/// raw volume between two samples
/// </summary>
public Double DeltaVolumeRaw;
/// <summary>
/// calculated out of dRawVolume * VolumeFactorRawToQm .
/// in cubic meters
/// </summary>
public Double DeltaVolumeQm;
/// <summary>
/// accumulated raw volume
/// </summary>
public Double AccuVolumeRaw;
/// <summary>
/// sample interval between two samples in seconds
/// </summary>
public Double SampleIntervalS;
/// <summary>
/// high threshold amplitude in V
/// </summary>
public Double AmplitudeUpV;
/// <summary>
/// low threshold amplitude in V
/// </summary>
public Double AmplitudeDownV;
/// <summary>
/// high ratio for pulse width
/// </summary>
public Double PulseWidthRatioUp;
/// <summary>
/// low ratio for pulse width
/// </summary>
public Double PulseWidthRatioDown;
/// <summary>
/// raw temperature
/// </summary>
public Double TemperatureRaw;
/// <summary>
/// temperature scale
/// </summary>
public Double TemperaturePowFactor;
/// <summary>
/// calculated temperature in degree C
/// </summary>
public Double TemperatureDegC;
/// <summary>
/// Get result as string
/// </summary>
/// <returns></returns>
public override String ToString()
{
var sb = new StringBuilder();
sb.Append($"Channel={Channel}").Append(",")
.Append($"Validation={Validation}").Append(",")
.Append($"TotalTimeOfFlightS={TotalTimeOfFlightS}").Append(",")
.Append($"DeltaTimeOfFlightS={DeltaTimeOfFlightS}").Append(",")
.Append($"DeltaVolumeRaw={DeltaVolumeRaw}").Append(",")
.Append($"VolumeScaleRawPerMl={VolumeScaleRawPerMl}").Append(",")
.Append($"VolumeFactorRawToQm={VolumeFactorRawToQm}").Append(",")
.Append($"DeltaVolumeRaw={DeltaVolumeRaw}").Append(",")
.Append($"DeltaVolumeQm={DeltaVolumeQm}").Append(",")
.Append($"AccuVolumeRaw={AccuVolumeRaw}").Append(",")
.Append($"VolumeCm={VolumeCm}").Append(",")
.Append($"AccuDutOverflowVolumeCm={OverflowVolumeCm}").Append(",")
.Append($"SampleIntervalS={SampleIntervalS}").Append(",")
.Append($"AmplitudeUpV={AmplitudeUpV}").Append(",")
.Append($"AmplitudeDownV={AmplitudeDownV}").Append(",")
.Append($"PulseWidthRatioUp={PulseWidthRatioUp}").Append(",")
.Append($"PulseWidthRatioDown={PulseWidthRatioDown}").Append(",")
.Append($"TemperatureRaw={TemperatureRaw}").Append(",")
.Append($"TemperaturePowFactor={TemperaturePowFactor}").Append(",")
.Append($"TemperatureDegC={TemperatureDegC}").Append(",")
.Append($"TimeS={TimeS}").Append(",")
.Append($"OverflowTimeS={OverflowTimeS}").Append(",")
.Append($"CRC={Crc}").Append(",")
.Append($"IsValid={IsValid}").Append(",")
.Append($"ReceivedTimeUtc={ReceivedTime}").Append(",")
.Append($"DecodedTimeUtc={DecodedTime}").Append(",")
.Append($"SyncMarkRecord={SyncMarkRecord}");
return sb.ToString();
}
}
}
@@ -0,0 +1,32 @@
using System;
using System.Text;
using Xylem.Common.Metrology.Measurements;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords
{
/// <inheritdoc />
/// <summary>
/// struct to hold Led record for protocol F (contains measurement record)
/// </summary>
public class FlowTestRecord : MeasurementRecord
{
/// <summary>
/// Get result as string
/// </summary>
/// <returns></returns>
public override String ToString()
{
var sb = new StringBuilder();
sb = sb.Append($"VolumeCm={VolumeCm}").Append(",")
.Append($"OverflowVolumeCm={OverflowVolumeCm}").Append(",")
.Append($"TimeS={TimeS}").Append(",")
.Append($"OverflowTimeS={OverflowTimeS}").Append(",")
.Append($"CRC={Crc}").Append(",")
.Append($"IsValid={IsValid}").Append(",")
.Append($"ReceivedTimeUtc={ReceivedTime}").Append(",")
.Append($"DecodedTimeUtc={DecodedTime}").Append(",")
.Append($"SyncMarkRecord={SyncMarkRecord}");
return sb.ToString();
}
}
}
@@ -0,0 +1,49 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages
{
/// <summary>
/// All parameters needed for radio setup
/// </summary>
public class RadioConfigurationParams
{
/// <summary>
/// Pcb identification
/// </summary>
public String PcbId;
/// <summary>
/// Serial number
/// </summary>
public UInt32 SerialNumber;
/// <summary>
/// Radio address
/// </summary>
public UInt32 RadioAddress;
/// <summary>
/// Power level
/// </summary>
public UInt32 PowerLevel;
/// <summary>
/// Power level option
/// </summary>
public UInt32 PowerLevelOption;
/// <summary>
/// New code for impedance
/// </summary>
public UInt32 ImpedanceCodeNew;
/// <summary>
/// Impedance code option
/// </summary>
public UInt32 ImpedanceCodeOption;
/// <summary>
/// Encryption key
/// </summary>
public Byte[] EncryptionKey;
/// <summary>
/// Frequency offset
/// </summary>
public Int16? FrqOffset { get; set; }
}
}
@@ -0,0 +1,29 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages
{
/// <summary>
///
/// </summary>
public class RegisterToDb
{
/// <summary>
/// Register address and value to DB
/// </summary>
/// <param name="adresse"></param>
/// <param name="value"></param>
public RegisterToDb(Byte[] adresse, Byte[] value)
{
Adresse = adresse;
Value = value;
}
/// <summary>
/// Address
/// </summary>
public Byte[] Adresse;
/// <summary>
/// Value
/// </summary>
public Byte[] Value;
}
}
@@ -0,0 +1,27 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages
{
/// <summary>
/// Temperature Time Of Flight calculation
/// </summary>
public struct TempTofCalc
{
private DateTime _refDate;
private Double _refTemp;
private Double _tof;
/// <summary>
/// Reference data
/// </summary>
public DateTime RefDate { get => _refDate; set => _refDate = value; }
/// <summary>
/// Reference temperature
/// </summary>
public Double RefTemp { get => _refTemp; set => _refTemp = value; }
/// <summary>
/// Time Of Flight
/// </summary>
public Double Tof { get => _tof; set => _tof = value; }
}
}
@@ -0,0 +1,88 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig
{
/// <summary>
/// Setup of communication timeouts and retries for request and streaming protocol,
/// this timeout starts the retry.
///
/// The individual timings of the hardware will be covert by the
/// <see cref="TransmitPortSettings.ResponseTimeoutMs"/>
/// injected by the different transmit protocols:
/// <see cref="IrdaTransmitProtocol"/>
/// <see cref="LedTransmitProtocol"/>
/// <see cref="RfidTransmitProtocol"/>
/// <see cref="UartTransmitProtocol"/>
/// </summary>
public static class CommunicationConfig
{
private static Int32 _requestRetries = DefaultRequestRetries;
/// <summary>
/// Maximum retries for one command at request protocol
/// at missing response.
/// </summary>
public static Int32 RequestRetries
{
get => _requestRetries;
set => _requestRetries = value > MaxRequestRetries ? MaxRequestRetries : value;
}
/// <summary>
/// Acceptable error code as valid answer to skip retries,
/// this might be 0x0004 for not installed application.
/// </summary>
public const UInt16 SkipRetryErrorCode = 0x0004;
/// <summary>
/// Default retries for one command at request protocol
/// at missing response.
/// </summary>
public const Int32 DefaultRequestRetries = 2;
/// <summary>
/// Limit retries to this value.
/// </summary>
public const Int32 MaxRequestRetries = 6;
/// <summary>
/// Send delay between records in milliseconds before
/// new record is going to be sent or between retries,
/// this time is independent of the timeout, it is
/// used to delay the next send record after successful
/// response from meter.
/// </summary>
public const Int32 InterRecordSendDelayMs = 5;
//public const Int32 InterRecordSendDelayMs = 50;
/// <summary>
/// Timeout before retry will be initiated in milliseconds
/// for the response of the request protocol. This is an
/// offset value, the transmission specific timing will be
/// added from the transmit protocol timeout. This timeout
/// will be multiplied with the (retry + 1)!
/// </summary>
public const Int32 ResponseTimeoutMs = 250;
// public const Int32 ResponseTimeoutMs = 100;
/// <summary>
/// Timeout before retry will be initiated if the meter is
/// not ready indicated by a wakeup-message or decoding error.
/// </summary>
public const Int32 BusyTimeoutMs = 500;
// public const Int32 BusyTimeoutMs = 1000;
/// <summary>
/// Time to avoid automatic lock off of genesis device due to
/// missing communication with request protocol in seconds
/// </summary>
public const Int32 KeepSessionTimeS = 60 * 3;
/// <summary>
/// Time to update intermediate record for overflow detection
/// during a measurement and update of short-term measurement
/// in seconds
/// </summary>
public const Int32 MeasurementUpdateTimeS = 10;
}
}
@@ -0,0 +1,24 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig.Const
{
public static class MeterConfig
{
/// <summary>
/// Calibration Factor on init from meter
/// Is also the factor to convert CalibrationFactors to readable number,
/// this is the default if the ProccessConfig.json file cannot be read.
/// </summary>
public const UInt16 CalibrationDefault = 15625;
/// <summary>
/// Channels for calibration
/// </summary>
public const Int32 CalibChannels = 3;
/// <summary>
/// Maximum calibration value in percent
/// </summary>
public const Double DefaultMaxCalibFactorTolerancePercent = 100.0;
}
}
@@ -0,0 +1,86 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of meter
/// </summary>
public class MeterConfig
{
/// <summary>
/// ctor
/// </summary>
public MeterConfig()
{
UseRegisterWatchService = false;
UseErrorLogger = false;
}
/// <inheritdoc />
public MeterConfig(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Xylem.Common.Hardware.WaterMeter.Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
if (!File.Exists(configFile))
{
UseRegisterWatchService = false;
UseErrorLogger = false;
return;
}
}
using (var tr = new StreamReader(configFile))
{
var meterConfig = JsonConvert.DeserializeObject<MeterConfig>(tr.ReadToEnd());
UseRegisterWatchService = meterConfig.UseRegisterWatchService;
RegisterWatchServiceUrl = meterConfig.RegisterWatchServiceUrl;
UseMinMaxCheck = meterConfig.UseMinMaxCheck;
UseErrorLogger = meterConfig.UseErrorLogger;
ErrorLoggerServiceUrl = meterConfig.ErrorLoggerServiceUrl;
UseCalibrationLogger = meterConfig.UseCalibrationLogger;
CalibrationLoggerServiceUrl = meterConfig.CalibrationLoggerServiceUrl;
}
}
/// <summary>
///
/// </summary>
public Boolean UseRegisterWatchService { get; set; }
/// <summary>
///
/// </summary>
public String RegisterWatchServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseMinMaxCheck { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseErrorLogger { get; set; }
/// <summary>
///
/// </summary>
public String ErrorLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseCalibrationLogger { get; set; }
/// <summary>
///
/// </summary>
public String CalibrationLoggerServiceUrl { get; set; }
}
}
@@ -0,0 +1,114 @@
using System;
using System.ComponentModel;
using System.IO;
using Newtonsoft.Json;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of meter
/// </summary>
public class ProccessConfig
{
/// <summary>
/// ctor
/// </summary>
public ProccessConfig()
{
UseRegisterWatchService = false;
UseErrorLogger = false;
AutoUpdateFiles = false;
ProductionMode = true;
}
/// <inheritdoc />
public ProccessConfig(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Xylem.Common.Hardware.WaterMeter.Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
if (!File.Exists(configFile))
{
UseRegisterWatchService = false;
UseErrorLogger = false;
return;
}
}
using (var tr = new StreamReader(configFile))
{
var meterConfig = JsonConvert.DeserializeObject<ProccessConfig>(tr.ReadToEnd());
UseRegisterWatchService = meterConfig.UseRegisterWatchService;
RegisterWatchServiceUrl = meterConfig.RegisterWatchServiceUrl;
UseMinMaxCheck = meterConfig.UseMinMaxCheck;
UseErrorLogger = meterConfig.UseErrorLogger;
ErrorLoggerServiceUrl = meterConfig.ErrorLoggerServiceUrl;
UseCalibrationLogger = meterConfig.UseCalibrationLogger;
CalibrationLoggerServiceUrl = meterConfig.CalibrationLoggerServiceUrl;
AutoUpdateFiles = meterConfig.AutoUpdateFiles;
ProductionMode = meterConfig.ProductionMode;
}
}
public void Update(String file)
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(Xylem.Common.Hardware.WaterMeter.Genesis), file);
if (!File.Exists(configFile))
{
configFile = Path.Combine(file);
}
File.WriteAllText(file, JsonConvert.SerializeObject(this));
}
/// <summary>
///
/// </summary>
public Boolean UseRegisterWatchService { get; set; }
/// <summary>
///
/// </summary>
public String RegisterWatchServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseMinMaxCheck { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseErrorLogger { get; set; }
/// <summary>
///
/// </summary>
public String ErrorLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseCalibrationLogger { get; set; }
/// <summary>
///
/// </summary>
public String CalibrationLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public bool AutoUpdateFiles { get; set; }
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public bool ProductionMode { get; set; } = true;
}
}
@@ -0,0 +1,111 @@
using System;
using System.ComponentModel;
using System.IO;
using Newtonsoft.Json;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of meter
/// </summary>
public class ProcessConfig
{
/// <summary>
/// Load configuration file from AppRoaming or working directory
/// </summary>
public Boolean ReadProcessConfig()
{
// Check for AppRoaming
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
nameof(Xylem.Common.Hardware.WaterMeter.Genesis), ProgramConfig.MeterConfigFileName);
if (!File.Exists(configFile))
{
// Take actual working directory
configFile = Path.Combine(ProgramConfig.MeterConfigFileName);
if (!File.Exists(configFile))
{
UseRegisterWatchService = false;
UseErrorLogger = false;
AutoUpdateFiles = false;
ProductionMode = true;
return false;
}
}
using (var tr = new StreamReader(configFile))
{
var meterConfig = JsonConvert.DeserializeObject<ProcessConfig>(tr.ReadToEnd());
UseRegisterWatchService = meterConfig.UseRegisterWatchService;
RegisterWatchServiceUrl = meterConfig.RegisterWatchServiceUrl;
UseMinMaxCheck = meterConfig.UseMinMaxCheck;
UseErrorLogger = meterConfig.UseErrorLogger;
ErrorLoggerServiceUrl = meterConfig.ErrorLoggerServiceUrl;
UseCalibrationLogger = meterConfig.UseCalibrationLogger;
CalibrationLoggerServiceUrl = meterConfig.CalibrationLoggerServiceUrl;
AutoUpdateFiles = meterConfig.AutoUpdateFiles;
ProductionMode = meterConfig.ProductionMode;
}
return true;
}
/// <summary>
/// Write file to AppRoaming and backup it to actual working directory
/// </summary>
public void Update()
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
nameof(Xylem.Common.Hardware.WaterMeter.Genesis), ProgramConfig.MeterConfigFileName);
File.WriteAllText(configFile, JsonConvert.SerializeObject(this));
File.WriteAllText(ProgramConfig.MeterConfigFileName, JsonConvert.SerializeObject(this));
}
/// <summary>
///
/// </summary>
public Boolean UseRegisterWatchService { get; set; }
/// <summary>
///
/// </summary>
public String RegisterWatchServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseMinMaxCheck { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseErrorLogger { get; set; }
/// <summary>
///
/// </summary>
public String ErrorLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean UseCalibrationLogger { get; set; }
/// <summary>
///
/// </summary>
public String CalibrationLoggerServiceUrl { get; set; }
/// <summary>
///
/// </summary>
public Boolean AutoUpdateFiles { get; set; }
[DefaultValue(true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
public Boolean ProductionMode { get; set; }
}
}
@@ -0,0 +1,96 @@
using System;
using System.IO;
using Newtonsoft.Json;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig
{
/// <summary>
/// Configuration of SIRT
/// </summary>
public class SirtConfig
{
/// <summary>
/// Load SIRT configuration file from AppRoaming or working directory
/// </summary>
public Boolean ReadSirtConfig()
{
// Check for AppRoaming
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
nameof(Xylem.Common.Hardware.WaterMeter.Genesis), ProgramConfig.SirtConfigFileName);
if (!File.Exists(configFile))
{
// Take actual working directory
configFile = Path.Combine(ProgramConfig.SirtConfigFileName);
if (!File.Exists(configFile))
{
SirtComport433MHz = null;
SirtComport868MHz = null;
ServiceSirtComport868MHz = null;
ServiceSirtComport433MHz = null;
SirtBoxNo = null;
StationId = null;
return false;
}
}
using (var tr = new StreamReader(configFile))
{
var sirtConfig = JsonConvert.DeserializeObject<SirtConfig>(tr.ReadToEnd());
SirtComport433MHz = sirtConfig.SirtComport433MHz;
SirtComport868MHz = sirtConfig.SirtComport868MHz;
ServiceSirtComport433MHz = sirtConfig.ServiceSirtComport433MHz; ;
ServiceSirtComport868MHz = sirtConfig.ServiceSirtComport868MHz; ;
SirtBoxNo = sirtConfig.SirtBoxNo;
StationId = sirtConfig.StationId;
}
return true;
}
/// <summary>
/// Write file to AppRoaming and backup it to actual working directory
/// </summary>
public void Update()
{
var configFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
nameof(Xylem.Common.Hardware.WaterMeter.Genesis), ProgramConfig.SirtConfigFileName);
File.WriteAllText(configFile, JsonConvert.SerializeObject(this));
File.WriteAllText(ProgramConfig.SirtConfigFileName, JsonConvert.SerializeObject(this));
}
/// <summary>
/// Comport for SIRT interface with 433MHz radio frequency
/// </summary>
public String SirtComport433MHz { get; set; }
/// <summary>
/// Comport for SIRT interface with 868MHz radio frequency
/// </summary>
public String SirtComport868MHz { get; set; }
/// <summary>
/// Box number for calibration settings
/// </summary>
public Int32? SirtBoxNo { get; set; }
/// <summary>
/// Station ID where the SIRT is used
/// </summary>
public Int32? StationId { get; set; }
/// <summary>
/// Service Comport for SIRT interface with 433MHz radio frequency
/// </summary>
public String ServiceSirtComport433MHz { get; set; }
/// <summary>
/// Service Comport for SIRT interface with 868MHz radio frequency
/// </summary>
public String ServiceSirtComport868MHz { get; set; }
}
}
@@ -0,0 +1,107 @@
using System;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Calibration content
/// </summary>
public class CalibFactor
{
/// <summary>
/// has been calibrated
/// </summary>
public Boolean IsCalculated;
/// <summary>
/// Channel for calibration
/// </summary>
public Int32 Channel { get; }
/// <summary>
/// Register to store the calibration
/// </summary>
public String RegisterToStoreCalibFactor { get; }
/// <summary>
/// Ctor for calibration factor
/// </summary>
/// <param name="channel"></param>
/// <param name="registerToStoreCalibFactor"></param>
public CalibFactor(Int32 channel, String registerToStoreCalibFactor)
{
Channel = channel;
RegisterToStoreCalibFactor = registerToStoreCalibFactor;
IsCalculated = false;
}
private UInt16 _meterRawCalibFactor = MeterConfig.CalibrationDefault;
/// <summary>
/// set the user readable relative (around 1.0) calibration factor
/// and convert it to the raw calibration factor for the meter
/// </summary>
/// <param name="relativeCalibFactor"></param>
/// <exception cref="Exception"></exception>
public void BuildMeterCalibFactor(Double relativeCalibFactor)
{
try
{
var tmp = Math.Round(relativeCalibFactor * GetMeterRawCalibFactor(), 0);
_meterRawCalibFactor = Convert.ToUInt16(tmp);
}
catch (Exception ex)
{
throw new Exception("Unable to set calibration factor", ex);
}
}
/// <summary>
/// Read the relative (around 1.0) calibration factor calculated from the actual
/// meter calibration factor divided by the default calibration factor
/// </summary>
/// <returns></returns>
public Double GetRelativeCalibFactor()
{
Double calibFactor = GetMeterRawCalibFactor();
var calibDivider = GetMeterRawCalibFactorDefault();
if (0 != calibDivider)
{
calibFactor /= calibDivider;
}
else
{
throw new ApplicationException("Default calibration factor row value is 0");
}
return calibFactor;
}
/// <summary>
/// Genesis internal raw default value (around 15625)
/// </summary>
/// <returns></returns>
public UInt16 GetMeterRawCalibFactorDefault()
{
return MeterConfig.CalibrationDefault;
}
/// <summary>
/// Set the meter raw calibration factor (around 15625)
/// </summary>
/// <param name="meterRawCalibFactor"></param>
public void SetMeterRawCalibFactor(UInt16 meterRawCalibFactor)
{
_meterRawCalibFactor = meterRawCalibFactor;
}
/// <summary>
/// Get the meter raw calibration factor (around 15625)
/// </summary>
/// <returns></returns>
public UInt16 GetMeterRawCalibFactor()
{
return _meterRawCalibFactor;
}
}
}
@@ -0,0 +1,37 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts
{
/// <summary>
/// Alarm messages
/// </summary>
[Flags]
public enum Alarm
{
// ReSharper disable InconsistentNaming
#pragma warning disable CS1591
REBOOT = 1 << 0, //Bit 0
LOW_BATTERY = 1 << 1, //Bit 1
//Bit 2
//Bit 3
EMPTY_PIPE = 1 << 4, //Bit 4
//Bit 5
REVERSE_FLOW = 1 << 6, //Bit 6
SUSPECT_LEAK = 1 << 7, //Bit 7
BROKEN_PIPE = 1 << 8, //Bit 8
LOW_PRESSURE = 1 << 9, //Bit 9
HIGH_PRESSURE = 1 << 10, //Bit 10
LOW_TEMPERATURE = 1 << 11, //Bit 11
HIGH_TEMPERATURE = 1 << 12, //Bit 12
RADIO_ERROR = 1 << 13, //Bit 13
METROLOGY_PARAMS = 1 << 14, //Bit 14
METROLOGY_MEASURE = 1 << 15, //Bit 15
ALL = 0x7FFFFFFF //enum is defined as Int32
#pragma warning restore CS1591
// ReSharper restore InconsistentNaming
}
}
@@ -0,0 +1,6 @@
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts
{
class Applications
{
}
}
@@ -0,0 +1,66 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts
{
/// <summary>
/// Common definition for Meter handling in production and on test benches
/// </summary>
/// <summary>
/// represents any error state a meter can have
/// if hash code is 0 everything is running
/// is Flags, so watch out to check with hasFlag!
/// </summary>
[Flags]
public enum ErrorState
{
/// <summary>
/// everything is good
/// </summary>
None = 0x0000,
/// <summary>
/// problems on initialization
/// </summary>
InitFailed = 0x0001,
/// <summary>
/// problems on Measurement
/// </summary>
MeasurementFailed = 0x0002,
/// <summary>
/// Problem on communication with meter
/// </summary>
ComErrorRequestPort = 0x0004,
/// <summary>
/// Problem on optical output on meter
/// </summary>
ComErrorLed = 0x0008,
/// <summary>
/// if pulse can not readout
/// </summary>
ComErrorPulse = 0x0010,
/// <summary>
/// no (or no good) reference flow available
/// </summary>
ErrorQRef = 0x0020,
/// <summary>
/// Calibration went wrong
/// </summary>
CalibrationFailed = 0x0040,
/// <summary>
/// calibration is out of range. Check documentation from meter to find limitation
/// </summary>
CalibrationOutOfRange = 0x0080,
/// <summary>
/// blue screen like error
/// </summary>
FatalError = 0x0100
}
}
@@ -0,0 +1,38 @@
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts
{
/// <summary>
/// Streaming modes that supported by genesis meter.
/// Set meter to this Streaming mode means that , the meter pushes mode-specific record to port without any responses
/// </summary>
public enum LedMode
{
/// <summary>
/// Test mode with raw record to log.
/// Should give one <see cref="FlowTestRecord"/>
/// 3 times <see cref="CalibrationRecord"/> and
/// one <see cref="BendDetectionRecord"/>
/// </summary>
FlowTestAndCalibDataAndBendCorrect = 7,
/// <summary>
/// Test mode with raw record to log. Should give one <see cref="FlowTestRecord"/>
/// 3 times <see cref="CalibrationRecord"/> and repeat this till led mode is switch
/// </summary>
FlowTestAndCalibData = 6,
/// <summary>
/// Test mode should give <see cref="FlowTestRecord"/> out
/// </summary>
FlowTestData = 4,
/// <summary>
/// Calibration mode should give <see cref="CalibrationRecord"/> out
/// </summary>
CalibData = 3,
/// <summary>
/// Deactivate streaming
/// </summary>
Off = 0,
/// <summary>
/// not set or an new not supported Streaming mode
/// </summary>
Unknown = -1
}
}
@@ -0,0 +1,20 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts
{
/// <summary>
/// Update capability check for all necessary settings according to meter.
/// </summary>
public static class MetrologyDefinition
{
/// <summary>
/// It is allowed to upgrade the metrology, causing an upgrade capability check to pass always.
/// </summary>
public const Byte MetrologyUpgradePermitted = 0xFF;
/// <summary>
/// The name of the metrology application.
/// </summary>
public const String MetrologyName = "GENESISFLOW";
}
}
@@ -0,0 +1,86 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts
{
/// <summary>
/// Power correction constants
/// </summary>
public static class PowCorrConst
{
/// <summary>
/// Threshold for power correction EMEA version
/// </summary>
public const UInt32 EmeaFwThresholdForPowCorr = 0x1300;
/// <summary>
/// Threshold for power correction NA version
/// </summary>
public const UInt32 NaFNaFwThresholdForPowCorr = 0x2006;
/// <summary>
/// Minimum region radio current NA
/// </summary>
public const Double MinNaCurrent_uA = 119;
/// <summary>
/// Minimum region radio current sensus radio RF mode EMEA
/// </summary>
public const Double MinEmeaRadioRfModeCurrent_uA = 136;
/// <summary>
/// Minimum region radio current sensus radio TFX mode EMEA
/// </summary>
public const Double MinEmeaRadioTfxModeCurrent_uA = 155;
/// <summary>
/// Fixed current in production if production values are not logged in DB
/// and dispatched as 0 or null.
/// </summary>
public const Double FixProductionCurrent_uA = 160;
/// <summary>
/// Fixed region radio current NA
/// </summary>
public const Double FixNaCurrent_uA = 132;
/// <summary>
/// Fixed region radio current sensus radio RF mode EMEA
/// </summary>
public const Double PulseReportLengthEstimate_uA = 45.453;
/// <summary>
/// Fixed region radio current sensus radio RF mode EMEA
/// </summary>
public const Double FixEmeaRadioRfModeCurrent_uA = 153;
/// <summary>
/// Fixed region radio current sensus radio TFX mode EMEA
/// </summary>
public const Double FixEmeaRadioTfxModeCurrent_uA = 174;
/// <summary>
/// Pulse report length for pulse mode 1 to 4 even distribution 0
/// </summary>
public const UInt32 PulseReportLengthMode1To4Even0 = 8;
/// <summary>
/// Pulse report length for pulse mode 1 to 4 even distribution 1
/// </summary>
public const UInt32 PulseReportLengthMode1To4Even1 = 10;
/// <summary>
/// Pulse report length for pulse mode 5 to 6 even distribution 0
/// </summary>
public const UInt32 PulseReportLengthMode5To6Even0 = 6;
/// <summary>
/// Pulse report length for pulse mode 5 to 6 even distribution 1
/// </summary>
public const UInt32 PulseReportLengthMode5To6Even1 = 8;
/// <summary>
/// TFX mode mask of sensus radio system state
/// </summary>
public const Byte SensusRadioSystemStateTfXModeMask = 0x20;
}
}
@@ -0,0 +1,359 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json;
using Xylem.Common.Hardware.WaterMeter.WaterMeterRegisters;
using IRegister = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.WaterMeterCore.WaterMeterRegisters.IRegister;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// JSON filer reader for generation of register lists
/// </summary>
public class GenesisConfigurationReader
{
/// <summary>
/// Registers defined in configuration.json file
/// </summary>
public List<IRegister> ConfigRegistersDefinitions { get; }
= new List<IRegister>();
/// <summary>
/// Applications defined in configuration.json file
/// </summary>
public List<ApplicationDefinition> ConfigApplicationDefinitions { get; }
= new List<ApplicationDefinition>();
/// <summary>
/// Information collected during 'configuration.json' read
/// </summary>
public readonly InterfaceInfo InterfaceInfo = new InterfaceInfo();
/// <summary>
/// Ctor
/// </summary>
public GenesisConfigurationReader() { }
/// <summary>
/// Ctor
/// </summary>
/// <param name="configFilePath">the file path for configuration.json as interface description to the meter</param>
public GenesisConfigurationReader(String configFilePath)
{
if (string.IsNullOrEmpty(configFilePath))
{
throw new ApplicationException("JsonFileRegisterReader needs at least one file path");
}
if (!File.Exists(configFilePath))
{
throw new ApplicationException($" {configFilePath} doesn't exists");
}
BuildRegisterList(File.ReadAllText(configFilePath));
}
/// <summary>
/// Convert file content to register list
/// </summary>
/// <param name="configurationJson"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
/// <remarks date="??" author="Stoyan Slatev">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Aug-05" author="Thomas Wiedebusch">
/// - AppId from Byte to UInt16 including typecast for Byte[] return. To external, it will be used as Byte
/// as before this change. But being able to parse the configuration.json with OPTICALINTERFACE using the
/// AppId 256, which exceeds the byte range as this isn't a real application, but will be used to identify
/// the interface (configuration.json) version.
/// </remarks>
/// <remarks date="2025-Aug-07" author="Thomas Wiedebusch">
/// - Extract the max supported versions for EMEA and NA to check the supported FW.
/// </remarks>
/// <remarks date="2025-Sep-30" author="Thomas Wiedebusch">
/// - Supported application list introduced.
/// </remarks>
/// <remarks date="2025-Oct-10" author="Thomas Wiedebusch">
/// - Data size introduced including the new 'ByteArray' type and size to get rid of new defined 'uintxx_t'
/// data types.
/// </remarks>
public void BuildRegisterList(String configurationJson)
{
IDictionary<String, AppSection> sections;
try
{
sections = JsonConvert
.DeserializeObject<IDictionary<String, AppSection>>(configurationJson, SerializerSettings)
?? new Dictionary<String, AppSection>();
}
catch (Exception)
{
sections = new Dictionary<String, AppSection>();
}
foreach (var sectionKVP in sections)
{
var appId = sectionKVP.Value.Id;
var appName = sectionKVP.Key;
var appVersion = sectionKVP.Value.Version.Last;
if (appName.Equals("OPTICALINTERFACE"))
{
var builds = sectionKVP.Value.Builds;
var emeaBuilds = builds["emea"];
var naBuilds = builds["na"];
InterfaceInfo.SupportedFwVersions = new List<String>();
foreach (var build in emeaBuilds)
{
InterfaceInfo.SupportedFwVersions.Add(build.FW);
}
foreach (var build in naBuilds)
{
InterfaceInfo.SupportedFwVersions.Add(build.FW);
}
InterfaceInfo.InterfaceVersion = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter.BuildFwVersionStringFromDec(appVersion);
continue;
}
ConfigApplicationDefinitions.Add(new ApplicationDefinition
{
AppId = appId,
AppName = appName,
AppVersion = appVersion,
});
var section = sectionKVP.Value;
var registers = section.Registers;
if (registers is null)
{
continue;
}
foreach (var registerKVP in registers)
{
var register = registerKVP.Value;
if (register is null)
{
continue;
}
var details = register.Details;
if (details is null)
{
continue;
}
var regId = register.Id;
foreach (var detail in details)
{
var values = detail.Values;
ConfigRegistersDefinitions.Add(new RegisterDefinition
{
AppAddress = appId,
AppName = appName,
DataType = GetType(detail.Type),
DataSize = GetSize(detail.Type),
Default = values?.Default is Int64 d ? d : default(Int64?),
IsAvailable = false,
Maximum = values?.Maximum is Int64 max ? max : default(Int64?),
Minimum = values?.Minimum is Int64 min ? min : default(Int64?),
RegAddressInApp = regId,
// RegisterAddress = new byte[] { appId, regId },
RegisterDetail = detail,
RegisterName = registerKVP.Key,
RestoreCapability = new StaticType(detail.StaticType)
});
}
}
}
}
/// <summary>
/// Settings
/// </summary>
private static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
AccessConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
/// <summary>
/// Convert data types from interface 'configuration.json' to CLR type or array
/// </summary>
/// <param name="value"></param>
/// <returns>type</returns>
/// <exception cref="Exception"></exception>
/// <remarks date="??" author="Stoyan Slatev">
/// - Initial.
/// </remarks>
/// <remarks date="2025-Oct-10" author="Thomas Wiedebusch">
/// - Parsed all 'uintxx_t' which are not based on CLR types to byte-array
/// </remarks>
private static Type GetType(String value)
{
// Catch all uintxx_t to split to standard CLR or to array
var lowStrValue = value.ToLower();
if (lowStrValue.Contains("uint") && lowStrValue.Contains("_t"))
{
switch (lowStrValue)
{
case "uint8_t": return typeof(Byte);
case "uint16_t": return typeof(UInt16);
case "uint32_t": return typeof(UInt32);
case "uint64_t": return typeof(UInt64);
default:
return typeof(ByteArray);
}
}
// Catch the signed standard CLR and some special types
switch (lowStrValue)
{
// CLR types
case "bool_t": return typeof(Boolean);
case "int8_t": return typeof(SByte);
case "int16_t": return typeof(Int16);
case "int32_t": return typeof(Int32);
case "int64_t": return typeof(Int64);
case "string": return typeof(String);
// Special types defined in FW interface file 'configuration.json'
case "rpc": return typeof(Rpc);
case "time_t": return typeof(TimeT);
case "status_t": return typeof(StatusT);
case "enum8": return typeof(Enum8);
default:
throw new Exception($"Data type {value} in unknown!");
}
}
/// <summary>
/// Build the data size
/// </summary>
/// <param name="value"></param>
/// <returns>size</returns>
/// <remarks date="2025-Oct-10" author="Thomas Wiedebusch">
/// - Initial: - parsed all 'uintxx_t' which are not based on CLR types to byte-array
/// </remarks>
private static Int32 GetSize(String value)
{
// Catch all uintxx_t to split to standard CLR or to array
var lowStrValue = value.ToLower();
if (lowStrValue.Contains("uint") && lowStrValue.Contains("_t"))
{
switch (lowStrValue)
{
case "uint8_t": return sizeof(Byte);
case "uint16_t": return sizeof(UInt16);
case "uint32_t": return sizeof(UInt32);
case "uint64_t": return sizeof(UInt64);
default:
// Extract the size from the string
var dataSizeStr = Regex.Replace(lowStrValue, "[^0-9]", string.Empty);
if (Int32.TryParse(dataSizeStr, out var dataSize))
return dataSize / 8;
throw new Exception($"Data size {lowStrValue} cannot be converted!");
}
}
// Catch the signed standard CLR and some special types
switch (lowStrValue)
{
// CLR types
case "bool_t": return sizeof(Boolean);
case "int8_t": return sizeof(SByte);
case "int16_t": return sizeof(Int16);
case "int32_t": return sizeof(Int32);
case "int64_t": return sizeof(Int64);
// Take just some value as the string length is unknown
case "string": return 6 * 4;
// Special types defined in FW interface file 'configuration.json'
// Remote procedure call is always a 4 byte value
case "rpc": return sizeof(UInt32);
// Time will be in seconds since 01. Jan 2000 00:00:00 UTC as signed Int32
case "time_t": return sizeof(Int32);
case "status_t": return sizeof(UInt32);
case "enum8": return sizeof(Byte);
default:
throw new Exception($"Data type {value} in unknown!");
}
}
}
/// <summary>
///
/// </summary>
public class AccessConverter : JsonConverter
{
/// <summary>
///
/// </summary>
/// <param name="t"></param>
/// <returns></returns>
public override Boolean CanConvert(Type t) => t == typeof(Access) || t == typeof(Access?);
/// <summary>
///
/// </summary>
/// <param name="reader"></param>
/// <param name="t"></param>
/// <param name="existingValue"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public override Object ReadJson(JsonReader reader, Type t, Object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
var value = serializer.Deserialize<String>(reader);
return Enum.TryParse(value, true, out Access access) ? access : default(Access);
}
/// <summary>
///
/// </summary>
/// <param name="writer"></param>
/// <param name="untypedValue"></param>
/// <param name="serializer"></param>
public override void WriteJson(JsonWriter writer, Object untypedValue, JsonSerializer serializer)
{
if (untypedValue is Access access)
{
writer.WriteValue(access.ToString());
}
else
{
throw new Exception("Cannot marshal type Lvl");
}
}
/// <summary>
///
/// </summary>
public static readonly AccessConverter Singleton = new AccessConverter();
}
}
@@ -0,0 +1,214 @@
using System;
using System.Collections.Generic;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.SerialPorts;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol;
using Xylem.Common.CommonCore.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Check the positioning of the streaming LED to validate the quality of the communication line
/// </summary>
public class GenesisMeterStreamingQuality : TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter
{
/// <summary>
/// Backup of last quality
/// </summary>
public QualityResult LastQualityResult;
private StreamingDecoder _sd;
/// <summary>
/// Quality check is active
/// </summary>
public Boolean IsRunning;
private DateTimeOffset? _startTime;
private DateTimeOffset? _stopTime;
private List<String> _collectedRecords;
private Int32? _tillDecodedCount;
/// <summary>
/// Kick off the streaming quality check.
/// </summary>
/// <param name="tillDecodedCount"></param>
/// <exception cref="ApplicationException"></exception>
public void StartStreamingQualityCheck(Int32? tillDecodedCount = null)
{
if (IsRunning)
{
return;
}
_tillDecodedCount = tillDecodedCount;
if (_tillDecodedCount.HasValue)
{
_sd = new StreamingDecoder(false);
}
if (StreamingPort == null || !(StreamingPort is LedSerialPort))
{
throw new ApplicationException("No valid streaming port");
}
_stopTime = null;
_collectedRecords = new List<String>();
SyncStreamingBuffer(SyncMarkRecord.DecodeEveryPackage);
((LedSerialPort)StreamingPort).OnRawRecordReceived += GenesisMeterStreamingQuality_OnRawRecordReceived;
_startTime = DateTimeOffset.UtcNow;
LogRawData(true);
IsRunning = true;
}
private void GenesisMeterStreamingQuality_OnRawRecordReceived(Object sender, BasePortDataEventArgs e)
{
_collectedRecords.Add(e.GetData().ToString());
if (_tillDecodedCount.HasValue)
{
if (_sd.DecodeMsg(e.GetData().ToString()))
{
_tillDecodedCount = _tillDecodedCount.Value - 1;
if (_tillDecodedCount <= 0)
{
IsRunning = false;
LastQualityResult = StopStreamingQualityCheck(10);
}
}
}
}
/// <summary>
/// Stop the streaming check.
/// </summary>
/// <param name="sampleRateHz"></param>
/// <returns></returns>
/// <exception cref="ApplicationException"></exception>
public QualityResult StopStreamingQualityCheck(Int32 sampleRateHz)
{
_stopTime = DateTimeOffset.UtcNow;
if (!IsRunning && !_startTime.HasValue)
{
return new QualityResult();
}
if (StreamingPort == null || !(StreamingPort is LedSerialPort))
{
throw new ApplicationException("No valid streaming port");
}
LogRawData(false);
((LedSerialPort)StreamingPort).OnRawRecordReceived -= GenesisMeterStreamingQuality_OnRawRecordReceived;
SyncStreamingBuffer(SyncMarkRecord.SkipDecoding);
IsRunning = false;
if (_stopTime != null && _startTime != null)
return new QualityResult(_collectedRecords, _stopTime.Value - _startTime.Value, sampleRateHz);
return new QualityResult();
}
/// <summary>
///
/// </summary>
public class QualityResult
{
/// <summary>
/// Number of records received (read line for LED message)
/// </summary>
public readonly Int32 TotalLinesCount;
/// <summary>
/// Total records decoded
/// </summary>
public readonly Int32 TotalDecodedCount;
/// <summary>
/// Total errors
/// </summary>
public readonly Int32 TotalTransportErrorCount;
/// <summary>
/// Validated without errors
/// </summary>
public readonly Int32 TotalValidationWithoutErrorsCount;
/// <summary>
/// Expected lines
/// </summary>
public readonly Int32 ExpectedLinesCount;
/// <summary>
/// Time
/// </summary>
public readonly TimeSpan TotalTime;
/// <summary>
/// Error counter
/// </summary>
public readonly Dictionary<UInt16, Int32> ErrorOccurredCounter;
/// <summary>
/// The real result of quality check
/// </summary>
/// <param name="collectedRecords"></param>
/// <param name="ts"></param>
/// <param name="sampleRateHz"></param>
public QualityResult(List<String> collectedRecords, TimeSpan ts, Int32 sampleRateHz)
{
TotalLinesCount = collectedRecords.Count;
TotalTime = ts;
ExpectedLinesCount = (Int32)(Math.Round(ts.TotalSeconds * sampleRateHz, 0));
ErrorOccurredCounter = new Dictionary<UInt16, Int32>();
foreach (var rawText in collectedRecords)
{
var sd = new StreamingDecoder(false);
if (!sd.DecodeMsg(rawText))
{
TotalTransportErrorCount++;
continue;
}
//here the decoding is valid including the CRC
TotalDecodedCount++;
//the DecodeMsg has assigned either a DataCalib or DataFlowTest data set
var calibrationRecord = sd.DataCalib;
var flowTestRecord = sd.DataFlowTest;
var bendDetectRecord = sd.DataBendDetectTest;
if (flowTestRecord != null)
{
TotalValidationWithoutErrorsCount++;
continue;
}
if (bendDetectRecord != null)
{
TotalValidationWithoutErrorsCount++;
continue;
}
if (calibrationRecord != null)
{
if (calibrationRecord.Validation == 0)
{
TotalValidationWithoutErrorsCount++;
}
else
{
if (ErrorOccurredCounter.ContainsKey(calibrationRecord.Validation))
{
ErrorOccurredCounter[calibrationRecord.Validation] += 1;
}
else
{
ErrorOccurredCounter.Add(calibrationRecord.Validation, 1);
}
}
}
}
}
/// <summary>
/// The quality result as dummy zero
/// </summary>
public QualityResult()
{
TotalLinesCount = 0;
TotalTime = TimeSpan.Zero;
ErrorOccurredCounter = new Dictionary<UInt16, Int32>();
}
}
}
}
@@ -0,0 +1,298 @@
using System;
using System.Collections.Generic;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Applications;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using IMeter = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.WaterMeterCore.IMeter;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Interface of special GenesisMeter derived from IMeter
/// </summary>
public interface IGenesisMeter : IMeter
{
#region properties
/// <summary>
/// Represent <see cref="Register.Configexchange.PcbSerialNumber" />
/// proposed for login
/// </summary>
String PcbId
{
get;
}
/// <summary>
/// A new meter has to be checked for the reboot counter. On retries the reboot counter
/// may have increased on unsuccessfully after-reboot procedure. This would force failing
/// the 'ConnectCordonel'.
/// </summary>
Boolean CheckRebootCtr
{
get;
set;
}
/// <summary>
/// Interface information
/// </summary>
InterfaceInfo InterfaceInfo
{
get;
}
/// <summary>
/// This FW version is supported by the interface "configuration.json".
/// </summary>
Boolean InterfaceSupportsFwVersion
{
get;
}
/// <summary>
/// Transmit protocol access for underlie objects to change response timeout
/// </summary>
ITransmitProtocol TransmitProtocol { get; }
/// <summary>
/// List of all present meter applications installed in meter
/// </summary>
List<MeterApplications> MeterAppListVersion
{
get;
}
/// <summary>
/// Successfully logged on to meter
/// </summary>
Boolean IsLoggedOn
{
get;
}
/// <summary>
/// Core revision of boot code.
/// </summary>
Int32? CoreRevision
{
get;
}
/// <summary>
/// Core revision of boot code as string.
/// </summary>
String StrCoreRevision
{
get;
}
/// <summary>
/// This is the FLEXNETVERSION version which describes the entire packet.
/// </summary>
String FwVersion
{
get;
}
/// <summary>
/// This is the FLEXNETVERSION version which describes the entire packet.
/// </summary>
UInt32? InstalledFwVersion
{
get;
}
/// <summary>
/// This is the Metrology Lookup Table CRC.
/// </summary>
String LutCrc
{
get;
}
/// <summary>
/// This is the meter size.
/// </summary>
String MeterSize { get; }
/// <summary>
/// Length of the meter.
/// </summary>
String MeterLength { get; set; }
/// <summary>
/// Pressure sensor assembled to meter.
/// </summary>
Boolean PressureSensorAssembled { get; }
/// <summary>
/// Region (EMEA, NA or China).
/// </summary>
String Region
{
get;
}
/// <summary>
/// Radio frequency in MHz (433 or 868 or null).
/// </summary>
Int32? RadioFrequencyMhz
{
get;
}
/// <summary>
/// Metrology upgrade permission.
/// </summary>
Byte MetrologyUpgradePermission
{
set; get;
}
/// <summary>
/// Order number under which this meter has to be produced
/// </summary>
Int32 OrderNumber
{
set; get;
}
/// <summary>
/// Radio address
/// </summary>
Int64 RadioAddress
{
set; get;
}
/// <summary>
/// Process configuration
/// </summary>
ProcessConfig Configuration
{
get;
}
/// <summary>
/// Customer serial number for informal issues
/// </summary>
String CustomerSerialNumber
{
get; set;
}
/// <summary>
/// Unlocked the property change ability for special sequences.
/// </summary>
Boolean UnlockEssentialProperties
{
get;
}
#endregion
#region methods
/// <summary>
/// Check region and size.
/// After reading the version, all valid registers are going to be selected.
/// </summary>
void CheckRegionSizeLutCrc();
/// <summary>
/// Unlocked the property change ability for special sequences.
/// </summary>
Boolean EnablePropertyChangeAbility(Boolean unlockPropertyChange);
/// <summary>
/// Set the meter size if previously unlocked.
/// The data types are: - String like "DN50" or
/// </summary>
Boolean SetMeterSize<T>(T meterSize);
/// <summary>
/// Set the pressure sensor if previously unlocked
/// </summary>
Boolean SetPressureSensorAssembled(Boolean pressureSensorAssembled);
/// <summary>
/// Reset the empty pipe alarm
/// </summary>
void ResetAlarm(Alarm clear);
/// <summary>
/// Upload app list to database
/// </summary>
/// <param name="progressName"></param>
/// <param name="version"></param>
/// <returns></returns>
StatusReturn PushProgress(String progressName, String version);
/// <summary>
/// Clear password to force new password reading
/// </summary>
void ClearPassword();
/// <summary>
/// Executes all StoreConfiguration and StoreCalibration for each application.
/// </summary>
/// <returns>true if all configurations are stored</returns>
Boolean StoreAllConfigurations();
/// <summary>
/// Login with identical password as last time to avoid get password from database
/// </summary>
Boolean ReLogin();
/// <summary>
/// Returns meter registers
/// </summary>
/// <returns></returns>
MeterRegisters GetConfigRegistersDefinitions();
/// <summary>
/// Common routine for reboot being able to override this routine which will be called in
/// MeteResetPsu to simulate a reboot.
/// </summary>
/// <returns></returns>
StatusReturn RebootGenesis();
/// <summary>
/// Write Register, optional: wait for result and validate,
/// write register will ALWAYS log the data DON'T use for password write
/// </summary>
/// <typeparam name="T">Data type of Register</typeparam>
/// <param name="regName">Register name</param>
/// <param name="value">Value to save as byte array in raw format</param>
/// <param name="waitForResult">wait until result is ready</param>
/// <param name="checkRegister">check the content of the register by read back</param>
/// <param name="skipRetryErrorCode">skip retries on this error code return</param>
/// <returns>true on successful operation</returns>
Boolean WriteRegister<T>(String regName, T value, Boolean waitForResult = true,
Boolean checkRegister = false, UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode);
/// <summary>
/// Read Register and return byte array
/// </summary>
/// <param name="regName">Register name</param>
/// <param name="expectedLength">expected length for response</param>
/// <param name="skipRetryErrorCode">skip retries on this error code return</param>
/// <returns>value as byte array in raw format or null</returns>
Byte[] ReadRegister(String regName, Int32? expectedLength = null,
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode);
/// <summary>
/// Clear all pending alarms.
/// </summary>
/// <remarks date="2025-Aug-26" author="Roland Drabesch">
/// - Initial.
/// </remarks>
void ClearAlarm();
#endregion
}
}
@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Collection of interface information
/// </summary>
public class InterfaceInfo
{
/// <summary>
/// The interface version
/// </summary>
public String InterfaceVersion { get; set; }
/// <summary>
/// List of supported FW versions by this interface
/// </summary>
public List<String> SupportedFwVersions { get; set; }
}
}
@@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using Newtonsoft.Json;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.ProcessExec;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.ProcessExec.EventArguments;
using Xylem.Common.Hardware.WaterMeter.WaterMeterCore;
using MeterBatch = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.WaterMeterCore.MeterBatch;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Port scanner:
/// using the Windows Device Manager listed ports,
/// tries to open the port with an exception if it cannot be accessed (very time-consuming),
/// tries to use the request protocol to detect a Genesis device.
/// </summary>
public class MeterPortScanner : IProcessState, IDisposable
{
/// <summary>
/// Genesis for test access
/// </summary>
private TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter _currentGenesis;
private MeterBatch _meterBatch;
private readonly String _portType;
/// <summary>
/// Auto-detected port name
/// </summary>
public String AutoDetectedPortName;
/// <summary>
/// Port scan result event for message dispatcher to caller
/// </summary>
public event EventHandler<ProcessExecEventArgs> OnProcessUpdate;
/// <summary>
/// Stop the port scan
/// </summary>
public Boolean StopPortScan;
/// <summary>
/// Number of ports
/// </summary>
public Int32 NumberOfPorts;
/// <summary>
/// Actual Port Counter
/// </summary>
public Int32 ActualPortCtr;
/// <summary>
/// Returns the port scan state
/// </summary>
/// <returns></returns>
/// <remarks date="2020-Oct-21" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public String GetPortScanState()
{
return $@"{Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources.StrScanPort} {ActualPortCtr}/{NumberOfPorts}";
}
/// <summary>
/// Ctor
/// </summary>
/// <returns>true if one port has been validated</returns>
/// <remarks date="2020-Dec-11" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <param name="portType">type of communication port to meter like IrDA</param>
public MeterPortScanner(String portType)
{
_portType = portType;
}
/// <inheritdoc />
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
/// - Initial.
/// </remarks>
/// <remarks date="2021-Mai-11" author="Thomas Wiedebusch">
/// - Remove all meters removed as _meterBatch.Dispose will remove all meters.
/// </remarks>
public void Dispose()
{
// removal from meter batch includes a disposal of meter
_meterBatch?.Dispose();
}
/// <summary>
/// Use initially the port configuration to speed up search,
/// If configuration file contains wrong port setup than scan all serial ports listed in
/// the windows device manager,
/// Read available ports,
/// Try to open port and connect to Genesis meter.
/// </summary>
/// <param name="slot">slot to search for</param>
/// <param name="portConfigFilePathName">configuration of port to speed up search</param>
/// <returns>true if port found</returns>
/// <returns>true if one port has been validated</returns>
/// <remarks date="2020-Oct-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
/// <remarks date="2020-Dec-11" author="Thomas Wiedebusch">
/// - Port type from Ctor,
/// - Event message changed:
/// - Overall message: ctr / counts - ongoing scan,
/// - Actual message: Information to log
/// </remarks>
/// <remarks date="2021-Jan-05" author="Thomas Wiedebusch">
/// - Changed exit of function,
/// - Added configuration file handling.
/// </remarks>
/// <remarks date="2021-Mai-11" author="Thomas Wiedebusch">
/// - Remove all meters removed as _meterBatch.Dispose will remove all meters.
/// </remarks>
public Boolean ScanAllSerialPorts(Int32 slot, String portConfigFilePathName)
{
var serialPort = new SerialPort();
var serialPorts = new List<String>();
_meterBatch = new MeterBatch();
serialPorts.AddRange(SerialPort.GetPortNames());
NumberOfPorts = serialPorts.Count;
ActualPortCtr = 0;
// If configuration file exists, read it and check for slot setup. This will speed up the recurrent detection
// process as at the end the detection process the detected slot will be saved.
var slotPortConfigs = new List<SlotConfig>();
if (portConfigFilePathName != null && File.Exists(portConfigFilePathName))
{
using (var tr = new StreamReader(portConfigFilePathName))
{
var fileStream = tr.ReadToEnd();
var slots = JsonConvert.DeserializeObject<SlotConfig[]>(fileStream);
if (slots != null && slots.Length > 0)
slotPortConfigs.AddRange(slots);
}
foreach (var slotPortConfig in slotPortConfigs.Where(slotPortConfig =>
slotPortConfig.Slot == slot && !string.IsNullOrEmpty(slotPortConfig.Request.PortName)))
{
// add port on bottom of port list even if it is doubled that way
NumberOfPorts++;
serialPorts.Add(slotPortConfig.Request.PortName);
break;
}
}
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs("", 0,
$@"{NumberOfPorts} {Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources.StrPortListDeviceManger}"));
var serialPortDetected = false;
StopPortScan = false;
// start with last port,this might be added at current connection of interface
for (var ctr = serialPorts.Count - 1; ctr >= 0; ctr--)
{
if (StopPortScan) break;
ActualPortCtr++;
serialPort.PortName = serialPorts[ctr];
var overallProcessCtrPercent = 100.0 * ActualPortCtr / (NumberOfPorts > 0 ? NumberOfPorts : 1);
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources.StrPortAccessRequest}"));
// skip all ports which are already opened, this might be another customer
if (serialPort.IsOpen) continue;
Interfaces.Ports.PortCore.PortConfig portConfig;
try
{
// removal from meter batch includes a disposal of meter
_meterBatch.RemoveAllMeters();
portConfig = new PortConfig
{
PortName = serialPort.PortName,
Type = _portType
};
_currentGenesis = new TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter(slot, portConfig, null);
if (_currentGenesis == null) return false;
// configuration has to be set BEFORE adding meter to batch to avoid e.g. auto update files
// from network and therefore have a long network request timeout before the task starts
_currentGenesis.Configuration.UseRegisterWatchService = false;
_currentGenesis.Configuration.UseMinMaxCheck = false;
_currentGenesis.Configuration.AutoUpdateFiles = false;
_meterBatch.AddMeter(_currentGenesis);
}
catch (Exception)
{
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources.StrPortFailedToOpen}"));
continue;
}
// if port cannot be opened on access to the port is senseless, try the next port
if (_currentGenesis != null && !_currentGenesis.RequestPort.IsOpen()) continue;
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources.StrPortSuccessfullyAccessed}"));
// read PCB ID from meter, this is the indicator, that a Genesis is connected to this port
if (_currentGenesis == null || string.Empty == _currentGenesis.GetPcbId())
{
OnProcessUpdate?.Invoke(this, new ProcessExecEventArgs(GetPortScanState(), overallProcessCtrPercent,
$@"{serialPort.PortName}: {Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources.StrRequestPortDetectionFailed}"));
continue;
}
AutoDetectedPortName = serialPort.PortName;
serialPortDetected = true;
// check if needed to store new configuration if slot and port name is not in file
if (!slotPortConfigs.Any(c => c.Slot == slot && c.Request.PortName == AutoDetectedPortName))
{
slotPortConfigs.Add(new SlotConfig() { Slot = slot, Request = portConfig });
var text = JsonConvert.SerializeObject(slotPortConfigs);
if (portConfigFilePathName != null)
File.WriteAllText(portConfigFilePathName, text);
}
// stop loop if one Genesis has been detected
break;
}
serialPort.Dispose();
// removal from meter batch includes a disposal of meter
_meterBatch?.Dispose();
return serialPortDetected;
}
}
}
@@ -0,0 +1,163 @@
using System;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Power correction parameters used for overestimated power consumption on
/// Cordonel FW update from:
/// EMEA below R1.3.x
/// NA below R2.0.07
/// </summary>
public class PowerCorrection
{
#region Data Base at EOL - Production
/// <summary>
/// Unique identification of PCB
/// </summary>
public String PcbId { get; set; }
/// <summary>
/// Date time UTC at EOL - Production
/// </summary>
public DateTime? ProductionDateTimeUtc { get; set; }
/// <summary>
/// Total used seconds at EOL - Production
/// </summary>
public UInt32 ProductionTotalUsedSeconds_s { get; set; }
/// <summary>
/// Total used charge of batteries at EOL - Production
/// </summary>
public UInt64 ProductionTotalUsedCharge_uAs { get; set; }
/// <summary>
/// Radio system state at EOL - Production
/// </summary>
public Byte? ProductionRadioSystemState { get; set; }
/// <summary>
/// Pulse adapter was installed during at EOL - Production VAKO
/// </summary>
public Boolean ProductionPulseAdapterIsInstalled { get; set; }
/// <summary>
/// Pulse mode during at EOL - Production
/// </summary>
public Byte ProductionPulseMode { get; set; }
/// <summary>
/// Pulse sequence counter during at EOL - Production
/// </summary>
public Byte ProductionPulseSequenceCounter { get; set; }
/// <summary>
/// Pulse distribution at EOL - Production
/// </summary>
public Boolean ProductionPulseEvenDistribution { get; set; }
#endregion
#region Maintenance readout
/// <summary>
/// Detected FW version before maintenance
/// </summary>
public UInt32? ActualFwVersion { get; set; }
/// <summary>
/// Date time UTC at time of maintenance
/// </summary>
public DateTime? ActualDateTimeUtc { get; set; }
/// <summary>
/// Total used seconds of entire runtime
/// </summary>
public UInt32 ActualTotalUsedSeconds_s { get; set; }
/// <summary>
/// Total used charge of batteries of entire runtime
/// </summary>
public UInt64 ActualTotalUsedCharge_uAs { get; set; }
/// <summary>
/// Actual detected radio system state at maintenance
/// </summary>
public Byte? ActualRadioSystemState { get; set; }
/// <summary>
/// Detection of pulse adapter installation
/// </summary>
public Boolean ActualPulseAdapterIsInstalled { get; set; }
/// <summary>
/// Pulse mode during maintenance
/// </summary>
public Byte ActualPulseMode { get; set; }
/// <summary>
/// Pulse sequence counter during maintenance
/// </summary>
public Byte ActualPulseSequenceCounter { get; set; }
/// <summary>
/// Pulse distribution during maintenance
/// </summary>
public Boolean ActualPulseEvenDistribution { get; set; }
/// <summary>
/// Mark pulse adapter as installed on any detected or sequence counted
/// </summary>
public Boolean PulseAdapterMarkedAsInstalled { get; set; }
/// <summary>
/// Remind battery quantity used for estimation of drained load
/// </summary>
public Int32 BatteryQuantity { get; set; }
/// <summary>
/// Remind battery initial load used for estimation of drained load
/// </summary>
public UInt32 InitialBatteryLoad_mAh { get; set; }
#endregion
#region Maintenance calculation
/// <summary>
/// FW version for the update
/// </summary>
public UInt32? RequiredFwVersion { get; set; }
/// <summary>
/// Calculated pulse report length
/// </summary>
public UInt32 PulseReportLength { get; set; }
/// <summary>
/// Total used seconds after EOL and before maintenance
/// </summary>
public UInt32 PostProductionTotalUsedSeconds_s { get; set; }
/// <summary>
/// Overestimated total used charge before maintenance
/// </summary>
public UInt64 OverestimatedTotalUsedCharge_uAs { get; set; }
/// <summary>
/// Based on fixed estimation total used charge before maintenance
/// </summary>
public UInt64 FixedEstimateTotalUsedCharge_uAs { get; set; }
/// <summary>
/// Lowest threshold for total used charge calculated during maintenance
/// </summary>
public UInt64 MinimumTotalUsedCharge_uAs { get; set; }
/// <summary>
/// Corrected total used charge during maintenance
/// </summary>
public UInt64? CorrectedTotalUsedCharge_uAs { get; set; }
#endregion
}
}
@@ -0,0 +1,219 @@
using System;
using System.Linq;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
/// <summary>
/// Read of required registers and calculation of power correction
/// </summary>
public class PowerCorrectionCalc
{
private readonly TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter _currentGenesis;
private readonly PowerCorrection _powCorr;
/// <summary>
/// Ctor
/// </summary>
/// <param name="currentGenesis"></param>
/// <param name="powCorr"></param>
/// <returns>true if successful</returns>
public PowerCorrectionCalc(TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter currentGenesis, PowerCorrection powCorr)
{
_currentGenesis = currentGenesis;
_powCorr = powCorr;
}
/// <summary>
/// Calculate the values for power correction
/// </summary>
/// <returns>true if successful</returns>
/// <remarks date="2024-May-15" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Boolean PowerCorrEstimation()
{
if (_currentGenesis == null || _powCorr == null)
return false;
var logOnReminder = _currentGenesis.IsLoggedOn;
var retVal = _currentGenesis.ReLogin();
if (retVal)
{
_powCorr.CorrectedTotalUsedCharge_uAs = null;
// read registers
_powCorr.ActualPulseSequenceCounter = RegisterConverter.ByteArrayToValue<Byte>(
_currentGenesis.ReadRegister(Register.Irda.PulseSequence));
_powCorr.ActualPulseEvenDistribution = RegisterConverter.ByteArrayToValue<Boolean>(
_currentGenesis.ReadRegister(Register.Metrologyasst.PulseEvenDistribution));
_powCorr.ActualPulseMode = RegisterConverter.ByteArrayToValue<Byte>(
_currentGenesis.ReadRegister(Register.Metrologyasst.PulseMode));
_powCorr.ActualRadioSystemState = RegisterConverter.ByteArrayToValue<Byte>(
_currentGenesis.ReadRegister(Register.Sensusradio.SystemState));
_powCorr.ActualTotalUsedSeconds_s = RegisterConverter.ByteArrayToValue<UInt32>(
_currentGenesis.ReadRegister(Register.Powermon.BatteryExceededSeconds));
_powCorr.ActualTotalUsedCharge_uAs = RegisterConverter.ByteArrayToValue<UInt64>(
_currentGenesis.ReadRegister(Register.Powermon.BatteryDrainedLoad, 8));
_powCorr.BatteryQuantity = RegisterConverter.ByteArrayToValue<Int32>(
_currentGenesis.ReadRegister(Register.Powermon.BatteryQuantity));
_powCorr.InitialBatteryLoad_mAh = RegisterConverter.ByteArrayToValue<UInt32>(
_currentGenesis.ReadRegister(Register.Powermon.BatteryInitialLoad));
if (!logOnReminder)
_currentGenesis.Logout();
// get actual time from PC
_powCorr.ActualDateTimeUtc = DateTime.UtcNow;
// skip if indicated that a pulse adapter never has been installed
_powCorr.PulseAdapterMarkedAsInstalled =
_powCorr.ProductionPulseAdapterIsInstalled ||
_powCorr.ActualPulseAdapterIsInstalled;
if (!_powCorr.PulseAdapterMarkedAsInstalled)
return true;
// calculate post production used seconds
if (_powCorr.ProductionTotalUsedSeconds_s > _powCorr.ActualTotalUsedSeconds_s)
_powCorr.PostProductionTotalUsedSeconds_s = _powCorr.ActualTotalUsedSeconds_s;
else
_powCorr.PostProductionTotalUsedSeconds_s = _powCorr.ActualTotalUsedSeconds_s -
_powCorr.ProductionTotalUsedSeconds_s;
// preset production used charge if not received from DB as logging of this value
// in early production processes was not granted
if (_powCorr.ProductionTotalUsedCharge_uAs == 0)
_powCorr.ProductionTotalUsedCharge_uAs = (UInt64)(_powCorr.ProductionTotalUsedSeconds_s *
PowCorrConst.FixProductionCurrent_uA);
// calculate pulse report length
if ((_powCorr.ActualPulseMode >= 1 && _powCorr.ActualPulseMode <= 4) ||
(_powCorr.ProductionPulseMode >= 1 && _powCorr.ProductionPulseMode <= 4))
{
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode1To4Even0;
if (_powCorr.ActualPulseEvenDistribution)
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode1To4Even1;
}
else if ((_powCorr.ActualPulseMode >= 5 && _powCorr.ActualPulseMode <= 6) ||
(_powCorr.ProductionPulseMode >= 5 && _powCorr.ProductionPulseMode <= 6))
{
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode5To6Even0;
if (_powCorr.ActualPulseEvenDistribution)
_powCorr.PulseReportLength = PowCorrConst.PulseReportLengthMode5To6Even1;
}
// calculate overestimation based on pulse report length
_powCorr.OverestimatedTotalUsedCharge_uAs = 0;
if ((_powCorr.ActualPulseMode >= 1 && _powCorr.ActualPulseMode <= 6) ||
(_powCorr.ProductionPulseMode >= 1 && _powCorr.ProductionPulseMode <= 6))
{
_powCorr.OverestimatedTotalUsedCharge_uAs =
(UInt64)(_powCorr.PulseReportLength * PowCorrConst.PulseReportLengthEstimate_uA *
_powCorr.PostProductionTotalUsedSeconds_s);
}
Byte radioSystemState = 0;
if (_powCorr.ActualRadioSystemState != null)
{
radioSystemState = (Byte)_powCorr.ActualRadioSystemState;
}
else if (_powCorr.ProductionRadioSystemState != null)
{
radioSystemState = (Byte)_powCorr.ProductionRadioSystemState;
}
if (_currentGenesis.Region == "NA")
{
_powCorr.MinimumTotalUsedCharge_uAs =
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.MinNaCurrent_uA);
_powCorr.FixedEstimateTotalUsedCharge_uAs =
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.FixNaCurrent_uA);
}
else if (_currentGenesis.Region == "EMEA" &&
(radioSystemState & PowCorrConst.SensusRadioSystemStateTfXModeMask) ==
PowCorrConst.SensusRadioSystemStateTfXModeMask)
{
_powCorr.MinimumTotalUsedCharge_uAs =
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.MinEmeaRadioTfxModeCurrent_uA);
_powCorr.FixedEstimateTotalUsedCharge_uAs =
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.FixEmeaRadioTfxModeCurrent_uA);
}
else if (_currentGenesis.Region == "EMEA" &&
(radioSystemState & PowCorrConst.SensusRadioSystemStateTfXModeMask) !=
PowCorrConst.SensusRadioSystemStateTfXModeMask)
{
_powCorr.MinimumTotalUsedCharge_uAs =
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.MinEmeaRadioRfModeCurrent_uA);
_powCorr.FixedEstimateTotalUsedCharge_uAs =
(UInt64)(_powCorr.ActualTotalUsedSeconds_s * PowCorrConst.FixEmeaRadioRfModeCurrent_uA);
}
if (_powCorr.OverestimatedTotalUsedCharge_uAs > _powCorr.ActualTotalUsedCharge_uAs)
_powCorr.CorrectedTotalUsedCharge_uAs = 0;
else
_powCorr.CorrectedTotalUsedCharge_uAs = _powCorr.ActualTotalUsedCharge_uAs -
_powCorr.OverestimatedTotalUsedCharge_uAs;
}
return retVal;
}
/// <summary>
/// Calculate the values for power correction
/// </summary>
/// <returns>true if successful</returns>
/// <remarks date="2024-May-14" author="Thomas Wiedebusch">
/// - Initial
/// </remarks>
public Boolean CorrectPowerConsumption()
{
if (_currentGenesis == null || _powCorr == null || _powCorr.CorrectedTotalUsedCharge_uAs == null)
return false;
// check which value should be applied and set the CorrectedTotalUsedCharge_uAs
if (_powCorr.ProductionPulseAdapterIsInstalled)
{
// avoid undershot below smallest value
if (_powCorr.CorrectedTotalUsedCharge_uAs < _powCorr.MinimumTotalUsedCharge_uAs)
_powCorr.CorrectedTotalUsedCharge_uAs = _powCorr.MinimumTotalUsedCharge_uAs;
}
else // retrofitted
{
// correct to smaller value and mark corrected = actual to log that correction not needed
_powCorr.CorrectedTotalUsedCharge_uAs =
_powCorr.ActualTotalUsedCharge_uAs > _powCorr.FixedEstimateTotalUsedCharge_uAs ?
_powCorr.FixedEstimateTotalUsedCharge_uAs : _powCorr.ActualTotalUsedCharge_uAs;
}
if (_powCorr.ActualTotalUsedCharge_uAs == _powCorr.CorrectedTotalUsedCharge_uAs)
return true;
// save corrected power consumption data to meter
var logOnReminder = _currentGenesis.IsLoggedOn;
var retVal = _currentGenesis.ReLogin();
if (retVal)
{
var retriesLeft = 2;
do
{
// write the new calculated value
retVal = _currentGenesis.WriteRegister(Register.Powermon.BatteryDrainedLoad,
RegisterConverter.ValueToByteArray(_powCorr.CorrectedTotalUsedCharge_uAs));
if (!retVal)
continue;
// store configuration
retVal = _currentGenesis.WriteRegister(Register.Powermon.StoreConfiguration, 1);
if (!retVal)
continue;
var readResult = _currentGenesis.ReadRegister(Register.Powermon.StoreConfiguration);
retVal = readResult != null && readResult.ToList().All(array => array == 0x00);
} while (!retVal && retriesLeft-- > 0);
}
if (!logOnReminder)
_currentGenesis.Logout();
return retVal;
}
}
}
@@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
using Xylem.Common.CommonCore.Configuration;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
public class PreadjustmentMeter : TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.GenesisMeter
{
private settings mySettings = new settings();
public class settings
{
public int Metersize { get; set; } = 2;
}
public void Preparation()
{
//Login
//getRegisterList
Login();
WriteRegister(Register.Genesisflow.CalFactor1, 15625);
WriteRegister(Register.Genesisflow.CalFactor2, 15625);
WriteRegister(Register.Genesisflow.CalFactor3, 15625);
WriteRegister(Register.Genesisflow.ZeroOffset1, 0);
WriteRegister(Register.Genesisflow.ZeroOffset2, 0);
WriteRegister(Register.Genesisflow.ZeroOffset3, 0);
WriteRegister(Register.Genesisflow.LedMode, 6);
WriteRegister(Register.Genesisflow.MeterSize, mySettings.Metersize);
//var r = LocalWebRequest.GetRequest($"{ ServiceUrls.GenesisFinalCheckServiceUrl()}GetMetersizeProgrammingData?MeterSize={mySettings.Metersize.GetHashCode().GetHashCode()}", 2000);
//var dt = JsonConvert.DeserializeObject<Dictionary<string, UInt32>>(r);
// foreach (var item in dt)
// {
// WriteRegister(item.Key, item.Value);
// }
}
public void RunAmp()
{
for (int i = 0; i < 35; i++)
{
}
}
}
}
@@ -0,0 +1,288 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Missing or invalid register definition (configuration.json)! Search in:.
/// </summary>
internal static string StrErrorMissingConfiguration {
get {
return ResourceManager.GetString("StrErrorMissingConfiguration", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot Open Comport. Check TaskManager for other Genesis relevant programs and close them! Also check if the comport is valid!.
/// </summary>
internal static string StrErrorMsgComPort {
get {
return ResourceManager.GetString("StrErrorMsgComPort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Could not find a CSD parameter in register list!.
/// </summary>
internal static string StrErrorMsgCsdMissing {
get {
return ResourceManager.GetString("StrErrorMsgCsdMissing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Could not find register in register list! Check configuration.json for latest version!.
/// </summary>
internal static string StrErrorMsgRegisterNotFound {
get {
return ResourceManager.GetString("StrErrorMsgRegisterNotFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Could not write register!.
/// </summary>
internal static string StrErrorMsgRegisterWrite {
get {
return ResourceManager.GetString("StrErrorMsgRegisterWrite", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Read back.
/// </summary>
internal static string StrMeterValue {
get {
return ResourceManager.GetString("StrMeterValue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Access request..
/// </summary>
internal static string StrPortAccessRequest {
get {
return ResourceManager.GetString("StrPortAccessRequest", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Failed to open..
/// </summary>
internal static string StrPortFailedToOpen {
get {
return ResourceManager.GetString("StrPortFailedToOpen", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Ports listed in the Windows device manager..
/// </summary>
internal static string StrPortListDeviceManger {
get {
return ResourceManager.GetString("StrPortListDeviceManger", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Automatic port scan started..
/// </summary>
internal static string StrPortScanStarted {
get {
return ResourceManager.GetString("StrPortScanStarted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Successfully accessed..
/// </summary>
internal static string StrPortSuccessfullyAccessed {
get {
return ResourceManager.GetString("StrPortSuccessfullyAccessed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Read register.
/// </summary>
internal static string StrReadRegister {
get {
return ResourceManager.GetString("StrReadRegister", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reading registers after maintenance.
/// </summary>
internal static string StrReadRegistersAfterUpdate {
get {
return ResourceManager.GetString("StrReadRegistersAfterUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reading registers before maintenance.
/// </summary>
internal static string StrReadRegistersBeforeUpdate {
get {
return ResourceManager.GetString("StrReadRegistersBeforeUpdate", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Compare register.
/// </summary>
internal static string StrRegisterCompare {
get {
return ResourceManager.GetString("StrRegisterCompare", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Comparison failed!.
/// </summary>
internal static string StrRegisterCompareFailed {
get {
return ResourceManager.GetString("StrRegisterCompareFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Successfully compared..
/// </summary>
internal static string StrRegisterCompareSucceeded {
get {
return ResourceManager.GetString("StrRegisterCompareSucceeded", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Execute configuration sequence.....
/// </summary>
internal static string StrRegisterRecoveryExec {
get {
return ResourceManager.GetString("StrRegisterRecoveryExec", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Finalize configuration sequence.....
/// </summary>
internal static string StrRegisterRecoveryFinalization {
get {
return ResourceManager.GetString("StrRegisterRecoveryFinalization", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Prepare configuration sequence.....
/// </summary>
internal static string StrRegisterRecoveryPreparation {
get {
return ResourceManager.GetString("StrRegisterRecoveryPreparation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ERROR: Register value is out of range!.
/// </summary>
internal static string StrRegisterValueOutOfRange {
get {
return ResourceManager.GetString("StrRegisterValueOutOfRange", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cordonel not detected!.
/// </summary>
internal static string StrRequestPortDetectionFailed {
get {
return ResourceManager.GetString("StrRequestPortDetectionFailed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Scan port.
/// </summary>
internal static string StrScanPort {
get {
return ResourceManager.GetString("StrScanPort", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to WARNING: Register value set to default.
/// </summary>
internal static string StrSetRegisterToDefault {
get {
return ResourceManager.GetString("StrSetRegisterToDefault", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Write register.
/// </summary>
internal static string StrWriteRegister {
get {
return ResourceManager.GetString("StrWriteRegister", resourceCulture);
}
}
}
}
@@ -0,0 +1,195 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="StrPortAccessRequest" xml:space="preserve">
<value>Teste Zugriff.</value>
</data>
<data name="StrPortFailedToOpen" xml:space="preserve">
<value>Zugriff verweigert.</value>
</data>
<data name="StrPortListDeviceManger" xml:space="preserve">
<value>Kommunikationsanschlüsse im Windows Gerätemanager.</value>
</data>
<data name="StrPortScanStarted" xml:space="preserve">
<value>Automatische Erkennung gestartet.</value>
</data>
<data name="StrPortSuccessfullyAccessed" xml:space="preserve">
<value>Zugriff erfolgreich.</value>
</data>
<data name="StrRequestPortDetectionFailed" xml:space="preserve">
<value>Keinen Cordonel erkannt!</value>
</data>
<data name="StrScanPort" xml:space="preserve">
<value>Suche Anschluß....</value>
</data>
<data name="StrRegisterCompare" xml:space="preserve">
<value>Vergleiche Register</value>
</data>
<data name="StrReadRegister" xml:space="preserve">
<value>Lese Register</value>
</data>
<data name="StrWriteRegister" xml:space="preserve">
<value>Schreibe Register</value>
</data>
<data name="StrReadRegistersBeforeUpdate" xml:space="preserve">
<value>Lese Register vor der Wartung</value>
</data>
<data name="StrReadRegistersAfterUpdate" xml:space="preserve">
<value>Lese Register nach der Wartung</value>
</data>
<data name="StrRegisterRecoveryExec" xml:space="preserve">
<value>Ausführung der Konfigurationsequenz....</value>
</data>
<data name="StrRegisterRecoveryFinalization" xml:space="preserve">
<value>Abschließen der Konfigurationsequenz....</value>
</data>
<data name="StrRegisterRecoveryPreparation" xml:space="preserve">
<value>Vorbereitung der Konfigurationsequenz....</value>
</data>
<data name="StrRegisterCompareFailed" xml:space="preserve">
<value>FEHLER: Vergleich fehlgeschlagen!</value>
</data>
<data name="StrRegisterCompareSucceeded" xml:space="preserve">
<value>Vergleich erfolgreich.</value>
</data>
<data name="StrMeterValue" xml:space="preserve">
<value>Zurückgelesen</value>
</data>
<data name="StrSetRegisterToDefault" xml:space="preserve">
<value>WARNUNG: Registerwert auf Standardwert gesetzt</value>
</data>
<data name="StrRegisterValueOutOfRange" xml:space="preserve">
<value>FEHLER: Registerwert außerhalb des zulässigen Bereiches!</value>
</data>
<data name="StrErrorMsgCsdMissing" xml:space="preserve">
<value>FEHLER: Kein CSD Parameter in Parameterliste gefunden!</value>
</data>
<data name="StrErrorMsgRegisterNotFound" xml:space="preserve">
<value>FEHLER: Register nicht in Registerliste gefunden! Version der Configuration.json prüfen!</value>
</data>
<data name="StrErrorMsgRegisterWrite" xml:space="preserve">
<value>FEHLER: Register konnte nicht geschrieben werden!</value>
</data>
<data name="StrErrorMissingConfiguration" xml:space="preserve">
<value>FEHLER: Konfigurationsdatei (configuration.json) nicht gefunden oder ungültig! Erwartetes Verzeichnis:</value>
</data>
<data name="StrErrorMsgComPort" xml:space="preserve">
<value>Kommunikationsanschluß nicht vorhanden oder von anderer Genesis App blockiert! Bitte TaskManger starten und prüfen!</value>
</data>
</root>
@@ -0,0 +1,195 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="StrPortAccessRequest" xml:space="preserve">
<value>Access request.</value>
</data>
<data name="StrPortFailedToOpen" xml:space="preserve">
<value>Failed to open.</value>
</data>
<data name="StrPortListDeviceManger" xml:space="preserve">
<value>Ports listed in the Windows device manager.</value>
</data>
<data name="StrPortScanStarted" xml:space="preserve">
<value>Automatic port scan started.</value>
</data>
<data name="StrPortSuccessfullyAccessed" xml:space="preserve">
<value>Successfully accessed.</value>
</data>
<data name="StrRequestPortDetectionFailed" xml:space="preserve">
<value>Cordonel not detected!</value>
</data>
<data name="StrScanPort" xml:space="preserve">
<value>Scan port</value>
</data>
<data name="StrReadRegistersAfterUpdate" xml:space="preserve">
<value>Reading registers after maintenance</value>
</data>
<data name="StrReadRegistersBeforeUpdate" xml:space="preserve">
<value>Reading registers before maintenance</value>
</data>
<data name="StrRegisterCompare" xml:space="preserve">
<value>Compare register</value>
</data>
<data name="StrReadRegister" xml:space="preserve">
<value>Read register</value>
</data>
<data name="StrWriteRegister" xml:space="preserve">
<value>Write register</value>
</data>
<data name="StrRegisterRecoveryExec" xml:space="preserve">
<value>Execute configuration sequence....</value>
</data>
<data name="StrRegisterRecoveryFinalization" xml:space="preserve">
<value>Finalize configuration sequence....</value>
</data>
<data name="StrRegisterRecoveryPreparation" xml:space="preserve">
<value>Prepare configuration sequence....</value>
</data>
<data name="StrRegisterCompareFailed" xml:space="preserve">
<value>ERROR: Comparison failed!</value>
</data>
<data name="StrRegisterCompareSucceeded" xml:space="preserve">
<value>Successfully compared.</value>
</data>
<data name="StrMeterValue" xml:space="preserve">
<value>Read back</value>
</data>
<data name="StrSetRegisterToDefault" xml:space="preserve">
<value>WARNING: Register value set to default</value>
</data>
<data name="StrRegisterValueOutOfRange" xml:space="preserve">
<value>ERROR: Register value is out of range!</value>
</data>
<data name="StrErrorMsgCsdMissing" xml:space="preserve">
<value>ERROR: Could not find a CSD parameter in register list!</value>
</data>
<data name="StrErrorMsgRegisterNotFound" xml:space="preserve">
<value>ERROR: Could not find register in register list! Check configuration.json for latest version!</value>
</data>
<data name="StrErrorMsgRegisterWrite" xml:space="preserve">
<value>ERROR: Could not write register!</value>
</data>
<data name="StrErrorMissingConfiguration" xml:space="preserve">
<value>ERROR: Missing or invalid register definition (configuration.json)! Search in:</value>
</data>
<data name="StrErrorMsgComPort" xml:space="preserve">
<value>Cannot Open Comport. Check TaskManager for other Genesis relevant programs and close them! Also check if the comport is valid!</value>
</data>
</root>
@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Xylem.Common.Hardware.WaterMeter.Genesis.GenesisCore.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}
@@ -0,0 +1,6 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
</SettingsFile>
@@ -0,0 +1,237 @@
using System;
using System.Linq;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const;
using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
public partial class GenesisMeter
{
/// <summary>
/// Calibration factors for all channels
/// </summary>
private readonly CalibFactor[] _calibFactor = new CalibFactor[MeterConfig.CalibChannels];
/// <summary>
/// Registers needed to store the calibration for the individual channel
/// </summary>
private readonly String[] _registersToStoreCalibFactor =
{
Register.Genesisflow.CalFactor1,
Register.Genesisflow.CalFactor2,
Register.Genesisflow.CalFactor3
};
/// <inheritdoc />
public void InitCalibration()
{
Logger.Info($"Slot:{Slot} - Init calibration and set calibration values of all channels to default");
for (var channel = 0; channel < MeterConfig.CalibChannels; channel++)
{
_calibFactor[channel] = new CalibFactor(channel, _registersToStoreCalibFactor[channel]);
WriteRegister(_registersToStoreCalibFactor[channel],
_calibFactor[channel].GetMeterRawCalibFactorDefault(), checkRegister: true);
}
PreMeasurement();
}
/// <inheritdoc />
public void StartCalibration()
{
StartMeasurement();
}
/// <inheritdoc />
/// <summary>
/// Just stop recording without calculation
/// </summary>
public void StopCalibration()
{
StopMeasurement();
}
/// <inheritdoc />
public MeasurementStates GetCalibrationState()
{
return GetMeasurementState();
}
/// <inheritdoc />
public void BuildAndCheckCalibFactorsAllChannels(Double refVolumeCm, Double? refTimeS, Double reqDeviationPercent, Double maxCalibFactorTolerancePercent = MeterConfig.DefaultMaxCalibFactorTolerancePercent, int? testrunNr = null)
{
for (var channel = 0; channel < MeterConfig.CalibChannels; channel++)
{
_calibFactor[channel] = new CalibFactor(channel, _registersToStoreCalibFactor[channel]);
}
foreach (var item in Enumerable.Where<IMeasurement>(_listOfIMeasurement, m => m.GetType() == typeof(CalibrationRecord)))
{
if (!(item is CalibrationMeasurement))
{
continue;
}
var calib = item as CalibrationMeasurement;
var channel = calib.GetChannel();
if (channel == 0 || channel > MeterConfig.CalibChannels)
{
var message = $"Slot:{Slot}, Channel:{channel} - Calibration channel out of range";
Logger.Warn(message);
throw new ApplicationException(message);
}
Logger.Info($"Slot:{Slot}, Channel:{channel} - Calculate Calibrationfactor Relative(RefVolumeCm:{refVolumeCm}, refTimeS:{refTimeS},reqDeviationPercent:{reqDeviationPercent})");
var calibFactorRel = calib.CalculateCalibFactorRel(refVolumeCm, refTimeS, reqDeviationPercent);
//if the null return indicates an invalid calibration factor the calibFactorX.IsCalculated remains false
if (null == calibFactorRel)
{
var message = $"Slot:{Slot}, Channel:{channel} - Calibration factor not calculated";
Logger.Warn(message);
throw new ApplicationException(message);
}
_calibFactor[channel - 1].BuildMeterCalibFactor(calibFactorRel.Value);
_calibFactor[channel - 1].IsCalculated = true;
Logger.Info($"Slot:{Slot}, Channel:{channel} - New calibration factor({_calibFactor[channel - 1].GetRelativeCalibFactor()}rel/{calibFactorRel})");
Logger.Info($"Slot:{Slot}, Channel:{channel} - Calc /{_calibFactor[channel - 1].GetMeterRawCalibFactor()} * {calibFactorRel.Value}) -> UInt16");
//Setup the required tolerance value to check against
//plus/minus x% tolerance allowed
calib.CalibFactorTolerancePercent = maxCalibFactorTolerancePercent;
if (calib.IsCalibFactorInTolerance(calibFactorRel.Value))
{
continue;
}
var warnMessage = $"Slot:{Slot}, Channel:{channel} - " +
$"Calibration factor is out of range({calibFactorRel.Value}rel)";
Logger.Warn(warnMessage);
// throw new ApplicationException(warnMessage);
}
var t = new JustageResults
{
pcbId = PcbId,
p1Target = reqDeviationPercent,
p1Value = _calibFactor[0].GetMeterRawCalibFactor(),
p2Target = reqDeviationPercent,
p2Value = _calibFactor[1].GetMeterRawCalibFactor(),
p3Target = reqDeviationPercent,
p3Value = _calibFactor[2].GetMeterRawCalibFactor(),
dt = DateTime.UtcNow,
testrunNr = testrunNr
};
try
{
var tmpUrl = "http://10.49.40.25/MeterProcessState/api/TestBench/SetCalibrationValues";
Logger.Info($"Request Url: {tmpUrl}");
//ToDO: Update to service URl
LocalWebRequest.PostAsJson<JustageResults>(t, tmpUrl);
}
catch (Exception ex)
{
Logger.Error($"Request Url: {ex.Message}");
}
}
/// <inheritdoc />
public void SetCalibFactorsAllChannels(Boolean justLog = false)
{
WriteRegister<Byte>(Register.Genesisflow.SampleRate, 2, checkRegister: true);
WriteRegister<UInt16>(Register.Genesisflow.TriggerIdle, 0x1234);
for (var channel = 0; channel < MeterConfig.CalibChannels; channel++)
{
if (_calibFactor[channel].IsCalculated == false)
{
continue;
}
if (justLog)
{
Logger.Warn($"Slot:{Slot} - Calculated calibration factors but not stored to registers");
}
else
{
var registerRawCalibFactor = _calibFactor[channel].GetMeterRawCalibFactor();
var resultWrite = WriteRegister(_calibFactor[channel].RegisterToStoreCalibFactor,
registerRawCalibFactor, checkRegister: true);
Logger.Info($"Slot:{Slot}, Channel:{channel} - Stored calibration factor({_registersToStoreCalibFactor[channel]})");
//if (!string.IsNullOrEmpty(SerialNumber) && CurrentActionText.Contains("PG"))
//{
// var pathMain = NLogHelper.GetPath(_logger);
// var pathRaw = NLogHelper.GetPath(((LedSerialPort)StreamingPort).GetRawLogger());
// var basePath = Path.GetDirectoryName(pathMain);
// //
// var testRunString = CurrentActionText.Replace("PG", "");
//
if (resultWrite)
{
Logger.Warn($"Set calibration went wrong {_calibFactor[channel].RegisterToStoreCalibFactor}, with {registerRawCalibFactor}");
}
}
}
WriteRegister(Register.Genesisflow.ResetAccumulators, true);
//WriteRegister<UInt16>(Register.Genesisflow.ForwardArrow, 0, checkRegister: true);
WriteRegister<UInt16>(Register.Genesisflow.StoreCalibration, 1);
Logger.Info($"Slot:{Slot} - StoreCalibration {RegisterConverter.ByteArrayToValue<UInt16>(ReadRegister(Register.Genesisflow.StoreCalibration))}");
WriteRegister<UInt16>(Register.Genesisflow.TriggerActive, 1);
Logger.Info($"Slot:{Slot} - Set forward arrow of LCD, reset accumulators, set sample rate to 2Hz");
WriteRegister<Byte>(Register.Genesisflow.SampleRate, 10, checkRegister: true);
try
{
if (Enumerable.Any(GetRegistersDic(), a => a.Key.RegisterName == "GENESISFLOW_DelayedLedOff"))
{
WriteRegister("GENESISFLOW_DelayedLedOff", 60 * 60 * 2, true);
Logger.Info($"Slot:{Slot} - GENESISFLOW_DelayedLedOff to {60 * 60 * 2}S");
}
else
{
Logger.Info($"Slot:{Slot} - GENESISFLOW_DelayedLedOff is not present");
}
}
catch (Exception ex)
{
Logger.Info($"Slot:{Slot} - {ex}");
}
}
}
}
@@ -0,0 +1,320 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xylem.Common.CommonCore.Consts;
using Xylem.Common.Hardware.WaterMeter.Genesis.GenesisConfig.Const;
using Xylem.Common.Metrology.Measurements;
using Xylem.Common.Metrology.Measurements.Consts;
using LedMode = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore.Consts.LedMode;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisCore
{
public partial class GenesisMeter
{
/// <summary>
/// List of ongoing measurements, base- and calibration-measurements
/// </summary>
private List<IMeasurement> _listOfIMeasurement = new List<IMeasurement>();
/// <summary>
/// All channels required to process
/// </summary>
public Boolean AllChannelsRequired = true;
/// <summary>
/// Quality watch mode can be used to decode intermediate records and check
/// for actual quality of the measurements.
/// </summary>
public Boolean QualityWatchMode;
/// <inheritdoc />
/// <summary>
/// Perpetration of Measurement
/// SampleRate 10 and Led mode Test
/// </summary>
public void InitMeasurement()
{
Logger.Info($"Slot:{Slot} - Init measurement");
PreMeasurement();
}
private void PreMeasurement()
{
if (!SkipPreparationForTestBench)
{
//SkipPreparationForTestBench = true;
//TODO set display unit
//TODO calculated overflow volume for display
WriteRegister<Byte>(Register.Genesisflow.SampleRate, 0x08, checkRegister: true);
WriteRegister(Register.Genesisflow.LedMode, (Byte)LedMode.FlowTestAndCalibData, checkRegister: true);
WriteRegister("GENESISFLOW_SealDisplay", 0, true, true);
try
{
var displayState = ReadRegister(Register.Genesisflow.TriggerIdle);
if (displayState == null)
{
displayState = ReadRegister(Register.Genesisflow.TriggerIdle);
if (displayState == null)
{
Logger.Info($"Slot:{Slot} - Not able to read display state");
return;
}
}
var displayStateString = ByteStyler.ToString(displayState);
if (displayStateString.Substring(0, 2) != "FF" || displayStateString.Substring(0, 5) == "FF-55")
{
WriteRegister(Register.Genesisflow.TriggerIdle, 0, true, true);
displayState = ReadRegister(Register.Genesisflow.TriggerIdle);
displayStateString = ByteStyler.ToString(displayState);
if (displayStateString != "00-00-00-00")
{
WriteRegister(Register.Genesisflow.TriggerActive, 1);
}
}
else
{
WriteLog($"meter not active because display code has an error on code:{displayStateString}");
}
}
catch (Exception)
{
// ignored
}
WriteRegister("GENESISFLOW_DisplayPow10", (new Byte[] { 0x00, 0x00, 0x00, 0xFA }).Reverse().ToArray());
//WriteRegister(Register.Genesisflow.StoreCalibration, 1);
//WriteRegister(Register.Genesisflow.StoreConfiguration, 1);
try
{
if (Enumerable.Any(GetRegistersDic(), a => a.Key.RegisterName == "GENESISFLOW_DelayedLedOff"))
{
WriteRegister("GENESISFLOW_DelayedLedOff", 60 * 60 * 2, false);
}
}
catch (Exception)
{
}
}
else
{
WriteLog($"wait for measurement is starting");
}
ProcessStatus = ProcessState.WaitForMeasurement;
}
/// <inheritdoc />
public void StartMeasurement()
{
_listOfIMeasurement = new List<IMeasurement>
{
new BaseMeasurement(Slot, typeof(FlowTestRecord), IntermediateUpdateTimeS)
};
//add channels for calibration measurement
for (var i = 1; i <= MeterConfig.CalibChannels; i++)
{
_listOfIMeasurement.Add(new CalibrationMeasurement(Slot, typeof(CalibrationRecord), IntermediateUpdateTimeS, i));
}
foreach (var measurement in _listOfIMeasurement)
{
measurement.OnActionStateHasChanged += Measurement_ActionStateHasChanged;
}
Logger.Info($"Slot:{Slot} - Start measurement");
ProcessStatus = ProcessState.MeasurementIsActive;
_listOfIMeasurement.ForEach(m => m.MeasurementState = MeasurementStates.IsWaitingForStartData);
SyncStreamingBuffer(SyncMarkRecord.SyncStart);
}
/// <inheritdoc />
public void StopMeasurement()
{
Logger.Info($"Slot:{Slot} - Stop measurement");
ProcessStatus = ProcessState.MeasurementIsDone;
_listOfIMeasurement.ForEach(m => m.MeasurementState = MeasurementStates.IsWaitingForEndData);
SyncStreamingBuffer(SyncMarkRecord.SyncEnd);
}
/// <summary>
/// Testing the flow direction
/// </summary>
/// <returns></returns>
public Boolean CheckChannelFlowDirection()
{
Boolean? dirPositive = null;
foreach (var cali in _listOfIMeasurement.Where(m => m.GetType() == typeof(CalibrationRecord)))
{
var result = cali.GetIntermediateMeasurementResult();
var channelDirPositive = result.DutVolumeCm > 0.0;
if (!dirPositive.HasValue)
{
dirPositive = channelDirPositive;
}
if (dirPositive != channelDirPositive)
{
return false;
}
}
return true;
}
/// <inheritdoc />
/// <summary>
/// the first measurement is ALWAYS a FlowTestRecord due to the underlying<see cref="GetAllMeasurementResults"/> routine logic.
/// </summary>
public MeasurementResults GetMainMeasurementResult(Double? refVolume, Double? refTimeS, Boolean intermediateMeasurement = false, MeasurementDirection dir = MeasurementDirection.TakeBoth)
{
if (!intermediateMeasurement && GetMeasurementState(0) != MeasurementStates.IsCompleted)
{
throw new ApplicationException("Measurement is not completed");
}
return GetAllMeasurementResults(refVolume, refTimeS, intermediateMeasurement, 0).First();
}
/// <summary>
/// The first measurement is ALWAYS a FlowTestRecord.
/// </summary>
public List<MeasurementResults> GetAllMeasurementResults(Double? refVolume, Double? refTimeS, Boolean intermediateMeasurement = false, Int32? channel = null, MeasurementDirection dir = MeasurementDirection.TakeBoth)
{
//
if (!intermediateMeasurement && GetMeasurementState(channel) != MeasurementStates.IsCompleted)
{
throw new ApplicationException("Measurement is not completed");
}
var mainMeasurement = _listOfIMeasurement.FirstOrDefault(m => m.GetType() == typeof(FlowTestRecord));
if (mainMeasurement == null)
{
throw new ApplicationException("Can not find an ongoing measurement");
}
mainMeasurement.FlowDirectionforCalculatingVol = dir;
var results = new List<MeasurementResults>();
var mainResult = intermediateMeasurement
? mainMeasurement.GetIntermediateMeasurementResult(refVolume, refTimeS)
: mainMeasurement.GetFinalMeasurementResult(refVolume, refTimeS);
results.Add(mainResult);
Logger.Info($"Slot:{Slot}, Channel:{mainMeasurement.GetChannel()} - MeasuredTime({mainResult.DutTimeS}s), " +
$"MeasuredVolume({mainResult.DutVolumeCm}Qm)");
foreach (var otherMeasurement in _listOfIMeasurement.Where(m => m.GetType() != typeof(FlowTestRecord)))
{
try
{
var otherResults = intermediateMeasurement
? otherMeasurement.GetIntermediateMeasurementResult(refVolume, refTimeS)
: otherMeasurement.GetFinalMeasurementResult(refVolume, refTimeS);
Logger.Info($"Slot:{Slot}, Channel:{otherMeasurement.GetChannel()} - ChannelTime({otherResults.DutTimeS}s), " +
$"ChannelVolume({otherResults.DutVolumeCm}Qm)");
results.Add(otherResults);
}
catch (Exception ex)
{
Logger.Warn($"Slot:{Slot}, No Results for channel:{otherMeasurement.GetChannel()} Ex: {ex.Message}");
if (channel == null)
{
throw new ApplicationException($"Can not read result from channel:{otherMeasurement.GetChannel() } Ex: {ex.Message}");
}
}
}
return results;
}
private void AddData(IMeasurementRecord newRecord)
{
foreach (var measurement in _listOfIMeasurement)
{
if (measurement.GetType() != newRecord.GetType())
{
continue;
}
if (measurement.GetChannel() == newRecord.GetChannel())
{
measurement.AddData(newRecord);
}
}
// when every measurement has state IsRunning or every measurement has state IsCompleted, the streaming buffer set to skip decoding
if (_listOfIMeasurement.All(m => m.MeasurementState == MeasurementStates.IsRunning)
|| _listOfIMeasurement.All(m => m.MeasurementState == MeasurementStates.IsCompleted))
{
SyncStreamingBuffer(QualityWatchMode
? SyncMarkRecord.DecodeIntermediateQuality
: SyncMarkRecord.SkipDecoding);
}
}
private void Measurement_ActionStateHasChanged(Object sender, EventArgs e)
{
SyncStreamingBuffer(SyncMarkRecord.DecodeIntermediate);
}
/// <inheritdoc />
public MeasurementStates GetMeasurementState(Int32? onlyChannelNr = null)
{
Logger.Trace($"Slot:{Slot} - Get measurement state");
if (!_listOfIMeasurement.Any())
{
return MeasurementStates.NotStarted;
}
if (onlyChannelNr.HasValue)
{
var measurement = _listOfIMeasurement.First(f => f.GetChannel() == onlyChannelNr.Value);
return measurement.GetMeasurementState();
}
return _listOfIMeasurement.Min(m => m.MeasurementState);
}
/// <inheritdoc />
public Boolean RequestIntermediateMeasurementState(Int32? onlyChannelNr = null)
{
Logger.Trace($"Slot:{Slot} - Get measurement state");
if (!_listOfIMeasurement.Any())
{
return false;
}
Boolean? ret = null;
foreach (var item in _listOfIMeasurement)
{
if (!onlyChannelNr.HasValue || item.GetChannel() == onlyChannelNr.Value)
{
var measurement = _listOfIMeasurement.First(f => onlyChannelNr != null && f.GetChannel() == onlyChannelNr.Value);
if ((measurement.MeasurementState == MeasurementStates.IsRunning
|| measurement.MeasurementState == MeasurementStates.IsWaitingForIntermediateData))
{
measurement.RequestIntermediateRecord();
if (!ret.HasValue)
{
ret = true;
}
}
else
{
ret = false;
}
}
}
return ret.HasValue ? ret.Value : false;
}
}
}
@@ -0,0 +1,313 @@
{
"_comment": "Auto generated from 'Configuration.xls' - DO NOT MANUALLY EDIT THIS TABLE",
"CONFIGEXCHANGE_Privilege": {"type": "uint8_t", "id": 1024, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_Password": {"type": "uint96_t", "id": 1025, "privilege": {"lvl1": "WO", "lvl2": "WO", "lvl3": "WO", "lvl4": "WO", "lvl5": "WO", "lvl6": "WO", "lvl7": "WO", "lvl8": "WO"}},
"CONFIGEXCHANGE_FOpen": {"type": "RPC", "id": 1026, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FClose": {"type": "RPC", "id": 1027, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FRead": {"type": "RPC", "id": 1028, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FWrite": {"type": "RPC", "id": 1029, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FSeek": {"type": "RPC", "id": 1030, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FTell": {"type": "RPC", "id": 1031, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_Remove": {"type": "RPC", "id": 1032, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FEOF": {"type": "RPC", "id": 1033, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_Catalogue": {"type": "RPC", "id": 1034, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_PCBSerialNumber": {"type": "string", "id": 1036, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CONFIGEXCHANGE_ConfigAccessRights": {"type": "uint16_t", "id": 1037, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"CONFIGEXCHANGE_FFlush": {"type": "RPC", "id": 1038, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_AlarmStatus0": {"type": "uint32_t", "id": 2304, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmStatus1": {"type": "uint32_t", "id": 2305, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_TriggerAlarmCancel": {"type": "uint32_t", "id": 2306, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_AlarmStatus2": {"type": "uint32_t", "id": 2307, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmStatus3": {"type": "uint32_t", "id": 2308, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmStatus4": {"type": "uint32_t", "id": 2309, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmStatus5": {"type": "uint32_t", "id": 2310, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmStatus6": {"type": "uint32_t", "id": 2311, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmStatus7": {"type": "uint32_t", "id": 2312, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_AlarmEnableMask": {"type": "uint32_t", "id": 2313, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_AlarmBroadcastMask": {"type": "uint32_t", "id": 2314, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_AlarmVisualMask": {"type": "uint32_t", "id": 2315, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_AlarmVisualAutoClearMask": {"type": "uint32_t", "id": 2316, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_ExcessFlowVolumeThreshold": {"type": "uint32_t", "id": 2317, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_LeakTimeThreshold": {"type": "uint32_t", "id": 2318, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_ReverseFlowTimeThreshold": {"type": "uint16_t", "id": 2319, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_Locale": {"type": "uint16_t", "id": 2322, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_AppArrangement": {"type": "enum8", "id": 2323, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_RebootCount": {"type": "uint32_t", "id": 2324, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_ExcessFlowTimeThreshold": {"type": "uint32_t", "id": 2325, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_LeakFlowThreshold": {"type": "uint32_t", "id": 2326, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_MfgCharge": {"type": "uint32_t", "id": 2327, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_TemperatureHighThreshold": {"type": "int32_t", "id": 2328, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_TemperatureHighDelay": {"type": "uint32_t", "id": 2329, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_TemperatureLowThreshold": {"type": "int32_t", "id": 2330, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_TemperatureLowDelay": {"type": "uint32_t", "id": 2331, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_PressureHighThreshold": {"type": "int32_t", "id": 2332, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_PressureHighDelay": {"type": "uint32_t", "id": 2333, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_PressureLowThreshold": {"type": "int32_t", "id": 2334, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_PressureLowDelay": {"type": "uint32_t", "id": 2335, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_StoreConfiguration": {"type": "uint32_t", "id": 2336, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "RW", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_SerialNumber": {"type": "string", "id": 2337, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_BackupCalendarSeconds": {"type": "time_t", "id": 2338, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "NA", "lvl8": "NA"}},
"CUSTOMER_InstallationTime": {"type": "time_t", "id": 2339, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_LocaleDecimalPoint": {"type": "string", "id": 2340, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_LocaleThousandsSeparator": {"type": "string", "id": 2341, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"CUSTOMER_InternalLoginTest": {"type": "uint32_t", "id": 2342, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_NoFlowLimit": {"type": "uint32_t", "id": 2343, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"CUSTOMER_NoFlowAlarmResetHysteresis": {"type": "uint32_t", "id": 2344, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FLEXNETSERIAL_UpgState": {"type": "uint32_t", "id": 6656, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FLEXNETSERIAL_FwdlState": {"type": "uint32_t", "id": 6657, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_GP30Test": {"type": "uint32_t", "id": 2560, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_LCDTest": {"type": "uint32_t", "id": 2561, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_Iloop": {"type": "uint32_t", "id": 2562, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_Pulse": {"type": "uint32_t", "id": 2563, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_Pressure": {"type": "uint32_t", "id": 2564, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_BatteryVoltage": {"type": "uint32_t", "id": 2565, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"FUNCTEST_SupplyVoltage": {"type": "uint32_t", "id": 2566, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"FUNCTEST_Temperature": {"type": "int16_t", "id": 2567, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"FUNCTEST_RFID": {"type": "uint32_t", "id": 2568, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_OpticalOutput": {"type": "uint32_t", "id": 2569, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_Radio": {"type": "uint32_t", "id": 2570, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_MultiTest": {"type": "uint16_t", "id": 2571, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_NFCUID": {"type": "uint64_t", "id": 2572, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"FUNCTEST_CPUTest": {"type": "uint32_t", "id": 2573, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_SampleRate": {"type": "uint8_t", "id": 3840, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitLvlUp1": {"type": "int8_t", "id": 3841, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitLvlUp2": {"type": "int8_t", "id": 3842, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitLvlUp3": {"type": "int8_t", "id": 3843, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitLvlDown1": {"type": "int8_t", "id": 3844, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitLvlDown2": {"type": "int8_t", "id": 3845, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitLvlDown3": {"type": "int8_t", "id": 3846, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_StartHit": {"type": "uint8_t", "id": 3847, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_AmplitudePeakDetectEnd": {"type": "uint8_t", "id": 3848, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_NumFirePulses": {"type": "uint8_t", "id": 3849, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_DisplayPow10": {"type": "int8_t", "id": 3850, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_DisplayUnits": {"type": "enum8", "id": 3851, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MeterSize": {"type": "enum8", "id": 3852, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_CalFactor1": {"type": "uint16_t", "id": 3853, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_CalFactor2": {"type": "uint16_t", "id": 3854, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_CalFactor3": {"type": "uint16_t", "id": 3855, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ZeroOffset1": {"type": "int32_t", "id": 3856, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ZeroOffset2": {"type": "int32_t", "id": 3857, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ZeroOffset3": {"type": "int32_t", "id": 3858, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ScaledBilling": {"type": "int64_t", "id": 3859, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"GENESISFLOW_ResetAccumulators": {"type": "bool_t", "id": 3860, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"GENESISFLOW_ForwardArrow": {"type": "enum8", "id": 3861, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_LedMode": {"type": "enum8", "id": 3862, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_StoreCalibration": {"type": "uint32_t", "id": 3863, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_UnscaledFwd": {"type": "uint64_t", "id": 3864, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_UnscaledRev": {"type": "uint64_t", "id": 3865, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_LowFlowThreshold": {"type": "uint32_t", "id": 3866, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_LowFlowMaxPeriod": {"type": "uint32_t", "id": 3867, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_UpdateThreshold": {"type": "uint32_t", "id": 3868, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ArrowThreshold": {"type": "uint32_t", "id": 3869, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FireBuffer1": {"type": "uint8_t", "id": 3870, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FireBuffer2": {"type": "uint8_t", "id": 3871, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FireBuffer3": {"type": "uint8_t", "id": 3872, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_TriggerActive": {"type": "bool_t", "id": 3873, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "RW", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_TriggerIdle": {"type": "uint16_t", "id": 3874, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MaxValidDeltaToF": {"type": "uint32_t", "id": 3875, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MaxValidToF": {"type": "uint32_t", "id": 3876, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MinValidToF": {"type": "uint32_t", "id": 3877, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitPercent1": {"type": "uint8_t", "id": 3878, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitPercent2": {"type": "uint8_t", "id": 3879, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitPercent3": {"type": "uint8_t", "id": 3880, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitShift": {"type": "uint8_t", "id": 3881, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitMinimum": {"type": "uint8_t", "id": 3882, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ToFErrorLimit": {"type": "uint32_t", "id": 3883, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FirstHitUpdatePeriod": {"type": "uint8_t", "id": 3884, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_PipeFillingDelay": {"type": "uint8_t", "id": 3885, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ToFTempOffset1": {"type": "int32_t", "id": 3886, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ToFTempOffset2": {"type": "int32_t", "id": 3887, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ToFTempOffset3": {"type": "int32_t", "id": 3888, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_ToFTempCalibrate": {"type": "int32_t", "id": 3889, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"GENESISFLOW_StoreConfiguration": {"type": "uint32_t", "id": 3890, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "RW", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_SealDisplay": {"type": "bool_t", "id": 3891, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_TriggerTest": {"type": "bool_t", "id": 3892, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MaxValidAmplitude": {"type": "uint32_t", "id": 3893, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MinValidAmplitude": {"type": "uint32_t", "id": 3894, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_HardErrorLimit": {"type": "uint16_t", "id": 3895, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_Timeout": {"type": "uint8_t", "id": 3896, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MaxDeltaToFDeviation": {"type": "uint32_t", "id": 3897, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MaxTempRange": {"type": "uint32_t", "id": 3898, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_MaxDeltaToFRange": {"type": "uint32_t", "id": 3899, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_LookupFileCrc": {"type": "uint32_t", "id": 3900, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_DisplayLeadingZeros": {"type": "bool_t", "id": 3901, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_InstallationCorrectionEnabled": {"type": "bool_t", "id": 3902, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_InstallationDetectionThreshold": {"type": "uint32_t", "id": 3903, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_InstallationType": {"type": "uint32_t", "id": 3904, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_InstallationDetectionStatus": {"type": "uint8_t", "id": 3905, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_FractionalBars": {"type": "bool_t", "id": 3906, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"GENESISFLOW_CalibrationCount": {"type": "uint8_t", "id": 3907, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"GENESISFLOW_DelayedLedOff": {"type": "uint16_t", "id": 3908, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "WO", "lvl6": "WO", "lvl7": "WO", "lvl8": "WO"}},
"GENESISFLOW_DisplayVolumeRow2": {"type": "bool_t", "id": 3909, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_PulseReportRate": {"type": "enum8", "id": 5120, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_AdapterPresenceLimit": {"type": "uint8_t", "id": 5121, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_PulseSequence": {"type": "uint8_t", "id": 5122, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_StoreConfiguration": {"type": "uint32_t", "id": 5123, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_AMRDigits": {"type": "uint8_t", "id": 5124, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_AMROffset": {"type": "int8_t", "id": 5125, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_UI1203Fields": {"type": "uint16_t", "id": 5126, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_MfgDate": {"type": "time_t", "id": 5127, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_DisplayAMRDigits": {"type": "bool_t", "id": 5128, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"IRDA_AdapterID": {"type": "string", "id": 5129, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"IRDA_AdapterFwVersion": {"type": "uint16_t", "id": 5130, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"IRDA_ExtendedResolution": {"type": "bool_t", "id": 5131, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"LOGGER_Quota": {"type": "uint32_t", "id": 2048, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"LOGGER_Drive": {"type": "uint8_t", "id": 2049, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"LOGGER_TriggerLogFlush": {"type": "bool_t", "id": 2050, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"LOGGER_StoreConfiguration": {"type": "uint32_t", "id": 2051, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_ILoopMaxFlowRate": {"type": "uint32_t", "id": 4608, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_StoreConfiguration": {"type": "uint32_t", "id": 4609, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "RW", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PulseWeight": {"type": "uint32_t", "id": 4610, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PulseMode": {"type": "enum8", "id": 4611, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PulseLength": {"type": "enum8", "id": 4612, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_FlowUnits": {"type": "enum8", "id": 4613, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_FlowPoint": {"type": "enum8", "id": 4614, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_ILoopRate": {"type": "uint32_t", "id": 4615, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_TemperatureUnits": {"type": "enum8", "id": 4616, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressureUnits": {"type": "enum8", "id": 4617, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressureRate": {"type": "uint32_t", "id": 4618, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressureOffset": {"type": "int32_t", "id": 4619, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressurePresent": {"type": "bool_t", "id": 4620, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressureMeasure": {"type": "int32_t", "id": 4621, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_LatestFlowRate": {"type": "int32_t", "id": 4622, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_GenerateFwdPulses": {"type": "uint8_t", "id": 4623, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"METROLOGYASST_GenerateRevPulses": {"type": "uint8_t", "id": 4624, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"METROLOGYASST_PulseEvenDistribution": {"type": "bool_t", "id": 4625, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PulseResolution": {"type": "uint8_t", "id": 4626, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"METROLOGYASST_PressureCalibration": {"type": "int32_t", "id": 4627, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_DisplayEnable": {"type": "uint32_t", "id": 4628, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RW", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressureFlowRateCorrectionA": {"type": "int32_t", "id": 4629, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"METROLOGYASST_PressureFlowRateCorrectionB": {"type": "int32_t", "id": 4630, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"NA2WALARMS_StoreConfiguration": {"type": "uint32_t", "id": 5888, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "RW", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "RW", "lvl8": "RW"}},
"NA2WALARMS_PredefEnable": {"type": "uint32_t", "id": 5889, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"NA2WLOGGER_StoreConfiguration": {"type": "uint32_t", "id": 5632, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "RW", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "RW", "lvl8": "RW"}},
"NA2WLOGGER_LimitLogSize": {"type": "uint32_t", "id": 5633, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"NFC_EraseRMA": {"type": "bool_t", "id": 5376, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RW"}},
"NFC_ForceUpdate": {"type": "uint8_t", "id": 5377, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"OPTICALPORT_BaudRateCapabilities": {"type": "uint32_t", "id": 1280, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"OPTICALPORT_PacketSizeCapabilities": {"type": "uint32_t", "id": 1281, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"OPTICALPORT_ProtocolVersion": {"type": "uint16_t", "id": 1282, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"OPTICALPORT_BaudRateSelected": {"type": "uint32_t", "id": 1283, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"OPTICALPORT_PacketSizeSelected": {"type": "uint32_t", "id": 1284, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"OPTICALPORT_ExternalUartControl": {"type": "uint8_t", "id": 1285, "privilege": {"lvl1": "WO", "lvl2": "WO", "lvl3": "WO", "lvl4": "WO", "lvl5": "WO", "lvl6": "WO", "lvl7": "WO", "lvl8": "WO"}},
"PERIODICLOG_DataLogContents": {"type": "uint32_t", "id": 4352, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"PERIODICLOG_DataLogPeriod": {"type": "uint16_t", "id": 4353, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"PERIODICLOG_AverageFlowPeriod": {"type": "uint16_t", "id": 4354, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"PERIODICLOG_FixedDateReadingContents": {"type": "uint32_t", "id": 4355, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"PERIODICLOG_FixedDateDayOfMonth": {"type": "uint8_t", "id": 4356, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"PERIODICLOG_PeriodicLogLifeTimeCounter": {"type": "uint32_t", "id": 4357, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"PERIODICLOG_ResetCounter": {"type": "uint32_t", "id": 4358, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_BatteryVoltage": {"type": "uint16_t", "id": 256, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"POWERMON_BatteryManufacturer": {"type": "string", "id": 257, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"POWERMON_BatterySize": {"type": "enum8", "id": 258, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"POWERMON_BatteryQuantity": {"type": "uint8_t", "id": 260, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_BatteryRatedVoltage": {"type": "uint8_t", "id": 261, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"POWERMON_BatteryVoltageMinThreshold": {"type": "uint8_t", "id": 263, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"POWERMON_BatterySelection": {"type": "uint8_t", "id": 264, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_TotalUsedCharge": {"type": "uint64_t", "id": 265, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_TotalUsedSeconds": {"type": "uint32_t", "id": 266, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_WarnFromClamp": {"type": "uint32_t", "id": 267, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_BatteryMilliAHrRating": {"type": "uint16_t", "id": 268, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"POWERMON_StoreConfiguration": {"type": "uint32_t", "id": 269, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_CriticalRepeatLimit": {"type": "uint8_t", "id": 270, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"POWERMON_RemainingSeconds": {"type": "uint32_t", "id": 271, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"SENSUSRADIO_TxInterval": {"type": "uint16_t", "id": 4096, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_OmTxInterval": {"type": "uint16_t", "id": 4097, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_LatInterval": {"type": "uint8_t", "id": 4098, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_WakeupInterval": {"type": "uint8_t", "id": 4099, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_MbusState": {"type": "uint8_t", "id": 4100, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_FrequencyIndicator": {"type": "uint16_t", "id": 4101, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_FrequencyOffset": {"type": "int16_t", "id": 4102, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PowerLevel": {"type": "uint8_t", "id": 4103, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_SystemState": {"type": "uint8_t", "id": 4105, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_RadioAddress": {"type": "uint32_t", "id": 4106, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_UtcTimeOffset": {"type": "uint32_t", "id": 4107, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_SentBytesCounter": {"type": "uint16_t", "id": 4108, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ReceivedBytesCounter": {"type": "uint16_t", "id": 4109, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ResetCounter": {"type": "uint16_t", "id": 4110, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_BootLoaderState": {"type": "uint8_t", "id": 4111, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_LeakFlowThreshold": {"type": "uint16_t", "id": 4112, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_LeakFlowTimeThreshold": {"type": "uint16_t", "id": 4113, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_BrokenPipeFlowThreshold": {"type": "uint16_t", "id": 4114, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_BrokenPipeFlowTimeThreshold": {"type": "uint16_t", "id": 4115, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureMaxThreshold": {"type": "uint8_t", "id": 4116, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureMinThreshold": {"type": "uint8_t", "id": 4117, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureLimitTimeMaxThreshold": {"type": "uint16_t", "id": 4118, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureLimitTimeMinThreshold": {"type": "uint16_t", "id": 4119, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureMeasurePeriod": {"type": "uint16_t", "id": 4120, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureUnit": {"type": "uint8_t", "id": 4121, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureGaugeOffset": {"type": "uint16_t", "id": 4122, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TemperatureMaxThreshold": {"type": "uint8_t", "id": 4123, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TemperatureMinThreshold": {"type": "uint8_t", "id": 4124, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TemperatureTimeMaxThreshold": {"type": "uint16_t", "id": 4125, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TemperatureTimeMinThreshold": {"type": "uint16_t", "id": 4126, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TemperatureMeasurePeriod": {"type": "uint16_t", "id": 4127, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TemperatureUnit": {"type": "uint8_t", "id": 4128, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PulseOutWidth": {"type": "uint16_t", "id": 4129, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PulseOutDivisor": {"type": "uint16_t", "id": 4130, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PulseOutMode": {"type": "uint8_t", "id": 4131, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_CurrentLoopMax": {"type": "uint16_t", "id": 4132, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_CurrentLoopSource": {"type": "uint8_t", "id": 4133, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_MainAlarmMask": {"type": "uint8_t", "id": 4134, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ExtendedAlarmMask": {"type": "uint8_t", "id": 4135, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_HistoricalAlarmsDays": {"type": "uint8_t", "id": 4136, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TestModeTime": {"type": "uint16_t", "id": 4139, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_EncryptionKey": {"type": "uint32_t", "id": 4140, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_Authentification": {"type": "uint32_t", "id": 4141, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_UpgFWVersion": {"type": "uint16_t", "id": 4142, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_CustomerText": {"type": "uint32_t", "id": 4143, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_RadioLastReadTime": {"type": "uint32_t", "id": 4144, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_LowBatDateTime": {"type": "uint32_t", "id": 4145, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_VeryLowBatDateTime": {"type": "uint32_t", "id": 4146, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_Unit": {"type": "uint8_t", "id": 4147, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ExtendedUnitFlags": {"type": "uint8_t", "id": 4148, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_OTAControlFlags": {"type": "uint8_t", "id": 4149, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_Tfx_Structure": {"type": "uint32_t", "id": 4150, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_Dewa_Structure": {"type": "uint32_t", "id": 4151, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_DataLogContents": {"type": "uint32_t", "id": 4152, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_DataLogPeriod": {"type": "uint16_t", "id": 4153, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_AverageFlowPeriod": {"type": "uint16_t", "id": 4154, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_FixedDateReadingContents": {"type": "uint32_t", "id": 4155, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_FixedDateDayOfMonth": {"type": "uint8_t", "id": 4156, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_MetroRadioLifeTimeCounter": {"type": "uint32_t", "id": 4157, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PowerLevelOption": {"type": "uint8_t", "id": 4158, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ImpedanceCodeNew": {"type": "uint16_t", "id": 4159, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ImpedanceCodeOption": {"type": "uint16_t", "id": 4160, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_IrDAModulePresent": {"type": "bool_t", "id": 4161, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_StoreConfiguration": {"type": "bool_t", "id": 4162, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_LifeTimeSeconds": {"type": "uint32_t", "id": 4163, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_DutyCycleCredit": {"type": "uint32_t", "id": 4164, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_ActivityCredit": {"type": "uint32_t", "id": 4165, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PersistenceGroup1": {"type": "uint32_t", "id": 4166, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PersistenceGroup2": {"type": "uint32_t", "id": 4167, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PersistenceGroup3": {"type": "uint32_t", "id": 4168, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PersistenceGroup4": {"type": "uint32_t", "id": 4169, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PressureCalibration": {"type": "uint32_t", "id": 4170, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TfxSecondaryChannelInfo_1": {"type": "uint32_t", "id": 4171, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_TfxSecondaryChannelInfo_2": {"type": "uint32_t", "id": 4172, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_OmsLastMessageCounter": {"type": "uint32_t", "id": 4173, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PersistenceGroup5": {"type": "uint32_t", "id": 4174, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SENSUSRADIO_PersistenceGroup6": {"type": "uint32_t", "id": 4175, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_TriggerUpgrade": {"type": "bool_t", "id": 0, "privilege": {"lvl1": "NA", "lvl2": "NA", "lvl3": "NA", "lvl4": "NA", "lvl5": "NA", "lvl6": "NA", "lvl7": "WO", "lvl8": "WO"}},
"SYSTEM_CheckPresence": {"type": "RPC", "id": 1, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_PCBSerialNumber": {"type": "string", "id": 2, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"SYSTEM_CustomerSerialNumber0": {"type": "string", "id": 3, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_CustomerSerialNumber1": {"type": "string", "id": 4, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_CustomerSerialNumber2": {"type": "string", "id": 5, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_CustomerSerialNumber3": {"type": "string", "id": 6, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_MonotonicSeconds": {"type": "uint32_t", "id": 7, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"SYSTEM_CalendarSeconds": {"type": "time_t", "id": 8, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_CoreRevision": {"type": "string", "id": 9, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"SYSTEM_DriveCapacity": {"type": "uint32_t", "id": 10, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_ExitReason": {"type": "status_t", "id": 11, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_CorePlatform": {"type": "uint16_t", "id": 12, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RO", "lvl8": "RO"}},
"SYSTEM_CRC": {"type": "uint16_t", "id": 13, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_UpgradePermissions": {"type": "uint8_t", "id": 14, "privilege": {"lvl1": "RO", "lvl2": "RO", "lvl3": "RO", "lvl4": "RO", "lvl5": "RO", "lvl6": "RO", "lvl7": "RW", "lvl8": "RW"}},
"SYSTEM_CRC32": {"type": "uint32_t", "id": 15, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"TESTMANAGER_OutputFile": {"type": "string", "id": 25088, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"TESTMANAGER_TestNumber": {"type": "uint32_t", "id": 25089, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"TESTMANAGER_TestStatus": {"type": "enum8", "id": 25090, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"TESTMANAGER_TrapError": {"type": "uint32_t", "id": 25091, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"TESTMANAGER_TestParameter": {"type": "uint32_t", "id": 25092, "privilege": {"lvl1": "RW", "lvl2": "RW", "lvl3": "RW", "lvl4": "RW", "lvl5": "RW", "lvl6": "RW", "lvl7": "RW", "lvl8": "RW"}},
"_comment": "end of table"
}
@@ -0,0 +1,46 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments
{
/// <inheritdoc cref="EventArgs" />
public abstract class BasePortDataEventArgs : EventArgs, IPortDataEventArgs
{
/// <summary>
/// Marker for incoming record at time of the PC
/// </summary>
private DateTimeOffset _receivedTime = default(DateTimeOffset);
/// <inheritdoc />
public abstract Object GetData();
/// <inheritdoc />
public abstract void SetData(Object data);
/// <inheritdoc />
public void SetReceivedTime(DateTimeOffset receivedTime)
{
_receivedTime = receivedTime;
}
/// <inheritdoc />
public DateTimeOffset GetReceivedTime()
{
return _receivedTime;
}
private SyncMarkRecord _syncMarkRecord;
/// <inheritdoc />
public void SetSyncMarkRecord(SyncMarkRecord syncMarkRecord)
{
_syncMarkRecord = syncMarkRecord;
}
/// <inheritdoc />
public SyncMarkRecord GetSyncMarkRecord()
{
return _syncMarkRecord;
}
}
}
@@ -0,0 +1,48 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments
{
/// <summary>
/// Interface for port data event arguments
/// </summary>
public interface IPortDataEventArgs
{
/// <summary>
/// Read data
/// </summary>
/// <returns></returns>
Object GetData();
/// <summary>
/// Write data
/// </summary>
/// <param name="data"></param>
void SetData(Object data);
/// <summary>
/// Reading received time
/// </summary>
/// <returns></returns>
DateTimeOffset GetReceivedTime();
/// <summary>
/// Writing received time
/// </summary>
/// <param name="receivedTime"></param>
void SetReceivedTime(DateTimeOffset receivedTime);
/// <summary>
/// Setting the data marker
/// </summary>
/// <param name="syncMarkRecord"></param>
void SetSyncMarkRecord(SyncMarkRecord syncMarkRecord);
/// <summary>
/// Getting the data marker
/// </summary>
/// <returns></returns>
SyncMarkRecord GetSyncMarkRecord();
}
}
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using Xylem.Common.CommonCore.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments
{
/// <inheritdoc />
public class ListBytePortDataEventArgs : BasePortDataEventArgs
{
private List<Byte> _data;
/// <inheritdoc />
public ListBytePortDataEventArgs(List<Byte> data, DateTimeOffset readTimeStampPc = default(DateTimeOffset),
SyncMarkRecord syncMarkRecord = SyncMarkRecord.SkipDecoding)
{
_data = data;
SetSyncMarkRecord(syncMarkRecord);
SetReceivedTime(readTimeStampPc);
}
/// <inheritdoc />
public override Object GetData()
{
return _data;
}
/// <inheritdoc />
public override void SetData(Object data)
{
_data = (List<Byte>)data;
}
}
}
@@ -0,0 +1,34 @@
using System;
using Xylem.Common.CommonCore.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments
{
/// <inheritdoc />
public class StringPortDataEventArgs : BasePortDataEventArgs
{
private String _data;
/// <inheritdoc />
public StringPortDataEventArgs(String data, DateTimeOffset readTimeStampPc = default(DateTimeOffset),
SyncMarkRecord syncMarkRecord = SyncMarkRecord.SkipDecoding)
{
_data = data;
SetSyncMarkRecord(syncMarkRecord);
SetReceivedTime(readTimeStampPc);
}
/// <inheritdoc />
public override Object GetData()
{
return _data;
}
/// <inheritdoc />
public override void SetData(Object data)
{
_data = (String)data;
}
}
}
@@ -0,0 +1,63 @@
using System;
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
using Xylem.Common.CommonCore.Consts;
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore
{
/// <summary>
/// Interface for Ports
/// </summary>
public interface IPort
{
/// <summary>
/// event for record received, either bytes or string
/// </summary>
event EventHandler<BasePortDataEventArgs> OnRawRecordReceived;
/// <summary>
/// event for record received, either bytes or string
/// </summary>
event EventHandler<BasePortDataEventArgs> OnRawRecordSendOut;
/// <summary>
/// set specific mark
/// and flush or delete all incoming byte from buffer when SyncMarkRecord is <see cref="SyncMarkRecord.SyncStart"/> or
/// <see cref="SyncMarkRecord.SyncEnd"/>
/// </summary>
/// <param name="syncMarkRecord"></param>
void SynchronizeReceiveBuffer(SyncMarkRecord syncMarkRecord);
/// <summary>
/// if the port needs stuff to open, always open for better logical handling
/// </summary>
void Open();
/// <summary>
/// close and dispose all connections
/// </summary>
void Dispose();
/// <summary>
/// indicates if the Port is open (also on Ports that did not have an open state)
/// </summary>
/// <returns></returns>
Boolean IsOpen();
/// <summary>
/// Write byte[] to the Stream/Port on Child Class
/// wrapped with base class error handling
/// </summary>
/// <param name="data">Array of bytes to write</param>
void PortWrite(Byte[] data);
/// <summary>
/// discard all buffers
/// </summary>
void Clear();
/// <summary>
/// Return the port name
/// </summary>
/// <returns>name of the port as string</returns>
String GetPortName();
}
}

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