Compare commits
59
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c79688dc0c | ||
|
|
74bcbaf12d | ||
|
|
a3821d722b | ||
|
|
9339683a0e | ||
|
|
7170da4a70 | ||
|
|
842f009b42 | ||
|
|
7f30ca0cd1 | ||
|
|
93e207c211 | ||
|
|
64cafc088a | ||
|
|
8a0da27a07 | ||
|
|
5a8478e782 | ||
|
|
368cd95114 | ||
|
|
33ef914984 | ||
|
|
3eb0b87f0d | ||
|
|
711dfc03ef | ||
|
|
93459ccfab | ||
|
|
009aefb7c9 | ||
|
|
d2e18d615d | ||
|
|
69b475eb77 | ||
|
|
2210bb8240 | ||
|
|
3376ac41b4 | ||
|
|
f9181fee74 | ||
|
|
65ab19290f | ||
|
|
94c7279da1 | ||
|
|
2136695b80 | ||
|
|
c0f9b0ffa0 | ||
|
|
c4ef22436f | ||
|
|
0b73871518 | ||
|
|
f7dc6fee5e | ||
|
|
ba848a167d | ||
|
|
dcb0bf9a88 | ||
|
|
f6433134c1 | ||
|
|
7e3df77b5b | ||
|
|
ff5954d85b | ||
|
|
08dff1272b | ||
|
|
c09e266b2c | ||
|
|
d5c8fd1c8f | ||
|
|
a34740e838 | ||
|
|
836ba5a41f | ||
|
|
23c83ab452 | ||
|
|
bc9e583c29 | ||
|
|
18cef617da | ||
|
|
4ee736cff5 | ||
|
|
4e40dc8601 | ||
|
|
a5a68ec457 | ||
|
|
e26d88d437 | ||
|
|
2522d1da9f | ||
|
|
8983c01591 | ||
|
|
61b1320d4f | ||
|
|
2c8a0922d1 | ||
|
|
b078d9586d | ||
|
|
6e35de5f4d | ||
|
|
2dbe4cded4 | ||
|
|
10865c6878 | ||
|
|
ee1353fe54 | ||
|
|
5cefd7fc3a | ||
|
|
ab0e5ea82a | ||
|
|
b2276c086c | ||
|
|
0c41c750e8 |
@@ -13,5 +13,6 @@ namespace SchematicDrawing
|
||||
Common.Unit MsrdUnit { get; }
|
||||
double MsrdValLimLo { get; }
|
||||
double MsrdValLimHi { get; }
|
||||
bool IsOffline { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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("")]
|
||||
|
||||
@@ -19,6 +19,9 @@ using System.Runtime.InteropServices;
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
//Visibility to TBFTests internal classes
|
||||
[assembly: InternalsVisibleTo("TBFTests")]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("2c13538e-15ee-48b2-af0f-2c95afb67186")]
|
||||
|
||||
@@ -29,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
[assembly: AssemblyVersion("3.9.2149.4")]
|
||||
[assembly: AssemblyFileVersion("3.9.2149.4")]
|
||||
[assembly: AssemblyVersion("3.9.3058.1")]
|
||||
[assembly: AssemblyFileVersion("3.9.3058.1")]
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Generated
+9
-1
@@ -1,7 +1,6 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <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.
|
||||
@@ -6270,6 +6269,15 @@ namespace TBF.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Test in calculation.
|
||||
/// </summary>
|
||||
internal static string Test_in_calculation {
|
||||
get {
|
||||
return ResourceManager.GetString("Test_in_calculation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Test in progress.
|
||||
/// </summary>
|
||||
|
||||
Generated
+5012
File diff suppressed because it is too large
Load Diff
@@ -1764,4 +1764,7 @@
|
||||
<data name="Units" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="Test_in_calculation" xml:space="preserve">
|
||||
<value>Probíhá výpočet</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -2482,4 +2482,7 @@
|
||||
<data name="Units" xml:space="preserve">
|
||||
<value>Units</value>
|
||||
</data>
|
||||
<data name="Test_in_calculation" xml:space="preserve">
|
||||
<value>Test in calculation</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -259,4 +259,7 @@
|
||||
<data name="Units" xml:space="preserve">
|
||||
<value />
|
||||
</data>
|
||||
<data name="Test_in_calculation" xml:space="preserve">
|
||||
<value>Prebieha výpočet</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -111,6 +111,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
|
||||
|
||||
public void TurnOff()
|
||||
{
|
||||
log.DebugFormat("{0}.TurnOff()", Name);
|
||||
if ((pumpFm1 != null) && (pumpFm2 != null))
|
||||
{
|
||||
pumpFm1.TurnOff();
|
||||
@@ -120,6 +121,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -175,8 +175,8 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
double reqFlowLo = (0.7 * qFrom) + (0.3 * reqFlowAve); /// Move the lower limit 15% of the range up
|
||||
double reqFlowHi = (0.7 * qTo) + (0.3 * reqFlowAve); /// Move the upper limit 15% of the range down
|
||||
///
|
||||
uniCB.SetFlow(false, regV.Idx1, 2000.0 * reqFlowLo / flowMeter.NominalFlow,
|
||||
2000.0 * reqFlowHi / flowMeter.NominalFlow);
|
||||
uniCB.SetFlow(false, regV.Idx1, flowMeter.NominalFreq * reqFlowLo / flowMeter.NominalFlow,
|
||||
flowMeter.NominalFreq * reqFlowHi / flowMeter.NominalFlow);
|
||||
if (readDivTransition)
|
||||
{
|
||||
/// Schedule reading diverter transition data
|
||||
|
||||
@@ -803,12 +803,60 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
/// </summary>
|
||||
public void SetFlow(bool isFromUI, int regVId, double freqLo, double freqHi, int regulationMinStep = 0)
|
||||
{
|
||||
if (IsUIBlocked && isFromUI) return;
|
||||
LiveLogDiag.Log1("----------------------------------------");
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> ENTER isFromUI={0} regVId={1} freqLo={2} freqHi={3} regulationMinStep={4} IsUIBlocked={5}",
|
||||
isFromUI, regVId, freqLo, freqHi, regulationMinStep, IsUIBlocked);
|
||||
|
||||
actionQueue.Enqueue(Action.SetFlow(isFromUI, regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)), 200, regulationMinStep));
|
||||
log.InfoFormat("Enqueue( SetFlow(rv={0}, fLo={1}, fHi={2}, pid={3}) )", regVId, freqLo, freqHi, Convert.ToInt32(Math.Round(Devices.PidCoef)));
|
||||
if (IsUIBlocked && isFromUI)
|
||||
{
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> EXIT (UI BLOCKED) isFromUI={0}",
|
||||
isFromUI);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var a in actionQueue) log.DebugFormat(" {0}", a);
|
||||
int pid = Convert.ToInt32(Math.Round(Devices.PidCoef));
|
||||
int fixedParam = 200;
|
||||
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> PREPARE pid={0} fixedParam={1} regulationMinStep={2}",
|
||||
pid, fixedParam, regulationMinStep);
|
||||
|
||||
var action = Action.SetFlow(
|
||||
isFromUI,
|
||||
regVId,
|
||||
freqLo,
|
||||
freqHi,
|
||||
pid,
|
||||
fixedParam,
|
||||
regulationMinStep);
|
||||
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> ACTION CREATED: rv={0} fLo={1} fHi={2} pid={3} p6={4} minStep={5}",
|
||||
regVId, freqLo, freqHi, pid, fixedParam, regulationMinStep);
|
||||
|
||||
actionQueue.Enqueue(action);
|
||||
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> ENQUEUED actionQueue.Count={0}",
|
||||
actionQueue.Count);
|
||||
|
||||
log.InfoFormat(
|
||||
"Enqueue( SetFlow(rv={0}, fLo={1}, fHi={2}, pid={3}) )",
|
||||
regVId, freqLo, freqHi, pid);
|
||||
|
||||
int i = 0;
|
||||
foreach (var a in actionQueue)
|
||||
{
|
||||
LiveLogDiag.Log1(
|
||||
"SetFlow() >> QUEUE[{0}] = {1}",
|
||||
i++, a);
|
||||
log.DebugFormat(" {0}", a);
|
||||
}
|
||||
|
||||
LiveLogDiag.Log1("SetFlow() >> EXIT");
|
||||
LiveLogDiag.Log1("----------------------------------------");
|
||||
}
|
||||
|
||||
public void StartTest(bool isFromUI, int flowMId, int divId, int divThreshold,
|
||||
@@ -981,5 +1029,18 @@ namespace TBF.Rig.ControlBoard.Uni
|
||||
{
|
||||
/// TODO
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logging
|
||||
/// For activation use compilation condition: LIVELOGDIAG_FlowMeter_cs
|
||||
/// </summary>
|
||||
static class LiveLogDiag
|
||||
{
|
||||
[Conditional("LIVELOGDIAG_UniCB_cs")]
|
||||
public static void Log1(string format, params object[] args)
|
||||
{
|
||||
LiveLogCache.Instance.AddLog("UniCB.cs LOG>> " + string.Format(format, args));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -332,6 +332,8 @@ namespace TBF.Rig.Danfoss.VLT2800
|
||||
Telegram.UpdateTelegramChecksum(msg);
|
||||
}
|
||||
SendData(msg);
|
||||
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -264,6 +264,8 @@ namespace TBF.Rig.Modbus.PumpFM.DanfossVLT
|
||||
|
||||
modbus.SendMessage(msg, Name);
|
||||
}
|
||||
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -416,6 +416,8 @@ namespace TBF.Rig.Modbus.PumpFM.Grundfoss
|
||||
// --- DIAGNOSTIKA ---
|
||||
LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg));
|
||||
}
|
||||
|
||||
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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,75 @@
|
||||
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
|
||||
public bool EnableShowChanels;
|
||||
public int IBeginDataFlush;
|
||||
|
||||
|
||||
|
||||
/// <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;
|
||||
EnableShowChanels = false;
|
||||
IBeginDataFlush = 2000;
|
||||
}
|
||||
|
||||
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}, EnableShowChanels={EnableShowChanels}, IBeginDataFlush={IBeginDataFlush}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
///
|
||||
/// 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();
|
||||
tBBeginDataFlush.Text = config.IBeginDataFlush.ToString();
|
||||
checkBox_EnableShowChanels.Checked = config.EnableShowChanels;
|
||||
|
||||
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;
|
||||
tBBeginDataFlush.Enabled = true;
|
||||
checkBox_EnableShowChanels.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);
|
||||
}
|
||||
|
||||
if (!int.TryParse(tBBeginDataFlush.Text, out dummy) || dummy < 0 || dummy > 10000)
|
||||
{
|
||||
flags |= CfgUpdateFlags.Error;
|
||||
message += Environment.NewLine + string.Format(Strings.Invalid_0, tBBeginDataFlush.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);
|
||||
config.IBeginDataFlush = int.Parse(tBBeginDataFlush.Text);
|
||||
config.EnableShowChanels = checkBox_EnableShowChanels.Checked;
|
||||
|
||||
return flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
///
|
||||
/// 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.groupBox2 = new System.Windows.Forms.GroupBox();
|
||||
this.headPortNrTextBox = new System.Windows.Forms.TextBox();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
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.checkBox_EnableShowChanels = new System.Windows.Forms.CheckBox();
|
||||
this.tBBeginDataFlush = new System.Windows.Forms.TextBox();
|
||||
this.labelFlush = new System.Windows.Forms.Label();
|
||||
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.tabControl1.SuspendLayout();
|
||||
this.tabPage1.SuspendLayout();
|
||||
this.groupBox2.SuspendLayout();
|
||||
this.groupBox1.SuspendLayout();
|
||||
this.optoDataGroupBox.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// tabControl1
|
||||
//
|
||||
this.tabControl1.Controls.Add(this.tabPage1);
|
||||
this.tabControl1.Controls.Add(this.tabPage2);
|
||||
this.tabControl1.Location = new System.Drawing.Point(3, 4);
|
||||
this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tabControl1.Name = "tabControl1";
|
||||
this.tabControl1.SelectedIndex = 0;
|
||||
this.tabControl1.Size = new System.Drawing.Size(687, 540);
|
||||
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, 29);
|
||||
this.tabPage1.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tabPage1.Name = "tabPage1";
|
||||
this.tabPage1.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tabPage1.Size = new System.Drawing.Size(679, 507);
|
||||
this.tabPage1.TabIndex = 0;
|
||||
this.tabPage1.Text = "Config";
|
||||
this.tabPage1.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// groupBox2
|
||||
//
|
||||
this.groupBox2.Controls.Add(this.headPortNrTextBox);
|
||||
this.groupBox2.Controls.Add(this.label2);
|
||||
this.groupBox2.Location = new System.Drawing.Point(11, 450);
|
||||
this.groupBox2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.groupBox2.Name = "groupBox2";
|
||||
this.groupBox2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.groupBox2.Size = new System.Drawing.Size(621, 52);
|
||||
this.groupBox2.TabIndex = 26;
|
||||
this.groupBox2.TabStop = false;
|
||||
this.groupBox2.Text = "Head Communication";
|
||||
//
|
||||
// headPortNrTextBox
|
||||
//
|
||||
this.headPortNrTextBox.Enabled = false;
|
||||
this.headPortNrTextBox.Location = new System.Drawing.Point(494, 19);
|
||||
this.headPortNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.headPortNrTextBox.Name = "headPortNrTextBox";
|
||||
this.headPortNrTextBox.Size = new System.Drawing.Size(49, 26);
|
||||
this.headPortNrTextBox.TabIndex = 8;
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(361, 22);
|
||||
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(107, 20);
|
||||
this.label2.TabIndex = 7;
|
||||
this.label2.Text = "Serial port nr.:";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(234, 126);
|
||||
this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(52, 20);
|
||||
this.label4.TabIndex = 25;
|
||||
this.label4.Text = "1 .. 10";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(234, 90);
|
||||
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(43, 20);
|
||||
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(11, 363);
|
||||
this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.groupBox1.Name = "groupBox1";
|
||||
this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.groupBox1.Size = new System.Drawing.Size(621, 85);
|
||||
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(226, 34);
|
||||
this.comboBoxCommunicationInterface.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.comboBoxCommunicationInterface.Name = "comboBoxCommunicationInterface";
|
||||
this.comboBoxCommunicationInterface.Size = new System.Drawing.Size(79, 28);
|
||||
this.comboBoxCommunicationInterface.TabIndex = 9;
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(46, 38);
|
||||
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(187, 20);
|
||||
this.label1.TabIndex = 8;
|
||||
this.label1.Text = "Communication Interface";
|
||||
//
|
||||
// rfidPortNrTextBox
|
||||
//
|
||||
this.rfidPortNrTextBox.Enabled = false;
|
||||
this.rfidPortNrTextBox.Location = new System.Drawing.Point(494, 32);
|
||||
this.rfidPortNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.rfidPortNrTextBox.Name = "rfidPortNrTextBox";
|
||||
this.rfidPortNrTextBox.Size = new System.Drawing.Size(49, 26);
|
||||
this.rfidPortNrTextBox.TabIndex = 7;
|
||||
//
|
||||
// rfidSerialPortNrLabel
|
||||
//
|
||||
this.rfidSerialPortNrLabel.AutoSize = true;
|
||||
this.rfidSerialPortNrLabel.Location = new System.Drawing.Point(361, 38);
|
||||
this.rfidSerialPortNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.rfidSerialPortNrLabel.Name = "rfidSerialPortNrLabel";
|
||||
this.rfidSerialPortNrLabel.Size = new System.Drawing.Size(107, 20);
|
||||
this.rfidSerialPortNrLabel.TabIndex = 6;
|
||||
this.rfidSerialPortNrLabel.Text = "Serial port nr.:";
|
||||
//
|
||||
// optoDataGroupBox
|
||||
//
|
||||
this.optoDataGroupBox.Controls.Add(this.checkBox_EnableShowChanels);
|
||||
this.optoDataGroupBox.Controls.Add(this.tBBeginDataFlush);
|
||||
this.optoDataGroupBox.Controls.Add(this.labelFlush);
|
||||
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(11, 164);
|
||||
this.optoDataGroupBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.optoDataGroupBox.Name = "optoDataGroupBox";
|
||||
this.optoDataGroupBox.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.optoDataGroupBox.Size = new System.Drawing.Size(621, 189);
|
||||
this.optoDataGroupBox.TabIndex = 18;
|
||||
this.optoDataGroupBox.TabStop = false;
|
||||
this.optoDataGroupBox.Text = "Opto-data";
|
||||
//
|
||||
// checkBox_EnableShowChanels
|
||||
//
|
||||
this.checkBox_EnableShowChanels.Checked = true;
|
||||
this.checkBox_EnableShowChanels.CheckState = System.Windows.Forms.CheckState.Checked;
|
||||
this.checkBox_EnableShowChanels.Enabled = false;
|
||||
this.checkBox_EnableShowChanels.Location = new System.Drawing.Point(351, 147);
|
||||
this.checkBox_EnableShowChanels.Name = "checkBox_EnableShowChanels";
|
||||
this.checkBox_EnableShowChanels.Size = new System.Drawing.Size(238, 24);
|
||||
this.checkBox_EnableShowChanels.TabIndex = 10;
|
||||
this.checkBox_EnableShowChanels.Text = "Enable Show Channels";
|
||||
this.checkBox_EnableShowChanels.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// tBBeginDataFlush
|
||||
//
|
||||
this.tBBeginDataFlush.Enabled = false;
|
||||
this.tBBeginDataFlush.Location = new System.Drawing.Point(474, 105);
|
||||
this.tBBeginDataFlush.MaxLength = 8;
|
||||
this.tBBeginDataFlush.Name = "tBBeginDataFlush";
|
||||
this.tBBeginDataFlush.Size = new System.Drawing.Size(69, 26);
|
||||
this.tBBeginDataFlush.TabIndex = 9;
|
||||
this.tBBeginDataFlush.Text = "2000";
|
||||
this.tBBeginDataFlush.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
|
||||
//
|
||||
// labelFlush
|
||||
//
|
||||
this.labelFlush.Location = new System.Drawing.Point(328, 109);
|
||||
this.labelFlush.Name = "labelFlush";
|
||||
this.labelFlush.Size = new System.Drawing.Size(140, 22);
|
||||
this.labelFlush.TabIndex = 8;
|
||||
this.labelFlush.Text = "Begin Data Flush:";
|
||||
//
|
||||
// tcpipPortLabel
|
||||
//
|
||||
this.tcpipPortLabel.AutoSize = true;
|
||||
this.tcpipPortLabel.Location = new System.Drawing.Point(46, 109);
|
||||
this.tcpipPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.tcpipPortLabel.Name = "tcpipPortLabel";
|
||||
this.tcpipPortLabel.Size = new System.Drawing.Size(68, 20);
|
||||
this.tcpipPortLabel.TabIndex = 4;
|
||||
this.tcpipPortLabel.Text = "Port nr..:";
|
||||
//
|
||||
// tcpipPortTextBox
|
||||
//
|
||||
this.tcpipPortTextBox.Enabled = false;
|
||||
this.tcpipPortTextBox.Location = new System.Drawing.Point(161, 105);
|
||||
this.tcpipPortTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.tcpipPortTextBox.Name = "tcpipPortTextBox";
|
||||
this.tcpipPortTextBox.Size = new System.Drawing.Size(57, 26);
|
||||
this.tcpipPortTextBox.TabIndex = 5;
|
||||
//
|
||||
// ipAddressLabel
|
||||
//
|
||||
this.ipAddressLabel.AutoSize = true;
|
||||
this.ipAddressLabel.Location = new System.Drawing.Point(46, 74);
|
||||
this.ipAddressLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.ipAddressLabel.Name = "ipAddressLabel";
|
||||
this.ipAddressLabel.Size = new System.Drawing.Size(93, 20);
|
||||
this.ipAddressLabel.TabIndex = 2;
|
||||
this.ipAddressLabel.Text = "IP address.:";
|
||||
//
|
||||
// ipAddressTextBox
|
||||
//
|
||||
this.ipAddressTextBox.Enabled = false;
|
||||
this.ipAddressTextBox.Location = new System.Drawing.Point(161, 69);
|
||||
this.ipAddressTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.ipAddressTextBox.Name = "ipAddressTextBox";
|
||||
this.ipAddressTextBox.Size = new System.Drawing.Size(145, 26);
|
||||
this.ipAddressTextBox.TabIndex = 3;
|
||||
//
|
||||
// radioButton1
|
||||
//
|
||||
this.radioButton1.AutoSize = true;
|
||||
this.radioButton1.Checked = true;
|
||||
this.radioButton1.Enabled = false;
|
||||
this.radioButton1.Location = new System.Drawing.Point(33, 29);
|
||||
this.radioButton1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.radioButton1.Name = "radioButton1";
|
||||
this.radioButton1.Size = new System.Drawing.Size(109, 24);
|
||||
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(351, 29);
|
||||
this.radioButton2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.radioButton2.Name = "radioButton2";
|
||||
this.radioButton2.Size = new System.Drawing.Size(129, 24);
|
||||
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(361, 69);
|
||||
this.optoSerialPortLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.optoSerialPortLabel.Name = "optoSerialPortLabel";
|
||||
this.optoSerialPortLabel.Size = new System.Drawing.Size(107, 20);
|
||||
this.optoSerialPortLabel.TabIndex = 6;
|
||||
this.optoSerialPortLabel.Text = "Serial port nr.:";
|
||||
//
|
||||
// optoSerialPortTextBox
|
||||
//
|
||||
this.optoSerialPortTextBox.Enabled = false;
|
||||
this.optoSerialPortTextBox.Location = new System.Drawing.Point(494, 65);
|
||||
this.optoSerialPortTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.optoSerialPortTextBox.Name = "optoSerialPortTextBox";
|
||||
this.optoSerialPortTextBox.Size = new System.Drawing.Size(49, 26);
|
||||
this.optoSerialPortTextBox.TabIndex = 7;
|
||||
//
|
||||
// groupTextBox
|
||||
//
|
||||
this.groupTextBox.Enabled = false;
|
||||
this.groupTextBox.Location = new System.Drawing.Point(172, 121);
|
||||
this.groupTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.groupTextBox.Name = "groupTextBox";
|
||||
this.groupTextBox.Size = new System.Drawing.Size(49, 26);
|
||||
this.groupTextBox.TabIndex = 22;
|
||||
//
|
||||
// groupLabel
|
||||
//
|
||||
this.groupLabel.AutoSize = true;
|
||||
this.groupLabel.Location = new System.Drawing.Point(7, 126);
|
||||
this.groupLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.groupLabel.Name = "groupLabel";
|
||||
this.groupLabel.Size = new System.Drawing.Size(67, 20);
|
||||
this.groupLabel.TabIndex = 21;
|
||||
this.groupLabel.Text = "Group 2";
|
||||
//
|
||||
// muxBoardNrTextBox
|
||||
//
|
||||
this.muxBoardNrTextBox.Enabled = false;
|
||||
this.muxBoardNrTextBox.Location = new System.Drawing.Point(172, 86);
|
||||
this.muxBoardNrTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.muxBoardNrTextBox.Name = "muxBoardNrTextBox";
|
||||
this.muxBoardNrTextBox.Size = new System.Drawing.Size(49, 26);
|
||||
this.muxBoardNrTextBox.TabIndex = 20;
|
||||
//
|
||||
// muxBoardNrLabel
|
||||
//
|
||||
this.muxBoardNrLabel.AutoSize = true;
|
||||
this.muxBoardNrLabel.Location = new System.Drawing.Point(7, 90);
|
||||
this.muxBoardNrLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.muxBoardNrLabel.Name = "muxBoardNrLabel";
|
||||
this.muxBoardNrLabel.Size = new System.Drawing.Size(159, 20);
|
||||
this.muxBoardNrLabel.TabIndex = 19;
|
||||
this.muxBoardNrLabel.Text = "Group 1 (mux. board)";
|
||||
//
|
||||
// nameTextBox
|
||||
//
|
||||
this.nameTextBox.Enabled = false;
|
||||
this.nameTextBox.Location = new System.Drawing.Point(172, 50);
|
||||
this.nameTextBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.nameTextBox.Name = "nameTextBox";
|
||||
this.nameTextBox.Size = new System.Drawing.Size(180, 26);
|
||||
this.nameTextBox.TabIndex = 17;
|
||||
//
|
||||
// nameLabel
|
||||
//
|
||||
this.nameLabel.AutoSize = true;
|
||||
this.nameLabel.Location = new System.Drawing.Point(7, 55);
|
||||
this.nameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.nameLabel.Name = "nameLabel";
|
||||
this.nameLabel.Size = new System.Drawing.Size(51, 20);
|
||||
this.nameLabel.TabIndex = 16;
|
||||
this.nameLabel.Text = "Name";
|
||||
//
|
||||
// classNameLabel
|
||||
//
|
||||
this.classNameLabel.AutoSize = true;
|
||||
this.classNameLabel.Location = new System.Drawing.Point(168, 14);
|
||||
this.classNameLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
|
||||
this.classNameLabel.Name = "classNameLabel";
|
||||
this.classNameLabel.Size = new System.Drawing.Size(90, 20);
|
||||
this.classNameLabel.TabIndex = 15;
|
||||
this.classNameLabel.Text = "ClassName";
|
||||
//
|
||||
// tabPage2
|
||||
//
|
||||
this.tabPage2.Location = new System.Drawing.Point(4, 29);
|
||||
this.tabPage2.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tabPage2.Name = "tabPage2";
|
||||
this.tabPage2.Padding = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.tabPage2.Size = new System.Drawing.Size(679, 507);
|
||||
this.tabPage2.TabIndex = 1;
|
||||
this.tabPage2.Text = "Test";
|
||||
this.tabPage2.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// GenesisCfgCtrl
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.Controls.Add(this.tabControl1);
|
||||
this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
|
||||
this.Name = "GenesisCfgCtrl";
|
||||
this.Size = new System.Drawing.Size(694, 548);
|
||||
this.Load += new System.EventHandler(this.WaterMeterCfgCtrl_Load);
|
||||
this.tabControl1.ResumeLayout(false);
|
||||
this.tabPage1.ResumeLayout(false);
|
||||
this.tabPage1.PerformLayout();
|
||||
this.groupBox2.ResumeLayout(false);
|
||||
this.groupBox2.PerformLayout();
|
||||
this.groupBox1.ResumeLayout(false);
|
||||
this.groupBox1.PerformLayout();
|
||||
this.optoDataGroupBox.ResumeLayout(false);
|
||||
this.optoDataGroupBox.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
}
|
||||
|
||||
private System.Windows.Forms.CheckBox checkBox_EnableShowChanels;
|
||||
|
||||
private System.Windows.Forms.Label labelFlush;
|
||||
private System.Windows.Forms.TextBox tBBeginDataFlush;
|
||||
|
||||
#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 0–9 -> 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,391 @@
|
||||
///
|
||||
/// 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;
|
||||
using Xylem.Common.Metrology.Measurements;
|
||||
|
||||
|
||||
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;
|
||||
|
||||
public MeasurementRecord data;
|
||||
|
||||
///
|
||||
/// 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()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <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));
|
||||
|
||||
this.data = data;
|
||||
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, 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));
|
||||
this.data = data;
|
||||
UpdateData( data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS, 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 OverflowVolumeCm,
|
||||
double timeS,
|
||||
double OverflowTimeS,
|
||||
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;
|
||||
double nV = NormalizeByOverflow(volumeCm, OverflowVolumeCm);
|
||||
VolumeRaw = nV * 1000.0; // liters
|
||||
CheckSum = 0;
|
||||
Impedance = 0;
|
||||
EmfRaw = 0;
|
||||
MagneticFieldRaw = 0;
|
||||
|
||||
double ts = NormalizeByOverflow(timeS, OverflowTimeS);
|
||||
Timestamp = ts;
|
||||
|
||||
VolumeRawExt = UnwrapVolume(VolumeRaw,OverflowVolumeCm * 1000, ref volumeRawExtLast);
|
||||
TimestampExt = UnwrapTimestamp(ts,OverflowTimeS, ref timestampExtLast);
|
||||
}
|
||||
|
||||
private static double NormalizeByOverflow(double value, double overfValue = 0)
|
||||
{
|
||||
double nValue = value % overfValue;
|
||||
if (nValue < 0)
|
||||
nValue += overfValue;
|
||||
|
||||
return nValue;
|
||||
}
|
||||
|
||||
private static double UnwrapVolume(double currentVolume, double overfValue, ref double volumeRawExtLast)
|
||||
{
|
||||
double result;
|
||||
|
||||
if (double.IsNaN(volumeRawExtLast))
|
||||
{
|
||||
result = currentVolume;
|
||||
}
|
||||
else
|
||||
{
|
||||
double lastMod = volumeRawExtLast % overfValue;
|
||||
if (lastMod < 0)
|
||||
lastMod += overfValue;
|
||||
|
||||
double delta = currentVolume - lastMod;
|
||||
|
||||
if (delta < -overfValue / 2.0)
|
||||
delta += overfValue;
|
||||
else if (delta > overfValue / 2.0)
|
||||
delta -= overfValue;
|
||||
|
||||
result = volumeRawExtLast + delta;
|
||||
}
|
||||
|
||||
volumeRawExtLast = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
private static double UnwrapTimestamp(double currentTimestamp,double overfValue, ref double timestampExtLast)
|
||||
{
|
||||
double result;
|
||||
|
||||
if (double.IsNaN(timestampExtLast))
|
||||
{
|
||||
result = currentTimestamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
double lastMod = timestampExtLast % overfValue;
|
||||
if (lastMod < 0)
|
||||
lastMod += overfValue;
|
||||
|
||||
double delta = currentTimestamp - lastMod;
|
||||
|
||||
if (delta < -overfValue / 2.0)
|
||||
delta += overfValue;
|
||||
else if (delta > overfValue / 2.0)
|
||||
delta -= overfValue;
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public void Copy(OptoTelegramRaw optoTelegramRaw)
|
||||
{
|
||||
this.Flags = optoTelegramRaw.Flags;
|
||||
this.DateTime = optoTelegramRaw.DateTime;
|
||||
this.RefFlow = optoTelegramRaw.RefFlow;
|
||||
this.Counter = optoTelegramRaw.Counter;
|
||||
this.EmfRaw = optoTelegramRaw.EmfRaw;
|
||||
this.MagneticFieldRaw = optoTelegramRaw.MagneticFieldRaw;
|
||||
this.FlowRaw = optoTelegramRaw.FlowRaw;
|
||||
this.VolumeRaw = optoTelegramRaw.VolumeRaw;
|
||||
this.VolumeRawExt = optoTelegramRaw.VolumeRawExt;
|
||||
this.Impedance = optoTelegramRaw.Impedance;
|
||||
this.Timestamp = optoTelegramRaw.Timestamp;
|
||||
this.TimestampExt = optoTelegramRaw.TimestampExt;
|
||||
this.CheckSum = optoTelegramRaw.CheckSum;
|
||||
this.iChannel = optoTelegramRaw.iChannel;
|
||||
}
|
||||
|
||||
public string rawDataToString()
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
return string.Format("Channel: {0}, VolumeCm:{1}, OverflowVolumeCm:{2}, TimeS: {3}, OverflowTimeS:{4} ",
|
||||
data.Channel, data.VolumeCm, data.OverflowVolumeCm, data.TimeS, data.OverflowTimeS);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
+23
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+114
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+32
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -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; }
|
||||
}
|
||||
}
|
||||
+29
@@ -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;
|
||||
}
|
||||
}
|
||||
+27
@@ -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; }
|
||||
}
|
||||
}
|
||||
+88
@@ -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;
|
||||
}
|
||||
}
|
||||
+24
@@ -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;
|
||||
}
|
||||
}
|
||||
+86
@@ -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; }
|
||||
|
||||
}
|
||||
}
|
||||
+114
@@ -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;
|
||||
}
|
||||
}
|
||||
+111
@@ -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; }
|
||||
}
|
||||
}
|
||||
+96
@@ -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; }
|
||||
|
||||
}
|
||||
}
|
||||
+46
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -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();
|
||||
}
|
||||
}
|
||||
+33
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+63
@@ -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();
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Collection of port settings
|
||||
/// </summary>
|
||||
public struct PortConfig
|
||||
{
|
||||
private String _portName;
|
||||
/// <summary>
|
||||
/// Assigns a port name and creates the serial port object
|
||||
/// Port name e.g. "COM2"
|
||||
/// </summary>
|
||||
public String PortName
|
||||
{
|
||||
get =>
|
||||
//if the serial port object does not exists
|
||||
GetSerialPort() == null ? "NA" : _portName;
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
_portName = value;
|
||||
//create a serial port object
|
||||
if (GetSerialPort() == null)
|
||||
{
|
||||
SetSerialPort(new SerialPort(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSerialPort().PortName = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// setup of port type from name (referenced as "Type" in config file)
|
||||
/// </summary>
|
||||
public String Type { get; set; }
|
||||
|
||||
|
||||
private SerialPort _serialPort;
|
||||
|
||||
/// <summary>
|
||||
/// using dotNets <see cref="GetSerialPort()"/> for connection
|
||||
/// </summary>
|
||||
public SerialPort GetSerialPort()
|
||||
{
|
||||
return _serialPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// using dotNets <see cref="GetSerialPort()"/> for connection
|
||||
/// </summary>
|
||||
private void SetSerialPort(SerialPort value)
|
||||
{
|
||||
_serialPort = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Container for port configuration
|
||||
/// </summary>
|
||||
public class SlotConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Slot number
|
||||
/// </summary>
|
||||
public Int32 Slot { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Request port settings
|
||||
/// </summary>
|
||||
public PortConfig Request { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Streaming port settings
|
||||
/// </summary>
|
||||
public PortConfig Streaming { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Streaming port settings
|
||||
/// </summary>
|
||||
public SlotType Type { get; set; }
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Definition of type for slot
|
||||
/// </summary>
|
||||
public enum SlotType
|
||||
{
|
||||
/// <summary>
|
||||
/// Cordonel used as DUT with two serial ports
|
||||
/// </summary>
|
||||
DutMeter,
|
||||
/// <summary>
|
||||
/// Cordonel used as temperature meter with one serial port streaming the temperature
|
||||
/// </summary>
|
||||
TemperatureMeter,
|
||||
}
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore
|
||||
{
|
||||
/// <summary>
|
||||
/// Store Port settings set most likely from transmit protocol
|
||||
/// </summary>
|
||||
public struct TransmitPortSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The transmission protocol needs to inform the communication port how to set up,
|
||||
/// this is the data container.
|
||||
/// </summary>
|
||||
/// <param name="protSyncByte"></param>
|
||||
/// <param name="protLengthIndex"></param>
|
||||
/// <param name="protAddLength"></param>
|
||||
/// <param name="responseTimeoutMs"></param>
|
||||
/// <param name="baudRate"></param>
|
||||
/// <param name="receiveBufferFlushThreshold"></param>
|
||||
/// <param name="doubleSyncByte"></param>
|
||||
/// <param name="stringDelimiter"></param>
|
||||
/// <param name="dataBits"></param>
|
||||
/// <param name="parity"></param>
|
||||
/// <remarks date="2026-Jan-05" author="T.Wiedebusch">
|
||||
/// - String delimiter for ASCII to support others than LF "\n",
|
||||
/// - Flexible DataBits,
|
||||
/// - Flexible parity.
|
||||
/// </remarks>
|
||||
public TransmitPortSettings(Byte? protSyncByte, UInt16? protLengthIndex, UInt16 protAddLength, Int32 responseTimeoutMs,
|
||||
UInt32 baudRate, UInt32? receiveBufferFlushThreshold, Boolean doubleSyncByte = false, Char stringDelimiter = '\n',
|
||||
Int32 dataBits = 8, Parity parity = Parity.None )
|
||||
{
|
||||
ProtSyncByte = protSyncByte;
|
||||
ProtLengthIndex = protLengthIndex;
|
||||
ProtAddLength = protAddLength;
|
||||
ResponseTimeoutMs = responseTimeoutMs;
|
||||
BaudRate = baudRate;
|
||||
ReceiveBufferFlushThreshold = receiveBufferFlushThreshold;
|
||||
DoubleSyncByte = doubleSyncByte;
|
||||
StringDelimiter = stringDelimiter;
|
||||
DataBits = dataBits;
|
||||
Parity = parity;
|
||||
}
|
||||
/// <summary>
|
||||
/// String delimiter for ASCII to support others than LF "\n"
|
||||
/// </summary>
|
||||
public readonly Char StringDelimiter;
|
||||
|
||||
/// <summary>
|
||||
/// Flexible DataBits to support 7 bits.
|
||||
/// </summary>
|
||||
public readonly Int32 DataBits;
|
||||
|
||||
/// <summary>
|
||||
/// Flexible parity
|
||||
/// </summary>
|
||||
public readonly Parity Parity;
|
||||
|
||||
/// <summary>
|
||||
/// start of receiving synchronization byte at byte receive routine
|
||||
/// syncByte == null: use the readLine routine (ASCII) and NOT the BYTE routine,
|
||||
/// lengthPosition and additionalLength are not used
|
||||
/// </summary>
|
||||
public readonly Byte? ProtSyncByte;
|
||||
|
||||
/// <summary>
|
||||
/// position of length information field in received BYTE record
|
||||
/// lengthIndex == null: take the constant receive length of additionalLength
|
||||
/// because the record doesn't contain length information
|
||||
/// </summary>
|
||||
public readonly UInt16? ProtLengthIndex;
|
||||
|
||||
/// <summary>
|
||||
/// additional record length NOT covert by the record length information field
|
||||
/// lengthIndex == null: constant length for received record
|
||||
/// Being used for the BYTE records indicated by a valid syncByte,
|
||||
/// not being used for ASCII records.
|
||||
/// </summary>
|
||||
public readonly UInt16 ProtAddLength;
|
||||
|
||||
/// <summary>
|
||||
/// Response time out in milliseconds
|
||||
/// </summary>
|
||||
public readonly Int32 ResponseTimeoutMs;
|
||||
|
||||
/// <summary>
|
||||
/// BaudRate for Port
|
||||
/// </summary>
|
||||
public readonly UInt32 BaudRate;
|
||||
|
||||
/// <summary>
|
||||
/// lower threshold for buffer flushing if dataMarker != SkipDecoding
|
||||
/// receiveBufferFlushThreshold == null: never flush the communication buffer
|
||||
/// receiveBufferFlushThreshold == 0: flush always the communication buffer
|
||||
/// receiveBufferFlushThreshold == x: flush communication buffer if it exceeds x Byte
|
||||
/// waste old records if an up-to-date record is being needed for start/stop synchronization
|
||||
/// of a measurement. This is the level at which the old data have to be flushed because the
|
||||
/// data have been dammed up in the communication port input buffer which means they are to
|
||||
/// old for synchronization purposes.
|
||||
/// /// </summary>
|
||||
public readonly UInt32? ReceiveBufferFlushThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// a special implementation may require the doubling of the sync byte to re-synchronize
|
||||
/// </summary>
|
||||
public readonly Boolean DoubleSyncByte;
|
||||
}
|
||||
}
|
||||
+702
@@ -0,0 +1,702 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
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.SerialPorts
|
||||
{
|
||||
/// <summary>
|
||||
/// All Ports must use BasePort as an base class
|
||||
/// its support some wrapping event/function handling
|
||||
/// </summary>
|
||||
public abstract class BaseSerialPort : IPort, IDisposable
|
||||
{
|
||||
private static readonly ILog _byteDataLogger = LogManager.GetLogger(typeof(BaseSerialPort));
|
||||
internal ILog AsciiDataLogger = LogManager.GetLogger("LedRawData");
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual event EventHandler<BasePortDataEventArgs> OnRawRecordReceived;
|
||||
|
||||
/// <summary>
|
||||
/// The port is not assigned.
|
||||
/// </summary>
|
||||
public const String PortNotAssigned = "NA";
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual event EventHandler<BasePortDataEventArgs> OnRawRecordSendOut;
|
||||
|
||||
//initially do not signal event
|
||||
private readonly AutoResetEvent _onSyncReceiveThread = new AutoResetEvent(false);
|
||||
private readonly Thread _receiveThread;
|
||||
private readonly CancellationTokenSource _receiveToken = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// Delay between bytes if receiving has started in milliseconds
|
||||
/// </summary>
|
||||
private const Int32 InterByteReadDelayMs = 50;
|
||||
|
||||
/// <summary>
|
||||
/// internal SerialPort class
|
||||
/// </summary>
|
||||
private readonly SerialPort _serialPort;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Activate raw record recording
|
||||
/// </summary>
|
||||
public Boolean RecordStreamingRawData = false;
|
||||
//public Boolean RecordStreamingRawData
|
||||
//{
|
||||
// get => _recordStreamingRawData;
|
||||
// set => _recordStreamingRawData = value;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// date and time of incoming first date interrupt on serial IO to set the
|
||||
/// PC time-stamp to the record
|
||||
/// </summary>
|
||||
private DateTimeOffset _receiveTimeStampPc;
|
||||
|
||||
/// <summary>
|
||||
/// Individual port settings depending on transmit protocol
|
||||
/// </summary>
|
||||
protected TransmitPortSettings PortSettingsForTransmitProtocol;
|
||||
|
||||
/// <inheritdoc />
|
||||
public String GetPortName()
|
||||
{
|
||||
return _serialPort.PortName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// store a identification of a port, this contains the Slot, Port, Protocol and Type
|
||||
/// e.g. "Slot:1, Port:COM4, Protocol:Request, Type:IrDA -"
|
||||
/// </summary>
|
||||
public String Ident;
|
||||
|
||||
/// <summary>
|
||||
/// ctor for SerialComPort with and full constructed <see cref="T:System.IO.Ports.SerialPort" /> properties
|
||||
/// </summary>
|
||||
/// <param name="ident">set port as unique</param>
|
||||
/// <param name="serialPort">Class for communication, all parameters for serial communication needs to be set</param>
|
||||
/// <param name="setting">Class for store all parameters for serial communication needs to be set came from transmit protocol</param>
|
||||
protected BaseSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting)
|
||||
{
|
||||
Ident = ident;
|
||||
|
||||
//communication port settings
|
||||
_serialPort = serialPort;
|
||||
_serialPort.BaudRate = (Int32)setting.BaudRate;
|
||||
|
||||
PortSettingsForTransmitProtocol = setting;
|
||||
|
||||
//assign thread to loop
|
||||
_receiveThread = new Thread(ReadingThreadLoop) { Name = $"{Ident} Reading thread" };
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void RefreshLogger()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// received bytes in buffer of SerialComPort, calls base function
|
||||
/// </summary>
|
||||
/// <returns>number of bytes in Rx buffer</returns>
|
||||
protected Int32 BytesToRead()
|
||||
{
|
||||
try
|
||||
{
|
||||
return _serialPort.BytesToRead;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error(ex);
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// read single byte from Rx buffer of SerialComPort, calls base function
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected Int32 ReadByte()
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
return _serialPort.ReadByte();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error(ex);
|
||||
return 0;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PortWrite(Byte[] record)
|
||||
{
|
||||
try
|
||||
{
|
||||
SpecificPortWrite(record);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new SystemException($"{Ident} Communication error while sending data to port", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// flush Rx and Tx buffer of SerialComPort, calls base functions
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_serialPort.IsOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_serialPort.DiscardInBuffer();
|
||||
_serialPort.DiscardOutBuffer();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error(ex);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose serial port
|
||||
/// </summary>
|
||||
/// <remarks date="2025-Mai-12..14" author="T.Wiedebusch">
|
||||
/// - Dispose procedure changed.
|
||||
/// </remarks>
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
//remove registration for receive delegate
|
||||
_serialPort.DataReceived -= PhysicalDataReceived;
|
||||
|
||||
//Cancel send and receive tokens
|
||||
_receiveToken.Cancel();
|
||||
|
||||
//Run thread again to notice CancellationToken has changed
|
||||
_onSyncReceiveThread.Set();
|
||||
|
||||
// dispose directly called from here to overcome glitches caused by USB to serial interface
|
||||
_serialPort.Dispose();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Open()
|
||||
{
|
||||
try
|
||||
{
|
||||
//set receive interrupt threshold for immediate execution
|
||||
_serialPort.ReceivedBytesThreshold = 1;
|
||||
_serialPort.DataReceived += PhysicalDataReceived;
|
||||
|
||||
//start reading thread, will be immediately put to waitSleepJoin in ReadingThreadLoop
|
||||
//to avoid side effects with RFID communication
|
||||
|
||||
ThreadWatcher.Instance.Start(_receiveThread);
|
||||
|
||||
if (!_serialPort.IsOpen)
|
||||
{
|
||||
_serialPort.Open();
|
||||
if (!_serialPort.IsOpen)
|
||||
{
|
||||
_byteDataLogger.Error($"{Ident} Port cannot be opened");
|
||||
throw new ApplicationException($"{Ident} Port cannot be opened");
|
||||
}
|
||||
_byteDataLogger.Info($"{Ident} Port is opened");
|
||||
}
|
||||
else
|
||||
{
|
||||
_byteDataLogger.Warn($"{Ident} Port was already opened");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
||||
throw new ApplicationException($"{Ident} {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Getting the base port
|
||||
/// </summary>
|
||||
protected SerialPort GetBaseComport()
|
||||
{
|
||||
return _serialPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill FIFO with received data
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Feb-16" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Feb-21" author="T.Wiedebusch">
|
||||
/// - Time stamp added
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-14" author="T.Wiedebusch">
|
||||
/// - Timeout moved from <see cref="ReadingThreadLoop"/> to PhysicalDataReceived to avoid
|
||||
/// <see cref="TimeoutException"/> of serial port while waiting on first incoming
|
||||
/// data.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-12" author="T.Wiedebusch">
|
||||
/// - Inter byte read delay used instead of response delay!!!
|
||||
/// </remarks>
|
||||
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
public virtual void PhysicalDataReceived(Object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
if (!_receiveToken.IsCancellationRequested)
|
||||
{
|
||||
//remove event delegate to avoid repeated execution during one record
|
||||
_serialPort.DataReceived -= PhysicalDataReceived;
|
||||
|
||||
//put the actual time stamp to this record being able to assign it correctly
|
||||
//even if the decoding is delayed. This time stamp will be used to do the first
|
||||
//synchronization at start and stop of the measurement. Therefore, the serial buffer
|
||||
//has to be flushed in advance to avoid wrong time stamp to "old" records
|
||||
_receiveTimeStampPc = DateTimeOffset.UtcNow;
|
||||
|
||||
//start timeout for hanging read communication called inter byte delay
|
||||
_serialPort.ReadTimeout = InterByteReadDelayMs;
|
||||
// _serialPort.ReadTimeout = PortSettingsForTransmitProtocol.ResponseTimeoutMs;
|
||||
|
||||
//wake up the reading threat from JoinWaitSleep
|
||||
_onSyncReceiveThread.Set();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="SyncMarkRecord" />
|
||||
/// </summary>
|
||||
private SyncMarkRecord _syncMarkRecord;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SynchronizeReceiveBuffer(SyncMarkRecord syncMarkRecord)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
//not in test bench situation only for temp logging
|
||||
if (syncMarkRecord == SyncMarkRecord.FlushBuffer)
|
||||
{
|
||||
FlushBuffer();
|
||||
return;
|
||||
}
|
||||
//DecodeEveryPackage is ongoing and SkipDecoding is requested = do nothing
|
||||
if (_syncMarkRecord == SyncMarkRecord.DecodeEveryPackage &&
|
||||
syncMarkRecord == SyncMarkRecord.SkipDecoding)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//deny intermediate record when a start or end record is requested
|
||||
if (syncMarkRecord == SyncMarkRecord.DecodeIntermediate &&
|
||||
(_syncMarkRecord == SyncMarkRecord.SyncEnd ||
|
||||
_syncMarkRecord == SyncMarkRecord.SyncStart))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//being able to detect the first incoming record after a sync is requested, the buffer is going to be flushed
|
||||
//if the measurement will be started or stopped
|
||||
if (syncMarkRecord == SyncMarkRecord.SyncEnd ||
|
||||
syncMarkRecord == SyncMarkRecord.SyncStart ||
|
||||
syncMarkRecord == SyncMarkRecord.DecodeIntermediate)
|
||||
{
|
||||
FlushBuffer();
|
||||
}
|
||||
|
||||
_byteDataLogger.Info($"{Ident} Mark next incoming record as {syncMarkRecord} (was {_syncMarkRecord})");
|
||||
_syncMarkRecord = syncMarkRecord;
|
||||
|
||||
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void FlushBuffer()
|
||||
{
|
||||
//check if flushing is allowed
|
||||
if (null != PortSettingsForTransmitProtocol.ReceiveBufferFlushThreshold)
|
||||
{
|
||||
//flush buffer above threshold to get an accurate actual record with next record coming in
|
||||
if (_serialPort.IsOpen && _serialPort.BytesToRead > PortSettingsForTransmitProtocol.ReceiveBufferFlushThreshold)
|
||||
{
|
||||
_byteDataLogger.Warn($"{Ident} Flushed receive buffer({_serialPort.BytesToRead}Byte)");
|
||||
_serialPort.DiscardInBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reading thread loop getting data from serial port
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Feb-16" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Mar-08" author="T.Wiedebusch">
|
||||
/// - Reading will be executed and repeated until buffer is empty or timeout
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Mar-09" author="T.Wiedebusch">
|
||||
/// - Receive byte protocol more dynamically on position of length information in record
|
||||
/// and additional length,
|
||||
/// - decoding of data controlled by dataSyncMarker!= SyncMarkRecord.SkipDecoding to speed up recording
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Oct-23" author="T.Wiedebusch">
|
||||
/// - Directly invoked onRawRecord Received event,
|
||||
/// - Doubling of sync byte implemented
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-14" author="T.Wiedebusch">
|
||||
/// - Timeout moved from ReadingThreadLoop to <see cref="PhysicalDataReceived"/> to avoid
|
||||
/// <see cref="TimeoutException"/> of serial port while waiting on first incoming
|
||||
/// data and flush receive buffer at timeout to force task to enter JoinWaitSleep state.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-03" author="T.Wiedebusch">
|
||||
/// - Raw record recording for missing sync-byte issues.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-04" author="T.Wiedebusch">
|
||||
/// - Try to recover raw record on missing SYNC byte by adding the SYNC upfront and sending
|
||||
/// the record to the decoding thread which will detect if just the SYNC byte had been
|
||||
/// missed, then the CRC will match and the record can be decoded.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-05" author="T.Wiedebusch">
|
||||
/// - Improved recovering of data sets at byte records with preceding SYNC byte:
|
||||
/// - used payload length == 0 to skip decoding, it makes no sense to decode something where
|
||||
/// the payload is empty,
|
||||
/// - wait a certain time to complete incoming data.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-12" author="T.Wiedebusch">
|
||||
/// - Inter byte read delay used instead of response delay.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-14" author="T.Wiedebusch">
|
||||
/// - Flush buffer if received length is 0.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Jul-15" author="T.Wiedebusch">
|
||||
/// - Flush buffer (DiscardInBuffer) removed as some bytes are missing from time to time.
|
||||
/// The IrDA sniffer showed these on the communication line, but they are incompletely received.
|
||||
/// Assuming the DiscardInBuffer may be delayed, so the incoming data will be scrapped.
|
||||
/// </remarks>
|
||||
/// <remarks date="2025-Mai-12..14" author="T.Wiedebusch">
|
||||
/// - Dispose procedure changed.
|
||||
/// </remarks>
|
||||
/// <remarks date="2026-Jan-06" author="T.Wiedebusch">
|
||||
/// - Replaced 'ReadLine' with 'ReadTo' using a string delimiter to support others than LF "\n".
|
||||
/// </remarks>
|
||||
private void ReadingThreadLoop()
|
||||
{
|
||||
//put the receive thread to JoinWaitSleep to avoid reading and logger output for timeout on
|
||||
//RFID communication, because RFID will handle the physical receive by itself
|
||||
//here the assignment of the _synReadingThreadEvent to the reading thread is being done
|
||||
_onSyncReceiveThread.WaitOne();
|
||||
|
||||
try
|
||||
{
|
||||
while (!_receiveToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
//remind data marker for this Thread execution time slice
|
||||
var dataSyncMarkThisRun = _syncMarkRecord;
|
||||
|
||||
//read line of ASCII indicated by syncByte == null
|
||||
if (null == PortSettingsForTransmitProtocol.ProtSyncByte)
|
||||
{
|
||||
//exit is timeout from serialPort or received line
|
||||
var rxStringRecord = _serialPort.ReadLine();
|
||||
|
||||
if (!string.IsNullOrEmpty(rxStringRecord))
|
||||
{
|
||||
//decode optional at synchronized data SyncStart, SyncEnd or DecodeIntermediate
|
||||
//todo remove after air problem
|
||||
if (SyncMarkRecord.SkipDecoding != dataSyncMarkThisRun)
|
||||
{
|
||||
OnRawRecordReceived?.Invoke(this, new StringPortDataEventArgs(rxStringRecord,
|
||||
_receiveTimeStampPc, dataSyncMarkThisRun));
|
||||
}
|
||||
|
||||
if (RecordStreamingRawData)
|
||||
{
|
||||
AsciiDataLogger.Debug($"{rxStringRecord}");
|
||||
}
|
||||
}
|
||||
}
|
||||
//byte record with preceding SYNC byte
|
||||
else
|
||||
{
|
||||
//normally decoding is required
|
||||
dataSyncMarkThisRun = SyncMarkRecord.DecodeEveryPackage;
|
||||
|
||||
//this is the record for decoding
|
||||
var rxByteRecord = new List<Byte>();
|
||||
|
||||
//this is the informational record for logging on undetected SYNC byte
|
||||
var rxRawRecord = new List<Byte>();
|
||||
|
||||
//each byte protocol has to start with a syncByte, this has to be detected first
|
||||
Byte rxByte;
|
||||
do
|
||||
{
|
||||
rxByte = (Byte)_serialPort.ReadByte();
|
||||
//record raw data stream for output on missing sync-byte to investigate this issue
|
||||
rxRawRecord.Add(rxByte);
|
||||
} while (_serialPort.BytesToRead > 0 && PortSettingsForTransmitProtocol.ProtSyncByte != rxByte);
|
||||
|
||||
// if syncByte has not been detected skip read loop and wait for next incoming record
|
||||
if (PortSettingsForTransmitProtocol.ProtSyncByte == rxByte)
|
||||
{
|
||||
//save received SYNC byte
|
||||
rxByteRecord.Add(rxByte);
|
||||
|
||||
//to assemble the length it has to be extracted first from the record at given index,
|
||||
//the payload length index cannot be 0 because at this position is always the SYNC byte,
|
||||
//the overall length includes the SYNC byte!
|
||||
var length = PortSettingsForTransmitProtocol.ProtLengthIndex + 1;
|
||||
|
||||
//if the protLengthIndex is null the protAddLength equals the entire record length
|
||||
if (null == PortSettingsForTransmitProtocol.ProtLengthIndex)
|
||||
{
|
||||
length = PortSettingsForTransmitProtocol.ProtAddLength;
|
||||
}
|
||||
|
||||
//read until length index to extract the length from the record
|
||||
//the length index cannot be at position 0, because this is always the syncByte
|
||||
do
|
||||
{
|
||||
rxByte = (Byte)_serialPort.ReadByte();
|
||||
//read the next byte if doubled sync byte detected and required by transmit protocol settings
|
||||
if (PortSettingsForTransmitProtocol.DoubleSyncByte)
|
||||
{
|
||||
if (rxByte.Equals(PortSettingsForTransmitProtocol.ProtSyncByte) &&
|
||||
rxByteRecord[rxByteRecord.Count - 1]
|
||||
.Equals(PortSettingsForTransmitProtocol.ProtSyncByte))
|
||||
{
|
||||
//skips this byte and read the next one
|
||||
rxByte = (Byte)_serialPort.ReadByte();
|
||||
}
|
||||
}
|
||||
|
||||
//add byte to receive result buffer
|
||||
rxByteRecord.Add(rxByte);
|
||||
|
||||
//capture the length at given index and readjust the record length
|
||||
if (null != PortSettingsForTransmitProtocol.ProtLengthIndex
|
||||
&& rxByteRecord.Count - 1 == PortSettingsForTransmitProtocol.ProtLengthIndex)
|
||||
{
|
||||
//if the received length indicates empty payload, then nothing is to decode
|
||||
if (rxByte == 0)
|
||||
{
|
||||
//exit this loop
|
||||
dataSyncMarkThisRun = SyncMarkRecord.SkipDecoding;
|
||||
FlushBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
//build the new length to continue this receive loop
|
||||
length = (UInt16)(PortSettingsForTransmitProtocol.ProtAddLength + rxByte);
|
||||
}
|
||||
}
|
||||
|
||||
//wait a certain time to let the data stream coming in
|
||||
if (length - rxByteRecord.Count - 1 > _serialPort.BytesToRead)
|
||||
{
|
||||
Thread.Sleep(2);
|
||||
}
|
||||
|
||||
} while (rxByteRecord.Count < length && dataSyncMarkThisRun != SyncMarkRecord.SkipDecoding);
|
||||
|
||||
//all data received or skip decoding marked, skip decoding will clear the response timeout
|
||||
//these protocols have always to be decoded because the base is a request protocol
|
||||
OnRawRecordReceived?.Invoke(this, new ListBytePortDataEventArgs(rxByteRecord,
|
||||
_receiveTimeStampPc, dataSyncMarkThisRun));
|
||||
}
|
||||
else
|
||||
{
|
||||
//output actual byte and recorded raw data stream to investigate missed sync-byte
|
||||
_byteDataLogger.Warn($"{Ident} Response SYNC Byte missing. Raw Byte Received: " +
|
||||
$"{BitConverter.ToString(rxRawRecord.ToArray())}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
_byteDataLogger.Info($"{Ident} Thread abort exception fired!");
|
||||
}
|
||||
catch (TimeoutException)
|
||||
{
|
||||
//ATTENTION: This "_serialPort.DiscardInBuffer()" caused a lot of trouble as it flushes a few incoming
|
||||
//bytes and therefore destroys the already started data stream. Here it had been left in to indicate
|
||||
//this critical issue!
|
||||
/*-------------------------------DO NOT ACTIVATE-----------------------------------------------------*/
|
||||
//flush receive buffer at timeout to force task to enter JoinWaitSleep state in finally
|
||||
//if (_serialPort.IsOpen)
|
||||
//{
|
||||
// _serialPort.DiscardInBuffer();
|
||||
//}
|
||||
/*---------------------------------------------------------------------------------------------------*/
|
||||
|
||||
_byteDataLogger.Debug($"{Ident} Read timeout({InterByteReadDelayMs}ms)");
|
||||
//_byteDataLogger.Trace($"{Ident} Read timeout({PortSettingsForTransmitProtocol.ResponseTimeoutMs}ms)");
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (_receiveThread.ThreadState == ThreadState.Aborted
|
||||
|| _receiveThread.ThreadState == ThreadState.AbortRequested)
|
||||
{
|
||||
_byteDataLogger.Debug( $"{Ident} ThreadState is Aborted or AbortRequested but an " +
|
||||
"error occurred while reading records from serial port", ex);
|
||||
}
|
||||
else
|
||||
{
|
||||
_byteDataLogger.Error( $"{Ident} Error while reading records from serial port", ex);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_serialPort.IsOpen && !_receiveToken.IsCancellationRequested)
|
||||
{
|
||||
//it makes no sense to put the thread to sleep if there is something to read
|
||||
if (0 == _serialPort.BytesToRead)
|
||||
{
|
||||
//restore event delegate to activate handle for incoming records
|
||||
_serialPort.DataReceived += PhysicalDataReceived;
|
||||
|
||||
//put this thread (receive thread) to JainWaitSleep
|
||||
_onSyncReceiveThread.WaitOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
} //while (!_tokenReadData.IsCancellationRequested)
|
||||
} // over hole reading thread loop
|
||||
catch (ThreadAbortException)
|
||||
{
|
||||
_byteDataLogger.Info($"{Ident} Thread abort exception fired!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
||||
}
|
||||
//finally
|
||||
//{
|
||||
// //var retries = 5;
|
||||
// //while (_serialPort != null && _serialPort.IsOpen && retries-- > 0)
|
||||
// //{
|
||||
// // _serialPort.Close();
|
||||
// // if (_serialPort.IsOpen)
|
||||
// // {
|
||||
// // _byteDataLogger.Info($"{Ident} Port closing delay!");
|
||||
// // Thread.Sleep(500);
|
||||
// // }
|
||||
// //}
|
||||
// //if (_serialPort != null && _serialPort.IsOpen)
|
||||
// // _byteDataLogger.Info($"{Ident} Port unable to close");
|
||||
// //else
|
||||
// // _byteDataLogger.Info($"{Ident} Port is closed");
|
||||
//}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Boolean IsOpen()
|
||||
{
|
||||
return _serialPort != null && _serialPort.IsOpen;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serial port specific write routine
|
||||
/// </summary>
|
||||
/// <param name="txBuffer"></param>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
protected virtual void SpecificPortWrite(Byte[] txBuffer)
|
||||
{
|
||||
PhysicalWrite(txBuffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Physically sending to the serial port
|
||||
/// </summary>
|
||||
/// <remarks date="2017" author="R.Drabesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Oct-23" author="T.Wiedebusch">
|
||||
/// - Doubling of sync byte in protocol behind sync byte itself implemented
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Feb-25" author="T.Wiedebusch, R.Drabesch">
|
||||
/// - Flush buffer as MOXA sometimes takes two messages and combines these to one!
|
||||
/// As the Genesis needs a separated wake-up message with a following delay before the
|
||||
/// real payload message, this causes a lot of trouble.
|
||||
/// </remarks>
|
||||
protected void PhysicalWrite(Byte[] txBuffer)
|
||||
{
|
||||
try
|
||||
{
|
||||
//don't double the sync byte itself
|
||||
var txByteList = new List<Byte> { txBuffer[0] };
|
||||
for (var byteCtr = 1; byteCtr < txBuffer.Length; byteCtr++)
|
||||
{
|
||||
txByteList.Add(txBuffer[byteCtr]);
|
||||
|
||||
//write the doubled sync byte again if required by transmit protocol settings
|
||||
if (PortSettingsForTransmitProtocol.DoubleSyncByte)
|
||||
{
|
||||
if (txBuffer[byteCtr].Equals(PortSettingsForTransmitProtocol.ProtSyncByte))
|
||||
{
|
||||
txByteList.Add(txBuffer[byteCtr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_serialPort.Write(txByteList.ToArray(), 0, txByteList.Count);
|
||||
|
||||
// flush the buffer for MOXA, to avoid two subsequent communications assembled to one communication!
|
||||
_serialPort.BaseStream.Flush();
|
||||
|
||||
OnRawRecordSendOut?.Invoke(this, new ListBytePortDataEventArgs(txByteList, DateTimeOffset.UtcNow));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_byteDataLogger.Error($"{Ident} {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Threading;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.SerialPorts
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class IrdaSerialPort : BaseSerialPort
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IrdaSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting)
|
||||
: base(ident, serialPort, setting)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2018-Mar-15" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Aug-07" author="T.Wiedebusch">
|
||||
/// - wakeup burst pattern changed from 2 times 0xF0 to 4 times 0x01,
|
||||
/// - required delay from 500µs to 1ms between wakeup burst and data.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Jun-28" author="T.Wiedebusch">
|
||||
/// - Removed comments and one delay (was at 2 x 1 ms).
|
||||
/// </remarks>
|
||||
protected override void SpecificPortWrite(Byte[] tx)
|
||||
{
|
||||
//send wake up burst
|
||||
var wakeUpBurst = new Byte[] { 0x01, 0x01, 0x01, 0x01 };
|
||||
PhysicalWrite(wakeUpBurst);
|
||||
//delay at least 500 µs
|
||||
Thread.Sleep(1);
|
||||
|
||||
//send the IrDA record
|
||||
PhysicalWrite(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.SerialPorts
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class LedSerialPort : BaseSerialPort
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public LedSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting) : base(ident, serialPort, setting)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void SpecificPortWrite(Byte[] tx)
|
||||
{
|
||||
throw new NotSupportedException("Led port does not support writing");
|
||||
}
|
||||
|
||||
public ILog GetRawLogger()
|
||||
{
|
||||
return AsciiDataLogger;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+600
@@ -0,0 +1,600 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.SerialPorts
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class RfidSerialPort : BaseSerialPort
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override event EventHandler<BasePortDataEventArgs> OnRawRecordReceived;
|
||||
|
||||
private const Byte RfidFrameStartId = 0x01;
|
||||
private const Byte RfidFrameTxLength = 0x12;
|
||||
private const Byte RfidFrameRxLength = 0x0C;
|
||||
private const Byte RfidFrameExpectedRxLength = 0x0A;
|
||||
private const Byte RfidFramePayloadDataMarker = 0x7D;
|
||||
private const Byte RfidRxModeMarker = 0x7E;
|
||||
private const Byte RfidFramePollingByte = 0x03;
|
||||
|
||||
private const Int32 RfidRxStartSyncPosition = 0;
|
||||
private const Int32 RfidRxLengthPosition = 1;
|
||||
private const Int32 RfidRxModePosition = 4;
|
||||
private const Int32 RfidRxDataPosition = 5;
|
||||
private const Int32 RfidRxDataMarkerPosition = 11;
|
||||
private const Int32 RfidRxCrcLowPosition = 12;
|
||||
private const Int32 RfidRxCrcHighPosition = 13;
|
||||
private const Int32 RfidRxBccPosition = 14;
|
||||
private const Int32 RfidCommRetries = 4;
|
||||
private const Int32 RfidStartPatternLength = 10;
|
||||
//additional frame length for start, length and BCC added to frame length
|
||||
//being in the length itself
|
||||
private const Int32 RfidProtAddLength = 3;
|
||||
private const Int32 RfidRxLength = RfidFrameRxLength + RfidProtAddLength;
|
||||
private const Int32 RfidTxLength = RfidFrameTxLength + RfidProtAddLength;
|
||||
|
||||
//first byte is fixed to 0x7D (data mode) at the Tx or the last byte behind
|
||||
//the payload at Rx, the others are from the device protocol (the payload),
|
||||
//this byte needs to be included into the CRC calculation
|
||||
private const Int32 RfidPayLoadLength = 7;
|
||||
private const Int32 RfidRawPayLoadLength = 6;
|
||||
|
||||
//the raw payload to add to RFID transmit buffer, it has a start Id, a length position
|
||||
//and some additional length needed to add to the raw Rx length
|
||||
private Byte[] _rawTxPayLoad;
|
||||
private Int32 _rawRxLength;
|
||||
private Byte[] _rawRxPayLoad;
|
||||
|
||||
|
||||
//create RFID payload
|
||||
private Byte[] _rfidTxPayLoad;
|
||||
private Int32 _payLoadTxCounterPosition;
|
||||
private Int32 _payLoadRxCounterPosition;
|
||||
//remind send state
|
||||
private Boolean _txIsActive;
|
||||
//marker for first protocol containing the start id and the length
|
||||
private Boolean _waitRawStartSyncPattern;
|
||||
//this is the RFID raw buffer being sent to the serial port
|
||||
private Byte[] _rfidTxBuffer = new Byte[RfidTxLength];
|
||||
private Byte[] _rfidRxBuffer = new Byte[RfidRxLength];
|
||||
private Int32 _rfidCommRetryCounter = RfidCommRetries;
|
||||
private RfidRxState _rfidRxState;
|
||||
|
||||
private static readonly ILog _logger = LogManager.GetLogger(typeof(RfidSerialPort));
|
||||
|
||||
//communication counter for debug for application layer
|
||||
private Int32 _commCounter;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public RfidSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting)
|
||||
: base(ident, serialPort, setting)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Not needed and therefor deactivated by overwriting
|
||||
/// </remarks>
|
||||
public override void PhysicalDataReceived(Object sender, SerialDataReceivedEventArgs e)
|
||||
{
|
||||
//remove registration of delegate
|
||||
GetBaseComport().DataReceived -= PhysicalDataReceived;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Overwritten routine, kicks off the first communication,
|
||||
/// handles the communication state machine
|
||||
/// </summary>
|
||||
/// <param name="tx"></param>
|
||||
/// <remarks date="2017-Dec-15" author="R.Drabesch">
|
||||
/// - Not needed and therefor deactivated by overwriting
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-17" author="T.Wiedebusch">
|
||||
/// - communication scheduler removed, call directly the stats machine
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
protected override void SpecificPortWrite(Byte[] tx)
|
||||
{
|
||||
//raw protocol before wrapped into RFID
|
||||
SendDataViaRfid(tx);
|
||||
|
||||
RfidRxState rfidState;
|
||||
|
||||
//call state machine until ready
|
||||
do
|
||||
{
|
||||
rfidState = RfidCommStateMachine();
|
||||
|
||||
} while (RfidRxState.CommunicationFinished != rfidState &&
|
||||
RfidRxState.CommunicationFailed != rfidState);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send data via RFID
|
||||
/// </summary>
|
||||
/// <param name="data">data to be transferred via RFID</param>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-15" author="T.Wiedebusch">
|
||||
/// - Receive size removed, will be automatically extracted from underlay-protocol (raw)
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-17" author="T.Wiedebusch">
|
||||
/// - Raw buffer increased to communication retries * raw packet size.
|
||||
/// </remarks>
|
||||
public void SendDataViaRfid(Byte[] data)
|
||||
{
|
||||
if (data.Length < RfidRawPayLoadLength) return;
|
||||
//length is unknown before receiving the first raw data, setup to x packets size for
|
||||
//receive routine
|
||||
_rawRxLength = RfidRawPayLoadLength * _rfidCommRetryCounter;
|
||||
_rawRxPayLoad = new Byte[_rawRxLength];
|
||||
_waitRawStartSyncPattern = true;
|
||||
//start with sending of data
|
||||
_txIsActive = true;
|
||||
//allow retries during communication
|
||||
_rfidCommRetryCounter = RfidCommRetries;
|
||||
//reset running counters
|
||||
_payLoadTxCounterPosition = 0;
|
||||
_payLoadRxCounterPosition = 0;
|
||||
_commCounter = 0;
|
||||
|
||||
//copy part of the huge payload buffer (containing the entire data) to small
|
||||
//6 byte chunks being able to transmit within one RFID communication
|
||||
_rfidTxPayLoad = null;
|
||||
_rfidTxPayLoad = new Byte[RfidRawPayLoadLength];
|
||||
_rawTxPayLoad = new Byte[data.Length];
|
||||
_rawTxPayLoad = data;
|
||||
for (var i = 0; i < RfidRawPayLoadLength; i++)
|
||||
{
|
||||
_rfidTxPayLoad[i] = _rawTxPayLoad[_payLoadTxCounterPosition];
|
||||
_payLoadTxCounterPosition++;
|
||||
}
|
||||
//assemble the RFID transmit buffer
|
||||
PrepareRfidTxBuffer(ref _rfidTxBuffer, _rfidTxPayLoad);
|
||||
//kick off first communication
|
||||
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
|
||||
}
|
||||
/// <summary>
|
||||
/// RFID Tx buffer content
|
||||
/// </summary>
|
||||
/// <returns>array of RFID Tx buffer content</returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Byte[] GetRfidTxBuffer()
|
||||
{
|
||||
return _rfidTxBuffer;
|
||||
}
|
||||
/// <summary>
|
||||
/// RFID Rx buffer content
|
||||
/// </summary>
|
||||
/// <returns>array of RFID Rx buffer content</returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Byte[] GetRfidRxBuffer()
|
||||
{
|
||||
return _rfidRxBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// read out the received data from RFID
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Byte[] GetDecodedDataFromRfid()
|
||||
{
|
||||
return _rawRxPayLoad;
|
||||
}
|
||||
/// <summary>
|
||||
/// feedback of communication counter
|
||||
/// </summary>
|
||||
/// <returns>number of RFID communications</returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public Int32 GetRfidComCounter()
|
||||
{
|
||||
return _commCounter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// communication state machine
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-15" author="T.Wiedebusch">
|
||||
/// - Extract length information from raw protocol
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-17" author="T.Wiedebusch">
|
||||
/// - Return of communication failed on missing start sync pattern of raw protocol
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-19" author="T.Wiedebusch">
|
||||
/// - New modes implemented to differ between sending / waiting for Rx start
|
||||
/// and receiving
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Feb-21" author="T.Wiedebusch">
|
||||
/// - Time stamp added
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-12" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
public RfidRxState RfidCommStateMachine()
|
||||
{
|
||||
//check the receive status
|
||||
switch (_rfidRxState)
|
||||
{
|
||||
case RfidRxState.EchoRxProtocolOk:
|
||||
//clear payload buffer to force filling with polling pattern
|
||||
_rfidTxPayLoad = null;
|
||||
//send loop
|
||||
if (_txIsActive)
|
||||
{
|
||||
if (_payLoadTxCounterPosition < _rawTxPayLoad.Length)
|
||||
{
|
||||
//assign new Tx buffer
|
||||
Int32 arraySize;
|
||||
if (_payLoadTxCounterPosition + RfidRawPayLoadLength <
|
||||
_rawTxPayLoad.Length)
|
||||
arraySize = RfidRawPayLoadLength;
|
||||
else
|
||||
arraySize = _rawTxPayLoad.Length - _payLoadTxCounterPosition;
|
||||
_rfidTxPayLoad = new Byte[arraySize];
|
||||
//fill payload buffer with remaining payload bytes
|
||||
for (var i = 0; i < arraySize; i++)
|
||||
{
|
||||
if (_payLoadTxCounterPosition >= _rawTxPayLoad.Length) continue;
|
||||
_rfidTxPayLoad[i] = _rawTxPayLoad[_payLoadTxCounterPosition];
|
||||
_payLoadTxCounterPosition++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//switch receive loop active
|
||||
_txIsActive = false;
|
||||
}
|
||||
}
|
||||
//reset retry counter
|
||||
_rfidCommRetryCounter = RfidCommRetries;
|
||||
//assemble new RFID transmit buffer
|
||||
PrepareRfidTxBuffer(ref _rfidTxBuffer, _rfidTxPayLoad);
|
||||
//start communication
|
||||
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
|
||||
break;
|
||||
|
||||
case RfidRxState.DataRxProtocolOk:
|
||||
//receive loop with polling pattern sending
|
||||
for (var i = 0; i < RfidRawPayLoadLength; i++)
|
||||
{
|
||||
//avoid out of bounce access
|
||||
if (_payLoadRxCounterPosition >= _rawRxLength) continue;
|
||||
_rawRxPayLoad[_payLoadRxCounterPosition] =
|
||||
_rfidRxBuffer[i + RfidRxDataPosition];
|
||||
_payLoadRxCounterPosition++;
|
||||
}
|
||||
//check for entire message received
|
||||
if (_payLoadRxCounterPosition >= _rawRxLength)
|
||||
{
|
||||
//assign time stamp of received record
|
||||
var readDateTimePc = DateTimeOffset.UtcNow;
|
||||
|
||||
//communication finished, return data to caller
|
||||
//RawRecordReceived(this, _rawRxPayLoad, readDateTimePc);
|
||||
var byteList = _rawRxPayLoad.ToList();
|
||||
OnRawRecordReceived?.Invoke(this, new ListBytePortDataEventArgs(byteList, readDateTimePc));
|
||||
|
||||
//log the RFID payload, RFID protocol is removed
|
||||
//_logger.Trace($"{Ident} Read({BitConverter.ToString(_rawRxPayLoad)})");
|
||||
|
||||
return _waitRawStartSyncPattern ? RfidRxState.CommunicationFailed :
|
||||
RfidRxState.CommunicationFinished;
|
||||
}
|
||||
//reset retry counter
|
||||
_rfidCommRetryCounter = RfidCommRetries;
|
||||
//start communication with polling pattern, has been assembled in last send run
|
||||
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
|
||||
break;
|
||||
|
||||
case RfidRxState.Idle:
|
||||
break;
|
||||
|
||||
default:
|
||||
//RFID or device is not ready or protocol error, start retry
|
||||
if (_rfidCommRetryCounter > 0)
|
||||
{
|
||||
_logger.Debug($"{Ident} Internal RFID retry");
|
||||
_rfidCommRetryCounter--;
|
||||
|
||||
//send the same RFID message again
|
||||
_rfidRxState = RfidComm(ref _rfidRxBuffer, _rfidTxBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
//communication failed because of exceeded internal RFID retry counter
|
||||
_rfidRxState = RfidRxState.CommunicationFailed;
|
||||
_logger.Debug($"{Ident} Internal RFID communication failed, RFID retry counter exceeded");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return _rfidRxState;
|
||||
}
|
||||
/// <summary>
|
||||
/// send and receive
|
||||
/// </summary>
|
||||
/// <param name="rxBuffer"></param>
|
||||
/// <param name="txBuffer"></param>
|
||||
/// <returns>RfidRxState</returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-18" author="T.Wiedebusch">
|
||||
/// - Communication timeout activated.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-12" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
private RfidRxState RfidComm(ref Byte[] rxBuffer, Byte[] txBuffer)
|
||||
{
|
||||
var rfidRxState = RfidRxState.CommPortError;
|
||||
|
||||
if (!IsOpen()) return rfidRxState;
|
||||
|
||||
Clear();
|
||||
//_logger.Trace($"{Ident} RFID raw sent({BitConverter.ToString(txBuffer)})");
|
||||
PhysicalWrite(txBuffer);
|
||||
_commCounter++;
|
||||
//set timeout for receive exit, take 5 records of 6 byte chunks as base
|
||||
var commTimeoutMs = PortSettingsForTransmitProtocol.ResponseTimeoutMs / 5;
|
||||
while (commTimeoutMs > 0 && BytesToRead() < RfidRxLength)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
commTimeoutMs--;
|
||||
}
|
||||
var i = 0;
|
||||
//test if something is in input buffer
|
||||
if (RfidRxLength > BytesToRead())
|
||||
{
|
||||
_logger.Debug($"{Ident} Internal RFID read timeout({PortSettingsForTransmitProtocol.ResponseTimeoutMs}ms)");
|
||||
return RfidRxState.RxTimeout;
|
||||
}
|
||||
while (BytesToRead() > 0 && i < RfidRxLength)
|
||||
{
|
||||
rxBuffer[i] = (Byte)ReadByte();
|
||||
i++;
|
||||
}
|
||||
if (i != RfidRxLength) return rfidRxState;
|
||||
|
||||
//all expected bytes received
|
||||
//_logger.Trace($"{Ident} RFID raw read({BitConverter.ToString(rxBuffer)})");
|
||||
rfidRxState = CheckRfidRxBuffer(rxBuffer);
|
||||
|
||||
return rfidRxState;
|
||||
}
|
||||
/// <summary>
|
||||
/// check the received buffer CRC, BCC and
|
||||
/// </summary>
|
||||
/// <param name="rfidRxBuffer"></param>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2017-Dec-19" author="T.Wiedebusch">
|
||||
/// - New modes implemented to differ between sending / waiting for Rx start
|
||||
/// and receiving
|
||||
/// </remarks>
|
||||
/// <returns>RfidRxState</returns>
|
||||
private RfidRxState CheckRfidRxBuffer(Byte[] rfidRxBuffer)
|
||||
{
|
||||
//retry required by start sync error
|
||||
if (RfidFrameStartId != rfidRxBuffer[RfidRxStartSyncPosition])
|
||||
return RfidRxState.StartSyncError;
|
||||
//retry required by length error
|
||||
if (RfidFrameRxLength != rfidRxBuffer[RfidRxLengthPosition])
|
||||
return RfidRxState.LengthError;
|
||||
|
||||
//data marked as useful data
|
||||
if (RfidRxModeMarker != rfidRxBuffer[RfidRxModePosition] ||
|
||||
RfidFramePayloadDataMarker != rfidRxBuffer[RfidRxDataMarkerPosition])
|
||||
return RfidRxState.RetryRequired;
|
||||
//BCC check transfer entire receive buffer, this will automatically handled
|
||||
if (rfidRxBuffer[RfidRxBccPosition] != BuildRfidBcc(rfidRxBuffer))
|
||||
return RfidRxState.BccError;
|
||||
|
||||
//extract the 6 data (payload) bytes and the data frame marker
|
||||
var testBuffer = new Byte[RfidPayLoadLength];
|
||||
for (var i = 0; i < RfidPayLoadLength; i++)
|
||||
testBuffer[i] = rfidRxBuffer[i + RfidRxDataPosition];
|
||||
//CRC with LSB first
|
||||
var buildCrc = Crc16Ccitt.CalculateLsb0408(testBuffer);
|
||||
UInt16 receivedCrc = rfidRxBuffer[RfidRxCrcHighPosition];
|
||||
receivedCrc <<= 8;
|
||||
receivedCrc &= 0xFF00;
|
||||
receivedCrc += rfidRxBuffer[RfidRxCrcLowPosition];
|
||||
if (receivedCrc != buildCrc) return RfidRxState.DataCrcError;
|
||||
//send loop is active
|
||||
if (_txIsActive) return RfidRxState.EchoRxProtocolOk;
|
||||
//if the start sync byte has been detected the normal receive mode is active
|
||||
if (!_waitRawStartSyncPattern) return RfidRxState.DataRxProtocolOk;
|
||||
//search for raw protocol start sync Id to extract length information,
|
||||
//if start Id hasn't been found the wait for Rx start mode is active
|
||||
//forcing the retry loop being active (default switch)
|
||||
if (PortSettingsForTransmitProtocol.ProtSyncByte != _rfidRxBuffer[RfidRxDataPosition])
|
||||
return RfidRxState.WaitRxStartProtocolOk;
|
||||
//here the Rx mode is going to be activated, first data received including
|
||||
//start sync Id and length information
|
||||
_waitRawStartSyncPattern = false;
|
||||
_rawRxLength = _rfidRxBuffer[RfidRxDataPosition +
|
||||
(PortSettingsForTransmitProtocol.ProtLengthIndex.HasValue ?
|
||||
PortSettingsForTransmitProtocol.ProtLengthIndex.Value : 0)] +
|
||||
PortSettingsForTransmitProtocol.ProtAddLength;
|
||||
_rawRxPayLoad = new Byte[_rawRxLength];
|
||||
return RfidRxState.DataRxProtocolOk;
|
||||
}
|
||||
/// <summary>
|
||||
/// assemble the RFID transmit buffer as bytes
|
||||
/// </summary>
|
||||
/// <param name="rfidTxBuffer"></param>
|
||||
/// <param name="payLoad"></param>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private static void PrepareRfidTxBuffer(ref Byte[] rfidTxBuffer, Byte[] payLoad)
|
||||
{
|
||||
RfidTxProtocol rfidTxStruct;
|
||||
rfidTxStruct.StartPattern = new Byte[]
|
||||
{
|
||||
RfidFrameStartId, //start identifier 0x01
|
||||
RfidFrameTxLength, //length, start behind length without BCC
|
||||
0xE8, 0x90, 0x00, //CMD 1, 2, 3
|
||||
0x00, 0x32, 0x00, 0x11, //Power 1 and 2
|
||||
0x48 //TX bits
|
||||
};
|
||||
//the default data is the polling pattern 0x03
|
||||
rfidTxStruct.PayLoad = new[] { RfidFramePayloadDataMarker,
|
||||
RfidFramePollingByte, RfidFramePollingByte, RfidFramePollingByte,
|
||||
RfidFramePollingByte, RfidFramePollingByte, RfidFramePollingByte};
|
||||
|
||||
//fill the payload with real data
|
||||
var i = 0;
|
||||
if (payLoad != null)
|
||||
{
|
||||
//put payload behind RFID frame payload data marker
|
||||
for (; i < payLoad.Length; i++)
|
||||
rfidTxStruct.PayLoad[i + 1] = payLoad[i];
|
||||
}
|
||||
rfidTxStruct.ExpectedRxBytes = RfidFrameExpectedRxLength;
|
||||
|
||||
//fill transmit buffer with constant start pattern for RFID communication
|
||||
i = 0;
|
||||
for (; i < RfidStartPatternLength; i++)
|
||||
{
|
||||
rfidTxBuffer[i] = rfidTxStruct.StartPattern[i];
|
||||
}
|
||||
//add payload to buffer, first byte is constant 0x7D (data mode)
|
||||
var c = 0;
|
||||
for (; i < RfidStartPatternLength + RfidPayLoadLength; i++)
|
||||
{
|
||||
rfidTxBuffer[i] = rfidTxStruct.PayLoad[c];
|
||||
c++;
|
||||
}
|
||||
//add data CRC, LSB first
|
||||
rfidTxStruct.DataCrc = Crc16Ccitt.CalculateLsb0408(rfidTxStruct.PayLoad);
|
||||
rfidTxBuffer[i] = (Byte)(rfidTxStruct.DataCrc & 0xFF);
|
||||
i++;
|
||||
rfidTxBuffer[i] = (Byte)((rfidTxStruct.DataCrc & 0xFF00) >> 8);
|
||||
//add expected receive bytes
|
||||
i++;
|
||||
rfidTxBuffer[i] = rfidTxStruct.ExpectedRxBytes;
|
||||
//add BCC
|
||||
rfidTxStruct.Bcc = BuildRfidBcc(rfidTxBuffer);
|
||||
i++;
|
||||
rfidTxBuffer[i] = rfidTxStruct.Bcc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// build simple byte by byte XOR'ed checksum called BCC for RFID
|
||||
/// </summary>
|
||||
/// <param name="rfidRawBuffer"></param>
|
||||
/// <returns>BCC code</returns>
|
||||
/// <remarks date="2017-Dec-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private static Byte BuildRfidBcc(Byte[] rfidRawBuffer)
|
||||
{
|
||||
//remove the "start byte" and the BCC itself
|
||||
var maxCounts = rfidRawBuffer.Length - 1;
|
||||
//start behind the "start byte"
|
||||
var i = 1;
|
||||
Byte bcc = rfidRawBuffer[i];
|
||||
//take the second value
|
||||
i++;
|
||||
for (; i < maxCounts; i++)
|
||||
{
|
||||
bcc ^= rfidRawBuffer[i];
|
||||
}
|
||||
return bcc;
|
||||
}
|
||||
|
||||
private struct RfidTxProtocol
|
||||
{
|
||||
public Byte[] StartPattern;
|
||||
public Byte[] PayLoad;
|
||||
public UInt16 DataCrc;
|
||||
public Byte ExpectedRxBytes;
|
||||
public Byte Bcc;
|
||||
}
|
||||
}
|
||||
|
||||
public enum RfidRxState
|
||||
{
|
||||
/// <summary>
|
||||
/// No communication is ongoing
|
||||
/// </summary>
|
||||
Idle,
|
||||
/// <summary>
|
||||
/// Echo of sent protocol is received successfully back
|
||||
/// </summary>
|
||||
EchoRxProtocolOk,
|
||||
/// <summary>
|
||||
/// Waiting for switch from transmitting to receiving
|
||||
/// </summary>
|
||||
WaitRxStartProtocolOk,
|
||||
/// <summary>
|
||||
/// Received data message is valid
|
||||
/// </summary>
|
||||
DataRxProtocolOk,
|
||||
/// <summary>
|
||||
/// Communication port assignment error
|
||||
/// </summary>
|
||||
CommPortError,
|
||||
/// <summary>
|
||||
/// Start of synchronization error
|
||||
/// </summary>
|
||||
StartSyncError,
|
||||
/// <summary>
|
||||
/// Length error
|
||||
/// </summary>
|
||||
LengthError,
|
||||
/// <summary>
|
||||
/// Retry required
|
||||
/// </summary>
|
||||
RetryRequired,
|
||||
/// <summary>
|
||||
/// CRC error of data in payload field
|
||||
/// </summary>
|
||||
DataCrcError,
|
||||
/// <summary>
|
||||
/// BCC (special checksum) error of RFID
|
||||
/// </summary>
|
||||
BccError,
|
||||
/// <summary>
|
||||
/// Receive timeout
|
||||
/// </summary>
|
||||
RxTimeout,
|
||||
/// <summary>
|
||||
/// Communication to RFID failed, record cannot be assembled
|
||||
/// </summary>
|
||||
CommunicationFailed,
|
||||
/// <summary>
|
||||
/// Communication to RFID successfully executed
|
||||
/// </summary>
|
||||
CommunicationFinished
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.IO.Ports;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.SerialPorts
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class UartSerialPort : BaseSerialPort
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public UartSerialPort(String ident, SerialPort serialPort, TransmitPortSettings setting) : base(ident, serialPort, setting)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol;
|
||||
using BaseDataEventArgs = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments.BaseDataEventArgs;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Base for all protocol types
|
||||
/// </summary>
|
||||
public abstract class BaseProtocol : IProtocol
|
||||
{
|
||||
private readonly CancellationTokenSource _decodingToken = new CancellationTokenSource();
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
|
||||
|
||||
private readonly ConcurrentQueue<IPortDataEventArgs> _decodingFifo = new ConcurrentQueue<IPortDataEventArgs>();
|
||||
|
||||
//initially do not signal event
|
||||
private readonly AutoResetEvent _onSyncDecodingThread = new AutoResetEvent(false);
|
||||
private readonly Thread _decodingThread;
|
||||
|
||||
/// <summary>
|
||||
/// Ident is a combined string of Slot, Port, Protocol and Type
|
||||
/// </summary>
|
||||
protected readonly String Ident;
|
||||
private ITransmitProtocol _transmitProtocol;
|
||||
/// <summary>
|
||||
/// Starting decoding thread
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
protected BaseProtocol(String ident)
|
||||
{
|
||||
Ident = ident;
|
||||
//assign thread to loop
|
||||
_decodingThread = new Thread(DecodingThreadLoop) { Name = $"{Ident} Decoding thread" };
|
||||
//start DecodingThread
|
||||
ThreadWatcher.Instance.Start(_decodingThread);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Kill the decoding thread
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Oct-25" author="T.Wiedebusch">
|
||||
/// - try catch block.
|
||||
/// </remarks>
|
||||
public virtual void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
//Cancel receive tokens
|
||||
_decodingToken.Cancel();
|
||||
|
||||
//Run thread again to notice CancellationToken has changed
|
||||
_onSyncDecodingThread.Set();
|
||||
|
||||
//this timeout counter is being used for dispose only
|
||||
var timeoutCounter = 100;
|
||||
|
||||
while (_decodingThread.ThreadState != ThreadState.Stopped && timeoutCounter > 0)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
timeoutCounter -= 1;
|
||||
//Run thread again to notice CancellationToken has changed
|
||||
_onSyncDecodingThread.Set();
|
||||
}
|
||||
|
||||
if (_decodingThread.ThreadState != ThreadState.Stopped)
|
||||
{
|
||||
//if thread still running, he stuck so try to abort
|
||||
// try to avoid abort (takes age to run and is not safe)
|
||||
_decodingThread.Abort();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new ApplicationException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill FIFO with received data
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public void FillDecodingBuffer(IPortDataEventArgs data)
|
||||
{
|
||||
if (_decodingToken.IsCancellationRequested) return;
|
||||
|
||||
//put data to FIFO
|
||||
_decodingFifo.Enqueue(data);
|
||||
|
||||
//put DecodingThread state from WaitSleepJoin to Running
|
||||
_onSyncDecodingThread.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decoding thread loop calling the individual decoding
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Feb-14" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
private void DecodingThreadLoop()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!_decodingToken.IsCancellationRequested)
|
||||
{
|
||||
while (_decodingFifo.TryDequeue(out var receivedRecord))
|
||||
{
|
||||
DecodeRecord(receivedRecord);
|
||||
}
|
||||
|
||||
//put DecodingThread to WaitSleepJoin until next data arrived
|
||||
_onSyncDecodingThread.WaitOne();
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
{ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The decoding routine
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
protected abstract void DecodeRecord(IPortDataEventArgs data);
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITransmitProtocol GetTransmitProtocol()
|
||||
{
|
||||
return _transmitProtocol;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetTransmitProtocol(ITransmitProtocol transmitProtocol)
|
||||
{
|
||||
_transmitProtocol = transmitProtocol;
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore.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;
|
||||
}
|
||||
}
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol;
|
||||
using BaseDataEventArgs = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments.BaseDataEventArgs;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Protocol interface
|
||||
/// </summary>
|
||||
public interface IProtocol : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Fill the buffer with data ready for decoding
|
||||
/// </summary>
|
||||
/// <param name="rawData"></param>
|
||||
void FillDecodingBuffer(IPortDataEventArgs rawData );
|
||||
|
||||
/// <summary>
|
||||
/// Record is ready to send including all protocols
|
||||
/// </summary>
|
||||
event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
|
||||
|
||||
/// <summary>
|
||||
/// Record is successfully decoded and ready for further processing
|
||||
/// </summary>
|
||||
event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
|
||||
|
||||
/// <summary>
|
||||
/// Return of transmit protocol settings
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
ITransmitProtocol GetTransmitProtocol();
|
||||
|
||||
/// <summary>
|
||||
/// Assignment of transmit protocol
|
||||
/// </summary>
|
||||
/// <param name="transmitProtocol"></param>
|
||||
void SetTransmitProtocol(ITransmitProtocol transmitProtocol);
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol
|
||||
{
|
||||
public abstract class BaseTransmitProtocol : ITransmitProtocol
|
||||
{
|
||||
public static readonly ILog Logger = LogManager.GetLogger(typeof(BaseTransmitProtocol));
|
||||
|
||||
|
||||
protected BaseTransmitProtocol(String port)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public abstract TransmitPortSettings GetTransmitPortSettings();
|
||||
public abstract void SetResponseTimeout(Int32 responseTimeoutMs);
|
||||
|
||||
public abstract void SetDefaultResponseTimeout();
|
||||
|
||||
public abstract List<Byte> DecodeDataForPhysicalLayer(String ident, Byte command, Byte[] payload, Boolean hideDataInLog = false);
|
||||
|
||||
public abstract List<Byte> DecodeDataForLogicLayer(String ident, List<Byte> rawData, Boolean hideDataInLog = false);
|
||||
|
||||
public abstract List<Byte> DecodeDataForPhysicalLayerUI1236(String ident, Byte[] payload, Boolean hideDataInLog = false);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol
|
||||
{
|
||||
public interface ITransmitProtocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Struct of settings being needed for transmit port
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
TransmitPortSettings GetTransmitPortSettings();
|
||||
|
||||
/// <summary>
|
||||
/// Overwrite the default response timeout
|
||||
/// </summary>
|
||||
void SetResponseTimeout(Int32 responseTimeoutMs);
|
||||
|
||||
/// <summary>
|
||||
/// Set timeout to the default response time
|
||||
/// </summary>
|
||||
void SetDefaultResponseTimeout();
|
||||
|
||||
// ReSharper disable once InconsistentNaming UI1236 is a naming forced by the caller
|
||||
List<Byte> DecodeDataForPhysicalLayerUI1236(String ident, Byte[] payload, Boolean hideDataInLog = false);
|
||||
|
||||
/// <summary>
|
||||
/// Send out data (including transport protocol specific data like CRC) to physical port (like UART or IrDA)
|
||||
/// </summary>
|
||||
List<Byte> DecodeDataForPhysicalLayer(String ident, Byte command , Byte[] payload, Boolean hideDataInLog = false);
|
||||
|
||||
/// <summary>
|
||||
/// Received data have to be checked and converted to request protocol
|
||||
/// </summary>
|
||||
List<Byte> DecodeDataForLogicLayer(String ident, List<Byte> rawData, Boolean hideDateInLog = false);
|
||||
}
|
||||
}
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol
|
||||
{
|
||||
/// <summary>
|
||||
/// IrDA transmission protocol
|
||||
/// </summary>
|
||||
public class IrdaTransmitProtocol : BaseTransmitProtocol
|
||||
{
|
||||
// Format of IrDA transmit protocol:
|
||||
// IrdaSyncByte|IrdaSend/ReceiveHeader|IrdaPayLoadLength|IrdaCommand/Message|
|
||||
// IrdaPayLoad|IrdaCrc LSB|IrdaCrc MSB
|
||||
private const Byte IrdaSyncByte = 0x9B;
|
||||
|
||||
// Encoding of IrdaSendHeader (LAT: listen after talk, LAT 10b: 500ms)
|
||||
// Bits: 0| 0| 10| 0| 000
|
||||
// standard frame|adapter to register|LAT|reserved|optical command
|
||||
private const Byte IrdaSendHeader = 0x20;
|
||||
|
||||
// Encoding of IrdaReceiveHeader (LAT: listen after talk, LAT 10b: 500ms)
|
||||
// Bits: 0| 1| 10| 0| 001
|
||||
// standard frame|register to adapter|LAT|reserved|optical message
|
||||
private const Byte IrdaReceiveHeader = 0x61;
|
||||
private const Byte IrdaWakeupHeader = 0x41;
|
||||
|
||||
// Mask out the LAT and the reserved bit for the IrdaReceiveHeader
|
||||
private const Byte IrdaReceiveHeaderMask = 0xC7;
|
||||
|
||||
// data from adapter (software) to register (water meter) referred as optical command
|
||||
private const Byte IrdaCommand = 0x02;
|
||||
|
||||
// data from register (water meter) to adapter (software) referred as optical message
|
||||
private const Byte IrdaMessageId = 0x03;
|
||||
private const Byte IrdaWakeupId = 0x04;
|
||||
|
||||
// IrdaSyncByte|IrdaReceiveHeader|IrdaPayLoadLength|IrdaMessageId|CRC LSB|CRC MSB
|
||||
private const Int32 IrdaProtocolFrameLength = 6;
|
||||
|
||||
// Indexes of IrDA optical protocol
|
||||
private const Int32 IrdaSyncByteIndex = 0;
|
||||
private const Int32 IrdaHeaderIndex = 1;
|
||||
private const Int32 IrdaPayLoadLengthIndex = 2;
|
||||
private const Int32 IrdaMessageIndex = 3;
|
||||
private const Int32 IrdaPayLoadIndex = 4;
|
||||
|
||||
//the IrDA length covers the payload length only
|
||||
private const UInt16 ProtAddLength = IrdaProtocolFrameLength;
|
||||
private const Int32 DefaultResponseTimeoutMs = 250;
|
||||
//private const Int32 DefaultResponseTimeoutMs = 150;
|
||||
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
|
||||
private const UInt32 BaudRate = 115200;
|
||||
|
||||
private readonly UInt32? _receiveBufferFlushThreshold = 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetResponseTimeout(Int32 responseTimeoutMs)
|
||||
{
|
||||
_responseTimeoutMs = responseTimeoutMs;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override void SetDefaultResponseTimeout()
|
||||
{
|
||||
_responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IrdaTransmitProtocol(String portName) : base(portName)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override TransmitPortSettings GetTransmitPortSettings()
|
||||
{
|
||||
return new TransmitPortSettings(IrdaSyncByte, IrdaPayLoadLengthIndex, ProtAddLength,
|
||||
_responseTimeoutMs, BaudRate, _receiveBufferFlushThreshold);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2018-Mar-15" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2020-Dec-19" author="T.Wiedebusch">
|
||||
/// - Removed IrdaMessageId identifier check as it was observed receiving a 0x03 or a 0x04 in this field.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Jul-19" author="T.Wiedebusch">
|
||||
/// - Wakeup message detection reported to log-file,
|
||||
/// - Message error text changed.
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Sep-01" author="T.Wiedebusch">
|
||||
/// - Hide data in log introduced.
|
||||
/// </remarks>
|
||||
public override List<Byte> DecodeDataForLogicLayer(String ident, List<Byte> irdaRecord, Boolean hideDataInLog = false)
|
||||
{
|
||||
// avoid logging of passwords or other sensitive data
|
||||
Logger.Info(hideDataInLog
|
||||
? $"{ident} DecodeDataForLogicalLayer Cordonel->PC(*****)"
|
||||
: $"{ident} DecodeDataForLogicalLayer Cordonel->PC({BitConverter.ToString(irdaRecord.ToArray())})");
|
||||
|
||||
// check the record length and start of frame information
|
||||
if (irdaRecord.Count < IrdaProtocolFrameLength + irdaRecord[IrdaPayLoadLengthIndex]
|
||||
|| irdaRecord[IrdaSyncByteIndex] != IrdaSyncByte
|
||||
|| (((irdaRecord[IrdaHeaderIndex] & IrdaReceiveHeaderMask) != (IrdaReceiveHeader & IrdaReceiveHeaderMask)
|
||||
&& (irdaRecord[IrdaHeaderIndex] != IrdaWakeupHeader))
|
||||
|| ((irdaRecord[IrdaMessageIndex] != IrdaMessageId)
|
||||
&& irdaRecord[IrdaMessageIndex] != IrdaWakeupId)))
|
||||
{
|
||||
var error = new ApplicationException($"{ident} Reply from IrDA is invalid.");
|
||||
Logger.Error(error.Message, error);
|
||||
|
||||
return new List<Byte>();
|
||||
}
|
||||
|
||||
// extract the received CRC first the MSB at last position
|
||||
UInt16 receivedCrc = irdaRecord[irdaRecord.Count - 1];
|
||||
receivedCrc <<= 8;
|
||||
// add the LSB from before last position, the LSB of the CRC will be sent first
|
||||
receivedCrc += irdaRecord[irdaRecord.Count - 2];
|
||||
|
||||
// the CRC is being built from IrdaReceiveHeader to end of IrdaPayload,
|
||||
// subtract irdaSyncByteLength and irdaCrcLength
|
||||
var irdaCrcInputBuffer = new List<Byte>();
|
||||
irdaCrcInputBuffer.AddRange(irdaRecord.GetRange(IrdaHeaderIndex, irdaRecord.Count - 3));
|
||||
|
||||
var calculatedCrc = Crc16Ccitt.CalculateReversedLsb8408(irdaCrcInputBuffer.ToArray());
|
||||
|
||||
if (receivedCrc != calculatedCrc)
|
||||
{
|
||||
var error = new ApplicationException($"{ident} Reply CRC failure. Decoding of transmit layer failed.");
|
||||
Logger.Error(error.Message, error);
|
||||
return new List<Byte>();
|
||||
}
|
||||
|
||||
// build the return value witch equals the IrDA payload
|
||||
var irdaPayLoad = new List<Byte>();
|
||||
irdaPayLoad.AddRange(irdaRecord.GetRange(IrdaPayLoadIndex, irdaRecord.Count - IrdaProtocolFrameLength));
|
||||
|
||||
return irdaPayLoad;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2018-Mar-13" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Mai-16" author="T.Wiedebusch">
|
||||
/// - Hide data in log forwarded to DecodeDateForPhysicalLayer to hide passwords in log files.
|
||||
/// </remarks>
|
||||
public override List<Byte> DecodeDataForPhysicalLayer(String ident, Byte requestProtocolCommand, Byte[] requestProtocolPayload,
|
||||
Boolean hideDataInLog = false)
|
||||
{
|
||||
var irdaCrcInputBuffer = new List<Byte>
|
||||
{
|
||||
// build CRC calculation buffer without sync byte
|
||||
// IrdaSendHeader|IrdaPayLoadLength|IrdaCommand|requestProtocolCommand|requestProtocolPayload
|
||||
IrdaSendHeader,
|
||||
|
||||
// IrdaPayloadLength is the requestProtocolCommandLength + requestProtocolPayloadLength
|
||||
(Byte)(requestProtocolPayload.Length + 1),
|
||||
IrdaCommand,
|
||||
|
||||
// add the IrdaPayload witch is the requestProtocolCommand + requestProtocolPayload
|
||||
requestProtocolCommand
|
||||
};
|
||||
irdaCrcInputBuffer.AddRange(requestProtocolPayload);
|
||||
|
||||
var crcResult = Crc16Ccitt.CalculateReversedLsb8408(irdaCrcInputBuffer.ToArray());
|
||||
|
||||
// assemble result buffer
|
||||
// IrdaSyncByte|crcInputBuffer|IrdaCrc LSB|IrdaCrc MSB
|
||||
var irdaRecord = new List<Byte> { IrdaSyncByte };
|
||||
irdaRecord.AddRange(irdaCrcInputBuffer);
|
||||
irdaRecord.Add((Byte)(crcResult & 0xFF));
|
||||
irdaRecord.Add((Byte)(crcResult >> 8));
|
||||
|
||||
// avoid logging of passwords or other sensitive data
|
||||
Logger.Info(hideDataInLog
|
||||
? $"{ident} DecodeDataForPhysicalLayer PC->Cordonel(*****)"
|
||||
: $"{ident} DecodeDataForPhysicalLayer PC->Cordonel({BitConverter.ToString(irdaRecord.ToArray())})");
|
||||
|
||||
return irdaRecord;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2022-Aug-24" author="R.Drabesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public override List<Byte> DecodeDataForPhysicalLayerUI1236(String ident, Byte[] requestProtocolPayload,
|
||||
Boolean hideDataInLog = false)
|
||||
{
|
||||
var collection = new List<Byte>
|
||||
{
|
||||
35,
|
||||
(Byte) (requestProtocolPayload.Length / 2)
|
||||
};
|
||||
collection.AddRange(requestProtocolPayload);
|
||||
var crcResult = Crc16Ccitt.CalculateReversedLsb8408(collection.ToArray());
|
||||
var byteList = new List<Byte>
|
||||
{
|
||||
155
|
||||
};
|
||||
byteList.AddRange(collection);
|
||||
byteList.Add((Byte)(crcResult & 0xFF));
|
||||
byteList.Add((Byte)(crcResult >> 8));
|
||||
// avoid logging of passwords or other sensitive data
|
||||
Logger.Info(hideDataInLog
|
||||
? $"{ident} DecodeDataForPhysicalLayerUI1236 PC->Cordonel(*****)"
|
||||
: $"{ident} DecodeDataForPhysicalLayerUI1236 PC->Cordonel({BitConverter.ToString(byteList.ToArray())})");
|
||||
return byteList;
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol
|
||||
{
|
||||
public class LedTransmitProtocol : BaseTransmitProtocol
|
||||
{
|
||||
private readonly Byte? _protSyncByte = null;
|
||||
private const UInt16 ProtAddLength = 0;
|
||||
private readonly UInt16? _protLengthIndex = null;
|
||||
private const Int32 DefaultResponseTimeoutMs = 50;
|
||||
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
|
||||
private const UInt32 BaudRate = 115200;
|
||||
|
||||
private readonly UInt32? _receiveBufferFlushThreshold = 1000;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetResponseTimeout(Int32 responseTimeoutMs)
|
||||
{
|
||||
_responseTimeoutMs = responseTimeoutMs;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override void SetDefaultResponseTimeout()
|
||||
{
|
||||
_responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
}
|
||||
public LedTransmitProtocol(String portName) : base (portName)
|
||||
{
|
||||
|
||||
}
|
||||
public override TransmitPortSettings GetTransmitPortSettings()
|
||||
{
|
||||
return new TransmitPortSettings(_protSyncByte, _protLengthIndex, ProtAddLength, _responseTimeoutMs, BaudRate,
|
||||
_receiveBufferFlushThreshold);
|
||||
}
|
||||
|
||||
public TransmitPortSettings GetTransmitPortSettings(UInt32 baudRate)
|
||||
{
|
||||
return new TransmitPortSettings(_protSyncByte, _protLengthIndex, ProtAddLength, _responseTimeoutMs, baudRate,
|
||||
_receiveBufferFlushThreshold);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override List<Byte> DecodeDataForPhysicalLayer(String ident, Byte command, Byte[] payload, Boolean hideDataInLog = false)
|
||||
{
|
||||
throw new ApplicationException("Streaming port cannot send data");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override List<Byte> DecodeDataForLogicLayer(String ident, List<Byte> rawData, Boolean hideDataInLog = false)
|
||||
{
|
||||
return rawData;
|
||||
}
|
||||
|
||||
public override List<Byte> DecodeDataForPhysicalLayerUI1236(String ident, Byte[] payload, Boolean hideDataInLog = false)
|
||||
=> throw new ApplicationException("Streaming port cannot send data");
|
||||
}
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol
|
||||
{
|
||||
public class RfidTransmitProtocol : UartTransmitProtocol
|
||||
{
|
||||
//use the UART transmit protocol sync byte wrapped to RFID protocol
|
||||
private const Byte ProtSyncByte = 0x5B;
|
||||
private const UInt16 ProtAddLength = 1;
|
||||
private readonly UInt16? _protLengthIndex = 1;
|
||||
//this will be used if RFID protocol has been extracted from RfidComPort
|
||||
//private const Byte ProtSyncByte = 0x01;
|
||||
//private const UInt16 ProtAddLength = 1;
|
||||
//private readonly UInt16? _protLengthIndex = 3;
|
||||
|
||||
private const Int32 DefaultResponseTimeoutMs = 1550;
|
||||
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetResponseTimeout(Int32 responseTimeoutMs)
|
||||
{
|
||||
_responseTimeoutMs = responseTimeoutMs;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override void SetDefaultResponseTimeout()
|
||||
{
|
||||
_responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
}
|
||||
private const UInt32 BaudRate = 9600;
|
||||
private readonly UInt32? _receiveBufferFlushThreshold = null;
|
||||
|
||||
public RfidTransmitProtocol(String portName) : base (portName)
|
||||
{
|
||||
|
||||
}
|
||||
public override TransmitPortSettings GetTransmitPortSettings()
|
||||
{
|
||||
return new TransmitPortSettings(ProtSyncByte, _protLengthIndex, ProtAddLength,
|
||||
_responseTimeoutMs, BaudRate, _receiveBufferFlushThreshold);
|
||||
}
|
||||
public override List<Byte> SpecificCrcCalc()
|
||||
{
|
||||
//returns an empty list of bytes to avoid filling of CRC in advance to the CRC calculation,
|
||||
//because the RFID does not use the CRC filled up with 0x00, 0x00 to calculate itself
|
||||
return new List<Byte>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.TransmitProtocol
|
||||
{
|
||||
public class UartTransmitProtocol : BaseTransmitProtocol
|
||||
{
|
||||
// Format of UART transmit protocol:
|
||||
// UartSyncByte|UartLength LSB|UartLength MSB|requestProtocolCommand|
|
||||
// NextRequestProtocolCommand|UartCrc LSB|UartCrc MSB|requestProtocolPayload
|
||||
// UartLength MSB will NOT be used, it is always 0x00
|
||||
// NextRequestProtocolCommand will NOT be used, it is always 0x00
|
||||
|
||||
//Indexes of UART transmit protocol
|
||||
private const Int32 UartSyncByteIndex = 0;
|
||||
private const Int32 UartLengthIndex = 1;
|
||||
private const Int32 UartCommandIndex = 3;
|
||||
private const Int32 UartCrcIndex = 5;
|
||||
private const Int32 UartPayloadIndex = 7;
|
||||
|
||||
//length of UART transmit protocol fields
|
||||
private const Int32 UartSyncByteLength = 1; //length of syncByte
|
||||
private const Int32 UartHeaderLength = 4; //length excluding syncByte and CRC
|
||||
private const Int32 UartCrcLength = 2;
|
||||
private const Int32 UartProtocolFrameLength = UartSyncByteLength + UartHeaderLength + UartCrcLength;
|
||||
|
||||
private const Byte UartSyncByte = 0x5B;
|
||||
//The UART length covers all bytes excluding the syncByte length
|
||||
private const Int32 DefaultResponseTimeoutMs = 50;
|
||||
private Int32 _responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
private const UInt16 ProtAddLength = UartSyncByteLength;
|
||||
private const UInt32 BaudRate = 9600;
|
||||
private readonly UInt32? _receiveBufferFlushThreshold = null;
|
||||
private const Boolean UartDoubleSyncByte = true;
|
||||
/// <inheritdoc />
|
||||
public override void SetResponseTimeout(Int32 responseTimeoutMs)
|
||||
{
|
||||
_responseTimeoutMs = responseTimeoutMs;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override void SetDefaultResponseTimeout()
|
||||
{
|
||||
_responseTimeoutMs = DefaultResponseTimeoutMs;
|
||||
}
|
||||
public UartTransmitProtocol(String portName) : base (portName)
|
||||
{
|
||||
|
||||
}
|
||||
public override TransmitPortSettings GetTransmitPortSettings()
|
||||
{
|
||||
return new TransmitPortSettings(UartSyncByte, UartLengthIndex, ProtAddLength,
|
||||
_responseTimeoutMs, BaudRate, _receiveBufferFlushThreshold, UartDoubleSyncByte);
|
||||
}
|
||||
|
||||
public override List<Byte> DecodeDataForLogicLayer(String ident, List<Byte> uartRecord, Boolean hideDataInLog = false)
|
||||
{
|
||||
// check the record length and start of frame information
|
||||
if (uartRecord.Count < uartRecord[UartLengthIndex] + UartSyncByteLength
|
||||
|| UartSyncByte != uartRecord[UartSyncByteIndex])
|
||||
{
|
||||
var error = new ApplicationException($"{ident} Reply from UART is invalid. Decoding of transmit layer failed.");
|
||||
Logger.Error(error.Message, error);
|
||||
return new List<Byte>();
|
||||
}
|
||||
|
||||
//extract the header without syncByte and the payload, call CRC calculation
|
||||
var calculatedCrc = CrcCalc(uartRecord.GetRange(UartLengthIndex, UartHeaderLength),
|
||||
uartRecord.GetRange(UartPayloadIndex, uartRecord.Count - UartProtocolFrameLength));
|
||||
|
||||
//extract the received CRC, first the MSB
|
||||
UInt16 receivedCrc = uartRecord[UartCrcIndex + 1];
|
||||
receivedCrc <<= 8;
|
||||
receivedCrc += uartRecord[UartCrcIndex];
|
||||
|
||||
if(receivedCrc != calculatedCrc)
|
||||
{
|
||||
var error = new ApplicationException($"{ident} Reply CRC failure. Decoding of transmit layer failed.");
|
||||
Logger.Error(error.Message, error);
|
||||
return new List<Byte>();
|
||||
}
|
||||
|
||||
//the request protocol needs the requestProtocolCommand and the requestProtocolPayload
|
||||
var ret = new List<Byte> {uartRecord[UartCommandIndex]};
|
||||
//add the requestProtocolCommand
|
||||
//add the request protocol payload
|
||||
ret.AddRange(uartRecord.GetRange(UartPayloadIndex, uartRecord.Count - UartProtocolFrameLength));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public virtual List<Byte> SpecificCrcCalc()
|
||||
{
|
||||
//for the pure UART transmit protocol the CRC fields will be set
|
||||
//to 0x00 and used for the CRC calculation
|
||||
return new List<Byte>() { 0x00, 0x00 };
|
||||
}
|
||||
public UInt16 CrcCalc(List<Byte> header, List<Byte> payload)
|
||||
{
|
||||
//the CRC calculation excludes the syncByte and optional includes the
|
||||
//CRC fields filled up with 0x00
|
||||
var tmpArr = new List<Byte>();
|
||||
tmpArr.AddRange(header);
|
||||
|
||||
//this adds the optional CRC fields
|
||||
tmpArr.AddRange(SpecificCrcCalc());
|
||||
tmpArr.AddRange(payload);
|
||||
|
||||
return Crc16Ccitt.CalculateMsb1021(tmpArr.ToArray());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override List<Byte> DecodeDataForPhysicalLayer(String ident, Byte requestProtocolCommand,
|
||||
Byte[] requestProtocolPayload, Boolean hideDataInLog = false)
|
||||
{
|
||||
var uartHeader = new List<Byte>();
|
||||
var uartPayload = requestProtocolPayload.ToList();
|
||||
var uartRecord = new List<Byte>();
|
||||
|
||||
//first assemble the list being used for CRC calculation,
|
||||
//this list does NOT contain the syncByte and optional
|
||||
//the CRC LSB and MSB if set in SpecificCrcCalc()
|
||||
|
||||
//the length of length LSB and MSB: 2
|
||||
//add the length of the requestProtocolCommand: 1
|
||||
//the nextRequestProtocolCommand length: 1
|
||||
//the length of the CRC MSB and LSB: 2
|
||||
uartHeader.Add((Byte)(requestProtocolPayload.Length + UartHeaderLength + UartCrcLength));
|
||||
//add the length MSB, always 0x00
|
||||
uartHeader.Add(0x00);
|
||||
uartHeader.Add(requestProtocolCommand);
|
||||
//add the nextRequestProtocolCommand, always 0x00
|
||||
uartHeader.Add(0x00);
|
||||
var crcResult = CrcCalc(uartHeader, uartPayload);
|
||||
|
||||
//assemble the entire record for sending via the communication port
|
||||
uartRecord.Add(UartSyncByte);
|
||||
uartRecord.AddRange(uartHeader);
|
||||
uartRecord.Add((Byte)(crcResult & 0xFF));
|
||||
uartRecord.Add((Byte)(crcResult >> 8));
|
||||
uartRecord.AddRange(uartPayload);
|
||||
|
||||
return uartRecord;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <remarks date="2022-Aug-24" author="R.Drabesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
public override List<Byte> DecodeDataForPhysicalLayerUI1236(String ident, Byte[] requestProtocolPayload,
|
||||
Boolean hideDataInLog = false)
|
||||
{
|
||||
var uartHeader = new List<Byte>();
|
||||
var uartPayload = requestProtocolPayload.ToList();
|
||||
var uartRecord = new List<Byte>();
|
||||
uartHeader.Add((Byte)(requestProtocolPayload.Length + 4 + 2));
|
||||
uartHeader.Add(0);
|
||||
uartHeader.Add(0);
|
||||
var crcResult = CrcCalc(uartHeader, uartPayload);
|
||||
uartRecord.Add(91);
|
||||
uartRecord.AddRange(uartHeader);
|
||||
uartRecord.Add((Byte)(crcResult & 0xFF));
|
||||
uartRecord.Add((Byte)(crcResult >> 8));
|
||||
uartRecord.AddRange(uartPayload);
|
||||
return uartRecord;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis
|
||||
{
|
||||
public static class ProgramConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Name of register definition file
|
||||
/// </summary>
|
||||
public const String RegisterDefinitionFileName = "configuration.json";
|
||||
|
||||
/// <summary>
|
||||
/// Name of serial configuration file
|
||||
/// </summary>
|
||||
public const String FM2014ConfigFileName = "FM2014Config.json";
|
||||
|
||||
/// <summary>
|
||||
/// Name of serial configuration file
|
||||
/// </summary>
|
||||
public const String SerialConfigFileName = "SerialConfig.json";
|
||||
|
||||
/// <summary>
|
||||
/// Name for NLog config
|
||||
/// </summary>
|
||||
public const String NlogConfig = "NlogConfig.xml";
|
||||
|
||||
/// <summary>
|
||||
/// Name for meter config
|
||||
/// </summary>
|
||||
public const String MeterConfigFileName = "MeterConfig.json";
|
||||
|
||||
/// <summary>
|
||||
/// Name for SIRT config
|
||||
/// </summary>
|
||||
public const String SirtConfigFileName = "SirtConfig.json";
|
||||
|
||||
/// <summary>
|
||||
/// Name for offline information e.g. Passwords
|
||||
/// </summary>
|
||||
public const String OfflineInfoFile = "OfflineFile.json";
|
||||
|
||||
/// <summary>
|
||||
/// Path for data logger.
|
||||
/// </summary>
|
||||
public const String BaseLoggingPath = "C:\\GenesisLog\\";
|
||||
|
||||
/// <summary>
|
||||
/// Sub folder for genesis contents.
|
||||
/// </summary>
|
||||
public const String GenesisBaseFolder = "Genesis\\";
|
||||
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Commands that the genesis meter support over the request protocol.
|
||||
/// Functional Specification Breeze Core and Applications Revision:3.03(12748DOC11 - functional spec ICOE472.pdf)
|
||||
/// </summary>
|
||||
public static class Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// 9.2.5
|
||||
///Command 0x00 (NOP)
|
||||
///This command will do nothing, and will have no response.
|
||||
/// </summary>
|
||||
public const Byte Nop = 0x00;
|
||||
/// <summary>
|
||||
/// 9.2.6
|
||||
/// Command 0x01 (Query capabilities)
|
||||
/// This command will be used by the external computer to discover the protocol parameters that may be varied.
|
||||
/// These can then be compared with the external computer’s capabilities and the best match selected.
|
||||
/// </summary>
|
||||
public const Byte QueryCaps = 0x01;
|
||||
/// <summary>
|
||||
/// response on <see cref="QueryCaps"/>
|
||||
/// </summary>
|
||||
public const Byte QueryCapsReply = 0x02;
|
||||
/// <summary>
|
||||
/// 9.2.7
|
||||
/// Command 0x03 (Set capabilities)
|
||||
/// Used to finalize the baud rate and packet settings after negotiation.
|
||||
/// The reply will be sent at the currently selected baud rate and packet length,
|
||||
/// after which the settings will take effect
|
||||
/// </summary>
|
||||
public const Byte SetCaps = 0x03;
|
||||
/// <summary>
|
||||
/// response on <see cref="SetCaps"/>
|
||||
/// </summary>
|
||||
public const Byte SetCapsReply = 0x04;
|
||||
/// <summary>
|
||||
/// 9.2.8
|
||||
/// Command 0x05 (Train)
|
||||
///This command will perform target driven data training, that is, where the target is in control of the data flow.
|
||||
///See also command 0x11.
|
||||
/// </summary>
|
||||
public const Byte Train = 0x05;
|
||||
/// <summary>
|
||||
/// response on <see cref="Train"/>
|
||||
/// </summary>
|
||||
public const Byte TrainReply = 0x06;
|
||||
/// <summary>
|
||||
/// 9.2.9
|
||||
/// Command 0x07 (Repeat last)
|
||||
/// Used by the external computer to request the last response to be resent, for example if it was found to be corrupted.
|
||||
/// Note that the response 0x08 will never be sent, the reply to command 0x07 will be a verbatim resend of the last response.
|
||||
/// </summary>
|
||||
public const Byte RepeatLast = 0x07;
|
||||
/// <summary>
|
||||
/// response on <see cref="RepeatLast"/>
|
||||
/// </summary>
|
||||
public const Byte RepeatLastReply = 0x08;
|
||||
/// <summary>
|
||||
/// 9.2.10
|
||||
///Command 0x09 (Read data)
|
||||
///This command makes reads of any random selection of configuration registers, up to the maximum packet size negotiated.
|
||||
/// </summary>
|
||||
public const Byte ReadData = 0x09;
|
||||
/// <summary>
|
||||
/// response on <see cref="ReadData"/>
|
||||
/// </summary>
|
||||
public const Byte ReadDataReply = 0x0A;
|
||||
/// <summary>
|
||||
/// 9.2.11
|
||||
/// Command 0x0B (Write data)
|
||||
/// This command makes writes to any random selection of configuration registers, up to the maximum packet size negotiated.
|
||||
/// </summary>
|
||||
public const Byte WriteData = 0x0B;
|
||||
/// <summary>
|
||||
/// response on <see cref="WriteData"/>
|
||||
/// </summary>
|
||||
public const Byte WriteDataReply = 0x0C;
|
||||
/// <summary>
|
||||
/// 9.2.12
|
||||
///Command 0x0D (Multiple read data)
|
||||
///This command makes reads of one register multiple times, which will be more efficient than performing successive reads using command 0x09.
|
||||
///This command will be available from protocol version 0.40, for earlier protocol versions command 0x09 should be used.
|
||||
/// </summary>
|
||||
public const Byte MultipleReadData = 0x0D;
|
||||
/// <summary>
|
||||
/// response on <see cref="MultipleReadData"/>
|
||||
/// </summary>
|
||||
public const Byte MultipleReadDataReply = 0x0E;
|
||||
/// <summary>
|
||||
/// 9.2.13
|
||||
/// Command 0x0F (Multiple write data)
|
||||
/// This command makes writes to one register multiple times, which will be more efficient than performing successive reads using command 0x0B.
|
||||
/// This command will be available from protocol version 0.40, for earlier protocol versions command 0x0B should be used.
|
||||
/// </summary>
|
||||
public const Byte MultipleWriteData = 0x0F;
|
||||
/// <summary>
|
||||
/// response on <see cref="MultipleWriteData"/>
|
||||
/// </summary>
|
||||
public const Byte MultipleWriteDataReply = 0x10;
|
||||
/// <summary>
|
||||
/// 9.2.14
|
||||
/// Command 0x11 (Set level)
|
||||
/// This command will perform external driven data training, that is, where the external computer is in control of the data flow.
|
||||
/// This command will be available from protocol version 0.42, for earlier protocol versions command 0x05 should be used.
|
||||
/// </summary>
|
||||
public const Byte SetLevel = 0x11;
|
||||
/// <summary>
|
||||
/// response on <see cref="SetLevel"/>
|
||||
/// </summary>
|
||||
public const Byte SetLevelReply = 0x12;
|
||||
|
||||
//todo remove?
|
||||
//public const byte SetBitrateLsb = 0x03;
|
||||
//public const byte SetBitrateMsb = 0x05;
|
||||
//public const byte SetPacketSizesLsb = 0x04;
|
||||
//public const byte SetPacketSizesMsb = 0x05;
|
||||
//public const byte SetProtocolLsb = 0x02;
|
||||
//public const byte SetProtocolMsb = 0x05;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// Config Exchange error codes.
|
||||
/// </summary>
|
||||
public enum ConfigExErrors
|
||||
{
|
||||
#pragma warning disable CS1591 //as a matter of course
|
||||
Base = 0x04,
|
||||
LockedOut = 0x00,
|
||||
AuthenticationFail = 0x01,
|
||||
AccessDenied = 0x02,
|
||||
UnknownParameter = 0x03,
|
||||
InUse = 0x04,
|
||||
SizesDoesNotMatch = 0x05,
|
||||
CantReadCfgFile = 0x06,
|
||||
UserNotKnown = 0x07,
|
||||
CantCreateCfgFile = 0x08,
|
||||
StoringFailed = 0x09,
|
||||
StoreCorrupt = 0x0A,
|
||||
ExpectedWrite = 0x0B,
|
||||
ExpectedRead = 0x0C,
|
||||
StopCycling = 0x0D,
|
||||
TooManyOpen = 0x0E,
|
||||
NeverOpened = 0x0F,
|
||||
FileProtected = 0x1F,
|
||||
PartialRecall = 0x2F,
|
||||
DefaultPasswordUsed = 0x3F,
|
||||
#pragma warning restore
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// Transport errors for request protocol
|
||||
/// </summary>
|
||||
public enum HighLevelErrors
|
||||
{
|
||||
#pragma warning disable CS1591 //as a matter of course
|
||||
Base = 0x05,
|
||||
PayloadCount = 0x00,
|
||||
InvalidSubReason = 0x01,
|
||||
InvalidBaudrate = 0x02,
|
||||
InvalidBufferSize = 0x03,
|
||||
CrcFailure = 0x04,
|
||||
UnrecognizedCmd = 0x05,
|
||||
Framing = 0x06,
|
||||
Overflow = 0x07,
|
||||
PacketTimeout = 0x08,
|
||||
InvalidEscape = 0x09,
|
||||
UnknownParameter = 0x0A,
|
||||
TrainingFailed = 0x0B,
|
||||
NoBreak = 0x0C,
|
||||
#pragma warning restore
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// List of constance for error codes from genesis meter
|
||||
/// </summary>
|
||||
public static class LowLevelErrors
|
||||
{
|
||||
/// <summary>
|
||||
/// everything is fine
|
||||
/// </summary>
|
||||
public const Int16 NoError = 0;
|
||||
/// <summary>
|
||||
/// Port is not open
|
||||
/// </summary>
|
||||
public const Int16 SerialPortClosed = -2;
|
||||
/// <summary>
|
||||
/// fails to write to serial port
|
||||
/// </summary>
|
||||
public const Int16 SerialPortWriteFailure = -3;
|
||||
/// <summary>
|
||||
/// fails to read from serial port
|
||||
/// </summary>
|
||||
public const Int16 SerialPortReadFailure = -4;
|
||||
/// <summary>
|
||||
/// Command is to long
|
||||
/// </summary>
|
||||
public const Int16 MaxDataLength = -5;
|
||||
/// <summary>
|
||||
/// deeper exception, check out log if this happen
|
||||
/// </summary>
|
||||
public const Int16 Exception = -6;
|
||||
/// <summary>
|
||||
/// wrong CRC
|
||||
/// </summary>
|
||||
public const Int16 CrcCalcError = -7;
|
||||
/// <summary>
|
||||
/// something strange
|
||||
/// </summary>
|
||||
public const Int16 Unknown = -8;
|
||||
/// <summary>
|
||||
/// Timeout occur
|
||||
/// </summary>
|
||||
public const Int16 Timeout = -9;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts
|
||||
{
|
||||
/// <summary>
|
||||
/// Acknowledge feedback from meter after communication
|
||||
/// </summary>
|
||||
public enum RequestAcknowledgeState
|
||||
{
|
||||
/// <summary>
|
||||
/// Acknowledge code for unassigned command
|
||||
/// </summary>
|
||||
CommandNotAssigned,
|
||||
/// <summary>
|
||||
/// Will be used initially as the record is not sent
|
||||
/// </summary>
|
||||
Unsent,
|
||||
/// <summary>
|
||||
/// Response missing
|
||||
/// </summary>
|
||||
NoResponse,
|
||||
/// <summary>
|
||||
/// Response command not match the required command
|
||||
/// </summary>
|
||||
CommandError,
|
||||
/// <summary>
|
||||
/// Lost connection (logged out from meter), a re-authorization is required
|
||||
/// to access this command and/or register
|
||||
/// </summary>
|
||||
AuthorizationRequired,
|
||||
/// <summary>
|
||||
/// Meter error received, a retry may be useful
|
||||
/// </summary>
|
||||
MeterError,
|
||||
/// <summary>
|
||||
/// The response record couldn't be decoded
|
||||
/// </summary>
|
||||
DecodingError,
|
||||
/// <summary>
|
||||
/// The meter sent a wakeup message instead of the required data
|
||||
/// </summary>
|
||||
WakeupMessage,
|
||||
/// <summary>
|
||||
/// Valid meter response record
|
||||
/// </summary>
|
||||
Ok
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.EventArguments
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// if session is gone and a re authorization is necessary
|
||||
/// </summary>
|
||||
public class AuthorizationRequiredEventArgs : EventArgs
|
||||
{
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Ctor
|
||||
/// </summary>
|
||||
/// <param name="command">this command will be resend after authorization</param>
|
||||
public AuthorizationRequiredEventArgs(RequestRecord command = null)
|
||||
{
|
||||
Command = command;
|
||||
}
|
||||
/// <summary>
|
||||
/// Command to resend
|
||||
/// </summary>
|
||||
public RequestRecord Command;
|
||||
}
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds all errors can occur on the request protocol
|
||||
/// </summary>
|
||||
public class RequestProtocolException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Exception occurred on this command
|
||||
/// </summary>
|
||||
public Object Command;
|
||||
/// <summary>
|
||||
/// Exception occurred while receiving this data
|
||||
/// </summary>
|
||||
public Object ReceivedData;
|
||||
|
||||
/// <summary>
|
||||
/// Error is interpreted and has a define error code
|
||||
/// if its not null is Genesis.Protocols.Request.Const.ConfigExErrors
|
||||
/// </summary>
|
||||
public Int32? ConfigExErrorCode;
|
||||
/// <summary>
|
||||
/// Error is interpreted and has a define error code
|
||||
/// if its not null is Genesis.Protocols.Request.Const.HighLevelError
|
||||
/// </summary>
|
||||
public Int32? HighLevelErrorCode;
|
||||
|
||||
/// <summary>
|
||||
/// Ctor with only message
|
||||
/// </summary>
|
||||
/// <param name="message">error message</param>
|
||||
public RequestProtocolException(String message) : base(message)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Ctor with message and <see cref="Command"/>
|
||||
/// </summary>
|
||||
/// <param name="message">error message</param>
|
||||
/// <param name="command"><see cref="Command"/></param>
|
||||
public RequestProtocolException(String message, Object command) : base(message)
|
||||
{
|
||||
Command = command;
|
||||
ReceivedData = null;
|
||||
}
|
||||
/// <summary>
|
||||
/// Ctor with message, <see cref="Command"/> and <see cref="ReceivedData"/>
|
||||
/// </summary>
|
||||
/// <param name="message">error message</param>
|
||||
/// <param name="command"><see cref="Command"/></param>
|
||||
/// <param name="receivedData"><see cref="ReceivedData"/> </param>
|
||||
public RequestProtocolException(String message, Object command, Object receivedData) : base(message)
|
||||
{
|
||||
Command = command;
|
||||
ReceivedData = receivedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
+767
@@ -0,0 +1,767 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using log4net;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
|
||||
|
||||
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol
|
||||
{
|
||||
/// <summary>
|
||||
/// a bidirectional protocol support read and write genesis meter registers
|
||||
/// needed for
|
||||
/// </summary>
|
||||
public class RequestProtocol : BaseProtocol
|
||||
{
|
||||
//All read and write data sets (payload size) are organized in 4 byte chunks
|
||||
|
||||
//Format of RequestProtocol read data
|
||||
//Request CMD | Register LSB | Register MSB | 0x00 | 0x00 | 0x00 | 0x00
|
||||
//Response CMD | ErrCode Reason | ErrCode Base | DATA0 | DATA1 | DATA2 | DATA3
|
||||
|
||||
//Format of RequestProtocol write data
|
||||
//Request CMD | Register LSB | Register MSB | DATA0 | DATA1 | DATA2 | DATA3
|
||||
//Response CMD | ErrCode Reason | ErrCode Base | UNDEF0 | UNDEF1 | UNDEF2 | UNDEF3
|
||||
|
||||
//Format of RequestProtocol multiple read data
|
||||
//Request CMD | Register LSB | Register MSB | RequestChunks LSB | RequestChunks MSB
|
||||
//Response CMD | ErrCode Reason | ErrCode Base | ErrPosition LSB | ErrPosition MSB |
|
||||
//DATA0 | DATA1 | DATA2 | DATA3.... (repeat for number of chunks)
|
||||
|
||||
//Format of RequestProtocol multiple read data
|
||||
//Request CMD | Register LSB | Register MSB | WriteChunks LSB | WriteChunks MSB |
|
||||
//DATA0 | DATA1 | DATA2 | DATA3.... (repeat for number of chunks)
|
||||
//Response CMD | ErrCode Reason | ErrCode Base | ErrPosition LSB | ErrPosition MSB
|
||||
|
||||
//Indexes of the request protocol read/write data
|
||||
//private const Int32 RequestCommandIndex = 0;
|
||||
//private const Int32 RequestRegisterIndex = 1;
|
||||
//private const Int32 RequestDataIndex = 3;
|
||||
|
||||
//Indexes for response protocol read/write data
|
||||
private const Int32 ResponseCommandIndex = 0;
|
||||
private const Int32 ResponseErrorCodeIndex = 1;
|
||||
private const Int32 ResponseDataIndex = 3;
|
||||
|
||||
//Indexes of the request protocol multiple read/write data
|
||||
//private const Int32 RequestChunksIndex = 3;
|
||||
//private const Int32 RequestMultiDataIndex = 5;
|
||||
|
||||
//Indexes of the response protocol multiple read/write data
|
||||
private const Int32 ResponseMultiErrorPosition = 3;
|
||||
private const Int32 ResponseMultiDataIndex = 5;
|
||||
|
||||
//Indexes of error code
|
||||
private const Int32 ErrorCodeReasonIndex = 0;
|
||||
private const Int32 ErrorCodeBaseIndex = 1;
|
||||
|
||||
//Multiple response header size is 1 byte command 2 bytes error code and 2 bytes error position
|
||||
private const Int32 ResponseMultiHeaderSize = 5;
|
||||
|
||||
//fill byte
|
||||
private const Byte FillByte = 0x00;
|
||||
|
||||
private static readonly ILog _logger = LogManager.GetLogger(typeof(RequestProtocol));
|
||||
|
||||
// wakeup from register (water meter) to adapter
|
||||
private static readonly Byte[] WakeupMessage = { 0x00, 0xFF, 0xFF };
|
||||
|
||||
/// <summary>
|
||||
/// FIFO of records to be send next
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<RequestRecord> _recordsSendFifo = new ConcurrentQueue<RequestRecord>();
|
||||
|
||||
/// <summary>
|
||||
/// This is the actual record in the send loop
|
||||
/// </summary>
|
||||
private RequestRecord _recordInProcess;
|
||||
|
||||
/// <summary>
|
||||
/// Last communication time for session refresh
|
||||
/// </summary>
|
||||
public DateTimeOffset? LastCommTime;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override event EventHandler<BasePortDataEventArgs> OnRecordReadyToSend;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a meter Response for write password is good
|
||||
/// </summary>
|
||||
public event EventHandler OnAuthorizationGrant;
|
||||
|
||||
/// <summary>
|
||||
/// Event after a register entries changed
|
||||
/// </summary>
|
||||
public event EventHandler<RegisterUpdatedEventArgs> OnMeterRegisterUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// User adjustable additional retry timeout. This is 0 ms for standard operation.
|
||||
/// </summary>
|
||||
public Int32 AdditionalRetryTimeoutMs;
|
||||
|
||||
private Int32 _maxTimeOutMs;
|
||||
|
||||
private readonly String _ident;
|
||||
|
||||
/// <inheritdoc />
|
||||
public RequestProtocol(String ident) : base(ident)
|
||||
{
|
||||
_ident = ident;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process all records needed to be sent, this routine has to be called
|
||||
/// to kick-off the communication of all RequestRecords saved to the send
|
||||
/// FIFO <see cref="AddRecordToSendFifo"/>
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Dec-07" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
|
||||
/// - Logging of raw RequestProtocol at time of sending,
|
||||
/// - Logging of retries.
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-12" author="T.Wiedebusch">
|
||||
/// - Added timeout from transmit protocol.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jan-02" author="Drabesch">
|
||||
/// - Added reorder FIFO to bring login at first position
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-06" author="T.Wiedebusch">
|
||||
/// - Retry counter reset if authorization required and this record will be put back into FIFO,
|
||||
/// - Retry delay corrected for all errors.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-12" author="T.Wiedebusch">
|
||||
/// - Dynamic retry delay: ResponseTimeoutMs * Retry counter.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
|
||||
/// - Skip retries on specific error mask to speed up communication on functional errors
|
||||
/// or informational feed backs (e.g FW not installed 0x0004)
|
||||
/// <see cref="RequestRecord.ResponseErrorCode"/>
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Aug-29" author="T.Wiedebusch">
|
||||
/// - User adjustable additional retry timeout.
|
||||
/// </remarks>
|
||||
/// <remarks date="2020-Jan-17/18" author="T.Wiedebusch">
|
||||
/// - Response timeout message output,
|
||||
/// - Initialize acknowledge code before communication to NoResponse.
|
||||
/// </remarks>
|
||||
/// <remarks date="2020-Jan-22" author="T.Wiedebusch">
|
||||
/// - Response timeout deviated from system time.
|
||||
/// </remarks>
|
||||
/// <remarks date="2021-Feb-02" author="T.Wiedebusch">
|
||||
/// - Avoid enqueue of _recordInProcess if retry counter is 0.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Nov-04" author="T.Wiedebusch">
|
||||
/// - Additional DEBUG information included about FIFO and loops.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Nov-07" author="T.Wiedebusch">
|
||||
/// - Command not assigned response for recordInProcess == null.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Apr-22" author="T.Wiedebusch">
|
||||
/// - Initial request acknowledge state changed from NotDecoded to NoResponse.
|
||||
/// </remarks>
|
||||
public RequestAcknowledgeState ProcessRecordList()
|
||||
{
|
||||
_logger.Debug($"{_ident} Entry to ProcessRecordList, FIFO contains ({_recordsSendFifo.Count}) records");
|
||||
|
||||
//The inter record send delay has to be hold before trying to communicate again
|
||||
const Int32 interRecordSendDelayMs = CommunicationConfig.InterRecordSendDelayMs;
|
||||
|
||||
//remind counter for FIFO and therefore execution loops
|
||||
var loopCounter = 0;
|
||||
|
||||
//check if FIFO is empty and get the actual record to process out of it
|
||||
while (_recordsSendFifo.TryDequeue(out var internalRecord))
|
||||
{
|
||||
// Starting with record number 1
|
||||
loopCounter++;
|
||||
_logger.Debug($"{_ident} Record({loopCounter}) - Dequeued from FIFO");
|
||||
|
||||
//pointer to new record
|
||||
_recordInProcess = internalRecord;
|
||||
if (_recordInProcess == null)
|
||||
return RequestAcknowledgeState.CommandNotAssigned;
|
||||
|
||||
do
|
||||
{
|
||||
//mark record as answer outstanding
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.NoResponse;
|
||||
|
||||
_logger.Debug($"{_ident} Record({loopCounter}) - Processing");
|
||||
//log retries
|
||||
if (_recordInProcess.RetryCtr > 0)
|
||||
{
|
||||
_logger.Debug($"{_ident} Retry({_recordInProcess.RetryCtr})");
|
||||
_logger.Debug($"{_ident} Record({loopCounter}) - Retry({_recordInProcess.RetryCtr})");
|
||||
}
|
||||
|
||||
//log request protocol content
|
||||
_logger.Debug(_recordInProcess.HideDataInLog
|
||||
? $"{_ident} SentData(*****)"
|
||||
: $"{_ident} SentData({BitConverter.ToString(_recordInProcess.RequestProtocolData.ToArray())})");
|
||||
|
||||
//remind time for keep-session-active test to deny automatic logout of meter
|
||||
LastCommTime = DateTimeOffset.UtcNow;
|
||||
|
||||
//set timeout for one communication trial adding the transmit protocol specific timeout and
|
||||
//increase the timeout with each retry
|
||||
_maxTimeOutMs = _recordInProcess.ResponseTimeoutMs + AdditionalRetryTimeoutMs +
|
||||
CommunicationConfig.ResponseTimeoutMs * (_recordInProcess.RetryCtr + 1);
|
||||
//start initial communication or retry
|
||||
OnRecordReadyToSend?.Invoke(this, new ListBytePortDataEventArgs(_recordInProcess.EncodedRequestData));
|
||||
|
||||
//time reminder of request record
|
||||
var requestTimeUtc = DateTimeOffset.UtcNow;
|
||||
Int32 actualResponseWaitTimeMs;
|
||||
|
||||
//wait for communication acknowledge or until timeout, this also handles the inter record send delay
|
||||
do
|
||||
{
|
||||
//minimum delay is the inter record send delay
|
||||
Thread.Sleep(interRecordSendDelayMs);
|
||||
var actualTimeUtc = DateTimeOffset.UtcNow;
|
||||
//actual time difference from request to now
|
||||
var timeSpan = actualTimeUtc - requestTimeUtc;
|
||||
//avoid total milliseconds below zero at time overflow
|
||||
if (timeSpan.TotalMilliseconds < 0)
|
||||
{
|
||||
requestTimeUtc = DateTimeOffset.UtcNow;
|
||||
}
|
||||
actualResponseWaitTimeMs = (Int32)timeSpan.TotalMilliseconds;
|
||||
|
||||
} while (_recordInProcess.Acknowledge != RequestAcknowledgeState.Ok &&
|
||||
_recordInProcess.SkipRetryErrorCode != _recordInProcess.ResponseErrorCode &&
|
||||
_maxTimeOutMs > actualResponseWaitTimeMs);
|
||||
|
||||
//if timeout value reaches zero, the response hasn't been received or the delay until
|
||||
//next communication needed to be hold
|
||||
if (_maxTimeOutMs <= actualResponseWaitTimeMs)
|
||||
{
|
||||
_logger.Error(_recordInProcess.Acknowledge == RequestAcknowledgeState.NoResponse
|
||||
? $"{_ident} Response timeout({actualResponseWaitTimeMs}ms)"
|
||||
: $"{_ident} Communication delay({actualResponseWaitTimeMs}ms)");
|
||||
}
|
||||
|
||||
//skip loop to handle re-authorization
|
||||
if (_recordInProcess.Acknowledge != RequestAcknowledgeState.AuthorizationRequired)
|
||||
continue;
|
||||
|
||||
//reset retry counter for this record, it will be dispatched after re-authorization
|
||||
_recordInProcess.RetryCtr = 0;
|
||||
|
||||
if (CommunicationConfig.RequestRetries > 0)
|
||||
{
|
||||
//add the recordInProcess to the top of the FIFO
|
||||
_recordsSendFifo.Enqueue(_recordInProcess);
|
||||
}
|
||||
//call login
|
||||
return _recordInProcess.Acknowledge;
|
||||
|
||||
} while (_recordInProcess.Acknowledge != RequestAcknowledgeState.Ok &&
|
||||
_recordInProcess.RetryCtr++ < CommunicationConfig.RequestRetries &&
|
||||
_recordInProcess.SkipRetryErrorCode != _recordInProcess.ResponseErrorCode);
|
||||
|
||||
}
|
||||
|
||||
return _recordInProcess.Acknowledge;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assemble record with transmit protocol and put it to record-send-FIFO,
|
||||
/// backup the ready-to-send, which is the dataEncodedWithTransmitProtocol,
|
||||
/// to the RequestRecord object for sending including the retry capability.
|
||||
/// </summary>
|
||||
/// <param name="payload">Data package with all details like CRC, etc</param>
|
||||
/// <param name="cmd">Command identifier <see cref="Commands" /></param>
|
||||
/// <param name="register"><see cref="RegisterDefinition" /> which is intend</param>
|
||||
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
|
||||
/// <param name="skipRetryErrorCode">error mask to skip retries</param>
|
||||
/// <returns>the assembled record for the send FIFO</returns>
|
||||
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
|
||||
/// - Logging of raw data (RequestProtocol) moved to ProcessRecordList.
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-12" author="T.Wiedebusch">
|
||||
/// - Added timeout from transmit protocol.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
|
||||
/// - Skip retries on specific error mask to speed up communication on functional errors
|
||||
/// or informational feed backs (e.g FW not installed 0x0004)
|
||||
/// <see cref="RequestRecord.ResponseErrorCode"/>
|
||||
/// </remarks>
|
||||
/// <remarks date="2020-Jan-18" author="T.Wiedebusch">
|
||||
/// - Initial skip retry error code set to 0x0004 (e.g FW not installed 0x0004).
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Mai-16" author="T.Wiedebusch">
|
||||
/// - Hide data in log forwarded to DecodeDateForPhysicalLayer to hide passwords in log files.
|
||||
/// </remarks>
|
||||
private RequestRecord AddRecordToSendFifo(Byte cmd, Byte[] payload, RegisterDefinition register = null,
|
||||
Boolean hideDataInLog = false, UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
{
|
||||
var requestProtocolData = new List<Byte> { cmd };
|
||||
requestProtocolData.AddRange(payload);
|
||||
|
||||
//encode request protocol data with transmit protocol
|
||||
var transmit = GetTransmitProtocol();
|
||||
var dataEncodedWithTransmitProtocol = transmit.DecodeDataForPhysicalLayer(_ident, cmd, payload, hideDataInLog);
|
||||
|
||||
//remind port specific response timeout
|
||||
var transmitPortSettings = transmit.GetTransmitPortSettings();
|
||||
|
||||
var recordForSendFifo = new RequestRecord(cmd, requestProtocolData, dataEncodedWithTransmitProtocol,
|
||||
transmitPortSettings.ResponseTimeoutMs, register, hideDataInLog, skipRetryErrorCode);
|
||||
|
||||
_recordsSendFifo.Enqueue(recordForSendFifo);
|
||||
|
||||
return recordForSendFifo;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Record dispatcher to send FIFO of UI1236 command
|
||||
/// </summary>
|
||||
/// <remarks date="2022-Aug-24" author="R.Drabesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <param name="ident">string for logging of slot</param>
|
||||
/// <param name="payload">Data package with all details like CRC, etc</param>
|
||||
/// <param name="register"><see cref="RegisterDefinition" /> which is intend</param>
|
||||
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
|
||||
/// <param name="skipRetryErrorCode">error mask to skip retries</param>
|
||||
/// <returns>the assembled record for the send FIFO</returns>
|
||||
// ReSharper disable once InconsistentNaming UI1236 is a naming forced by the caller
|
||||
public RequestRecord AddRecordToSendFifoUI1236(String ident, Byte[] payload, RegisterDefinition register = null,
|
||||
Boolean hideDataInLog = false, UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
{
|
||||
const Byte maxValue = byte.MaxValue;
|
||||
var requestProtocolData = new List<Byte>
|
||||
{
|
||||
maxValue
|
||||
};
|
||||
requestProtocolData.AddRange(payload);
|
||||
var transmitProtocol = GetTransmitProtocol();
|
||||
var encodedRequestData = transmitProtocol.DecodeDataForPhysicalLayerUI1236(ident, payload, hideDataInLog);
|
||||
var transmitPortSettings = transmitProtocol.GetTransmitPortSettings();
|
||||
var sendFifoUi1236 = new RequestRecord(maxValue, requestProtocolData, encodedRequestData,
|
||||
transmitPortSettings.ResponseTimeoutMs, register, hideDataInLog, skipRetryErrorCode);
|
||||
_recordsSendFifo.Enqueue(sendFifoUi1236);
|
||||
return sendFifoUi1236;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called after successful response.
|
||||
/// Decode and check record, handle Errors and dispatch result.
|
||||
/// Invokes <see cref="OnRecordIsDecoded" />if somebody is listening
|
||||
/// </summary>
|
||||
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
|
||||
/// - First part reworked to extract the response protocol information
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
|
||||
/// - Error code extraction changed for <see cref="RequestRecord.SkipRetryErrorCode"/>
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Dec-12/16" author="T.Wiedebusch">
|
||||
/// - Wakeup-message will avoid further timeout (during FW update this is in the range
|
||||
/// of 15000ms).
|
||||
/// - Allow one additional retry on decoding error, which is often the wakeup-message.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jan-17" author="T.Wiedebusch">
|
||||
/// - Send time reminder for timeout time calculation as output in log-file,
|
||||
/// - OnRecordIsDecoded?.Invoke moved before return to assure that the Acknowledge status is set.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jan-22" author="T.Wiedebusch">
|
||||
/// - Avoid activation of wakeup retry if record is meanwhile acknowledged.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Jul-19" author="T.Wiedebusch">
|
||||
/// - Wakeup message handling changed,
|
||||
/// - Multiple replies on wakeup message allowed.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Oct-02" author="T.Wiedebusch">
|
||||
/// - On wakeup message 5 retires are allowed to avoid an infinite loop.
|
||||
/// </remarks>
|
||||
/// <remarks date="2022-Oct-04" author="T.Wiedebusch">
|
||||
/// - On wakeup message exit this routine.
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Feb-13" author="T.Wiedebusch">
|
||||
/// - Early exit on _recordInProcess == null,
|
||||
/// - Wakeup message retries from 5 to 2,
|
||||
/// - Removed error base from error code decision as error base is only the AppId
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Sep-01" author="T.Wiedebusch">
|
||||
/// - HideDataInLog.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Jan-24" author="T.Wiedebusch">
|
||||
/// - Ignore wakeup if message already acknowledged (avoid to set
|
||||
/// "_recordInProcess.Acknowledge = RequestAcknowledgeState.NotDecoded"),
|
||||
/// - Avoid to overwrite "_recordInProcess.Acknowledge = RequestAcknowledgeState.Acknowledge" with
|
||||
/// "RequestAcknowledgeState.WakeupMessage".
|
||||
/// </remarks>
|
||||
protected override void DecodeRecord(IPortDataEventArgs data)
|
||||
{
|
||||
if (_recordInProcess == null)
|
||||
return;
|
||||
//the request response record covers the entire request protocol
|
||||
var responseRecord = GetTransmitProtocol().DecodeDataForLogicLayer(_ident, (List<Byte>)data.GetData(),
|
||||
_recordInProcess.HideDataInLog);
|
||||
|
||||
//on protocol decoding failure
|
||||
if (responseRecord.Count == 0)
|
||||
{
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.DecodingError;
|
||||
_logger.Debug($"{_ident} Message decoding error.");
|
||||
//reset timeout to a normal value if timeout is extremely high but response received
|
||||
if (_maxTimeOutMs > CommunicationConfig.BusyTimeoutMs)
|
||||
_maxTimeOutMs = CommunicationConfig.BusyTimeoutMs;
|
||||
return;
|
||||
}
|
||||
|
||||
if (responseRecord.Count == WakeupMessage.Length)
|
||||
{
|
||||
// check for wakeup message
|
||||
var wakeUp = true;
|
||||
for (var i = 0; i < WakeupMessage.Length; i++)
|
||||
{
|
||||
if (responseRecord[i] != WakeupMessage[i])
|
||||
wakeUp = false;
|
||||
}
|
||||
|
||||
if (wakeUp)
|
||||
{
|
||||
//avoid wakeup retry on acknowledged record
|
||||
if (RequestAcknowledgeState.Ok != _recordInProcess.Acknowledge)
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.WakeupMessage;
|
||||
//initiate a single retry on wakeup message response
|
||||
if (_recordInProcess.WakeupMessageRetryCtr < 2)
|
||||
{
|
||||
_recordInProcess.WakeupMessageRetryCtr++;
|
||||
_logger.Debug(
|
||||
$"{_ident} Wakeup message({_recordInProcess.WakeupMessageRetryCtr}) received");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//if it is not identified as valid wakeup message it is something unknown
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.DecodingError;
|
||||
_logger.Debug($"{_ident} Message decoding error.");
|
||||
}
|
||||
|
||||
// reset retry counter to get all required retries
|
||||
_recordInProcess.RetryCtr = -1;
|
||||
//reset timeout to a normal value if timeout is extremely high but response received
|
||||
if (_maxTimeOutMs > CommunicationConfig.BusyTimeoutMs)
|
||||
_maxTimeOutMs = CommunicationConfig.BusyTimeoutMs;
|
||||
return;
|
||||
}
|
||||
|
||||
//log request protocol content
|
||||
_logger.Debug(_recordInProcess.HideDataInLog
|
||||
? $"{_ident} DecodedRecord(*****)"
|
||||
: $"{_ident} DecodedRecord({BitConverter.ToString(responseRecord.ToArray())})");
|
||||
|
||||
//extract information command
|
||||
var replyCmd = responseRecord[ResponseCommandIndex];
|
||||
|
||||
//extract position of error for multiple access
|
||||
if (replyCmd == Commands.MultipleReadDataReply || replyCmd == Commands.MultipleWriteDataReply)
|
||||
{
|
||||
_recordInProcess.ResponseErrorPosition =
|
||||
(UInt16)((responseRecord[ResponseMultiErrorPosition] & 0x00FF) |
|
||||
(UInt16)((responseRecord[ResponseMultiErrorPosition + 1] << 8) & 0xFF00));
|
||||
}
|
||||
|
||||
//extract the error codes from response record to _record in process
|
||||
_recordInProcess.ResponseErrorBase = responseRecord[ResponseErrorCodeIndex + ErrorCodeBaseIndex];
|
||||
_recordInProcess.ResponseErrorReason = responseRecord[ResponseErrorCodeIndex + ErrorCodeReasonIndex];
|
||||
//combine error base and error reason
|
||||
_recordInProcess.ResponseErrorCode = (UInt16)(_recordInProcess.ResponseErrorReason |
|
||||
(_recordInProcess.ResponseErrorBase << 8));
|
||||
|
||||
_recordInProcess.ResponsePayload = new List<Byte>();
|
||||
switch (replyCmd)
|
||||
{
|
||||
case Commands.MultipleReadDataReply:
|
||||
_recordInProcess.ResponsePayload.AddRange(
|
||||
responseRecord.GetRange(ResponseMultiDataIndex,
|
||||
responseRecord.Count - ResponseMultiHeaderSize));
|
||||
break;
|
||||
case Commands.ReadDataReply:
|
||||
_recordInProcess.ResponsePayload.AddRange(responseRecord.GetRange(ResponseDataIndex,
|
||||
RegisterDefinition.ChunkSize));
|
||||
break;
|
||||
}
|
||||
|
||||
//Deny access if reply does not match
|
||||
if (_recordInProcess.ResponseCommand != replyCmd)
|
||||
{
|
||||
_logger.Fatal($"{_ident} Wrong command received({replyCmd:X2})," +
|
||||
$" expected command({_recordInProcess.ResponseCommand:X2})");
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.CommandError;
|
||||
OnRecordIsDecoded?.Invoke(this,
|
||||
new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
|
||||
return;
|
||||
}
|
||||
|
||||
//examine error code, if base (AppId) is not 0 the reason 0 may be an error!!!!
|
||||
if (/*_recordInProcess.ResponseErrorBase != 0 ||*/ _recordInProcess.ResponseErrorReason != 0)
|
||||
{
|
||||
_logger.Warn($"{_ident} Error code received(0x{_recordInProcess.ResponseErrorBase:X2}" +
|
||||
$"{_recordInProcess.ResponseErrorReason:X2})");
|
||||
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.MeterError;
|
||||
CheckErrorCode();
|
||||
OnRecordIsDecoded?.Invoke(this,
|
||||
new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
|
||||
return;
|
||||
}
|
||||
|
||||
//register password acknowledge
|
||||
if (_recordInProcess.Register.GetIdent() == Register.Configexchange.Password)
|
||||
{
|
||||
OnAuthorizationGrant?.Invoke(null, null);
|
||||
}
|
||||
|
||||
//dispatch data
|
||||
if (_recordInProcess.ResponsePayload != null)
|
||||
{
|
||||
OnMeterRegisterUpdated?.Invoke(this, new RegisterUpdatedEventArgs(_recordInProcess.Register,
|
||||
_recordInProcess.ResponsePayload.ToArray()));
|
||||
}
|
||||
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.Ok;
|
||||
OnRecordIsDecoded?.Invoke(this,
|
||||
new RequestResponseDataEventArgs { RequestResponseData = responseRecord });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to send basic Commands to Meter. Creates an integer of 4 bytes for the payload.
|
||||
/// Supported Commands are: <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
|
||||
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
|
||||
/// Calculate CRCs and Command length and check if command is valid.
|
||||
/// Use <see cref="AddRecordToSendFifo" /> to push data to Port/Meter.
|
||||
/// </summary>
|
||||
/// <param name="command">
|
||||
/// Supported <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />,
|
||||
/// <see cref="Commands.WriteData" />, <see cref="Commands.MultipleWriteData" /> and <see cref="Commands.QueryCaps" />.
|
||||
/// </param>
|
||||
/// <param name="meterRegister">Register to Read or Write, null for <see cref="Commands.QueryCaps" /></param>
|
||||
/// <param name="payload">
|
||||
/// Data to push into the <see cref="RegisterDefinition" />.
|
||||
/// must be null on Read Commands ( <see cref="Commands.ReadData" />,<see cref="Commands.MultipleReadData" />)
|
||||
/// and not null on WriteData (<see cref="Commands.WriteData" /> and <see cref="Commands.MultipleWriteData" />)
|
||||
/// </param>
|
||||
/// <param name="expectedLength"></param>
|
||||
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
|
||||
/// <param name="skipRetryErrorCode">mask to skip reties on error</param>
|
||||
/// <returns>an new command just send to port/meter </returns>
|
||||
/// <remarks date="2018-Mar-15" author="R.Drahbesch">
|
||||
/// - Initial
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-08" author="T.Wiedebusch">
|
||||
/// - Reworked to zero pad payload with chunks of 4 bytes, the caller needn't take care of the size
|
||||
/// </remarks>
|
||||
/// <remarks date="2018-Dec-09" author="T.Wiedebusch">
|
||||
/// - Corrected payload content in request protocol
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Apr-11" author="T.Wiedebusch">
|
||||
/// - Hide data in logging for e.g. passwords
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jun-28" author="T.Wiedebusch">
|
||||
/// - Skip retries on specific error mask to speed up communication on functional errors
|
||||
/// or informational feed backs (e.g FW not installed 0x0004)
|
||||
/// <see cref="RequestRecord.ResponseErrorCode"/>
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Aug-28" author="R.Drabesch">
|
||||
/// - Multiple read for string split to simple reads.
|
||||
/// </remarks>
|
||||
/// <remarks date="2019-Jan-18" author="T.Wiedebusch">
|
||||
/// - Default <see cref="CommunicationConfig.SkipRetryErrorCode"/> set.
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Sep-06" author="T.Wiedebusch">
|
||||
/// - MultipleReadData based on data size.
|
||||
/// </remarks>
|
||||
/// <remarks date="2023-Nov-24" author="T.Wiedebusch">
|
||||
/// - Exit multiple read date if retries exceeded.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Apr-11" author="T.Wiedebusch">
|
||||
/// - Exit multiple read date if retries exceeded increased to <see cref="CommunicationConfig.MaxRequestRetries"/>.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Apr-11 Version 2" author="T.Wiedebusch">
|
||||
/// - Rewound to version from 06.09.2023 14:44:33 before Commit 8c9d7704.
|
||||
/// </remarks>
|
||||
/// <remarks date="2024-Apr-15" author="T.Wiedebusch">
|
||||
/// - Removed useless too short comments as every string is going to be delimited by a 0 and usually won't match
|
||||
/// to a 4 byte chunk.
|
||||
/// </remarks>
|
||||
/// <remarks date="2026-Jan-06" author="T.Wiedebusch">
|
||||
/// - Removed redundant 'return cRecord'.
|
||||
/// </remarks>
|
||||
public RequestRecord CommandToMeter(Byte command, RegisterDefinition meterRegister = null,
|
||||
Byte[] payload = null, Int32? expectedLength = null, Boolean hideDataInLog = false,
|
||||
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
{
|
||||
if (command == Commands.MultipleReadData && meterRegister != null && meterRegister.DataType == typeof(String))
|
||||
{
|
||||
var completeResponse = new List<Byte>();
|
||||
while (true)
|
||||
{
|
||||
var cRecord = CommandToMeter(Commands.ReadData, meterRegister, payload, expectedLength,
|
||||
hideDataInLog, skipRetryErrorCode);
|
||||
var rest = ProcessRecordList();
|
||||
if (rest == RequestAcknowledgeState.Ok && cRecord?.ResponsePayload != null)
|
||||
{
|
||||
completeResponse.AddRange(cRecord.ResponsePayload);
|
||||
if (!cRecord.ResponsePayload.Contains(0)) continue;
|
||||
|
||||
cRecord.ResponsePayload = completeResponse;
|
||||
OnMeterRegisterUpdated?.Invoke(this,
|
||||
new RegisterUpdatedEventArgs(meterRegister, completeResponse.ToArray()));
|
||||
}
|
||||
|
||||
return cRecord;
|
||||
}
|
||||
}
|
||||
|
||||
//the minimum payload is one 4 byte chunk
|
||||
var chunkSize = RegisterDefinition.ChunkSize;
|
||||
if (payload == null)
|
||||
{
|
||||
payload = new Byte[chunkSize];
|
||||
for (var i = 0; i < chunkSize; i++)
|
||||
{
|
||||
payload[i] = FillByte;
|
||||
}
|
||||
}
|
||||
|
||||
//real payload based on 4 byte chunks
|
||||
var requestPayload = new List<Byte>();
|
||||
requestPayload.AddRange(payload);
|
||||
//fill-byte padding
|
||||
if (payload.Length % chunkSize != 0)
|
||||
{
|
||||
for (var i = 0; i < chunkSize - payload.Length % chunkSize; i++)
|
||||
{
|
||||
requestPayload.Add(FillByte);
|
||||
}
|
||||
}
|
||||
|
||||
//request protocol excluding the command
|
||||
var requestProtocol = new List<Byte>();
|
||||
|
||||
if (meterRegister != null)
|
||||
{
|
||||
requestProtocol.Add(meterRegister.RegisterAddress[1]);
|
||||
requestProtocol.Add(meterRegister.RegisterAddress[0]);
|
||||
|
||||
if (command == Commands.MultipleWriteData)
|
||||
{
|
||||
//set counter to inform meter writes are expected
|
||||
//the payload size is already padded to 4 byte chunks
|
||||
var numberOfChunks = (UInt16)(requestPayload.Count / chunkSize);
|
||||
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
|
||||
requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
if (command == Commands.MultipleReadData)
|
||||
{
|
||||
//TODO THW create expected size out of meterRegister.DataType
|
||||
if (!expectedLength.HasValue)
|
||||
{
|
||||
if ((meterRegister.DataType == typeof(UInt64) || meterRegister.DataType == typeof(Int64)))
|
||||
{
|
||||
expectedLength = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
expectedLength = 24;
|
||||
}
|
||||
}
|
||||
|
||||
var numberOfChunks = (UInt16)(expectedLength.Value / chunkSize);
|
||||
if (expectedLength % chunkSize != 0)
|
||||
numberOfChunks++;
|
||||
requestProtocol.Add((Byte)(numberOfChunks & 0xFF));
|
||||
requestProtocol.Add((Byte)((numberOfChunks >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
//add always the payload to the request protocol
|
||||
requestProtocol.AddRange(requestPayload);
|
||||
}
|
||||
|
||||
return AddRecordToSendFifo(command, requestProtocol.ToArray(), meterRegister,
|
||||
hideDataInLog, skipRetryErrorCode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analyzes the error code
|
||||
/// </summary>
|
||||
private void CheckErrorCode()
|
||||
{
|
||||
if (_recordInProcess.ResponseErrorBase == (Byte)ConfigExErrors.Base)
|
||||
{
|
||||
if (_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.AuthenticationFail ||
|
||||
_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.AccessDenied ||
|
||||
_recordInProcess.ResponseErrorReason == (Byte)ConfigExErrors.LockedOut)
|
||||
{
|
||||
_recordInProcess.Acknowledge = RequestAcknowledgeState.AuthorizationRequired;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reorders the record list so that first entry is the defined topRegister
|
||||
/// </summary>
|
||||
/// <param name="topRegister"></param>
|
||||
public void ReorderRecordList(String topRegister)
|
||||
{
|
||||
_recordsSendFifo.Enqueue(_recordInProcess);
|
||||
//reorder FIFO to bring login at first position
|
||||
for (var i = 0; i < _recordsSendFifo.Count; i++)
|
||||
{
|
||||
_recordsSendFifo.TryPeek(out var tmpRecord);
|
||||
if (tmpRecord == null || tmpRecord.Register.GetIdent() == topRegister)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_recordsSendFifo.TryDequeue(out tmpRecord);
|
||||
_recordsSendFifo.Enqueue(tmpRecord);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the active register to access
|
||||
/// </summary>
|
||||
/// <param name="registerName"></param>
|
||||
/// <returns></returns>
|
||||
public Boolean ContainsRegisterIdent(String registerName)
|
||||
{
|
||||
return _recordsSendFifo.Any(a => string.Equals(a.Register.GetIdent(),
|
||||
registerName, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol.Consts;
|
||||
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers;
|
||||
using CommunicationConfig = TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.GenesisConfig.CommunicationConfig;
|
||||
|
||||
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.RequestProtocol
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds request commands with detail parameters to see processing state
|
||||
/// </summary>
|
||||
public class RequestRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates the base command <see cref="Commands" />
|
||||
/// </summary>
|
||||
public readonly Byte ResponseCommand;
|
||||
|
||||
/// <summary>
|
||||
/// the register the command refers to.
|
||||
/// needed to set Register dictionary to link response with dictionary key
|
||||
/// </summary>
|
||||
public readonly RegisterDefinition Register;
|
||||
|
||||
/// <summary>
|
||||
/// Command acknowledged
|
||||
/// </summary>
|
||||
public RequestAcknowledgeState Acknowledge = RequestAcknowledgeState.Unsent;
|
||||
|
||||
/// <summary>
|
||||
/// Retry counter
|
||||
/// </summary>
|
||||
public Int32 RetryCtr = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Wakeup-message retry counter
|
||||
/// </summary>
|
||||
public Int32 WakeupMessageRetryCtr = 0;
|
||||
|
||||
/// <summary>
|
||||
/// indicates error base on <see cref="RequestRecord"/>
|
||||
/// </summary>
|
||||
public Byte ResponseErrorBase = 0xFF;
|
||||
|
||||
/// <summary>
|
||||
/// indicates error reason on <see cref="RequestRecord"/>
|
||||
/// </summary>
|
||||
public Byte ResponseErrorReason = 0xFF;
|
||||
|
||||
/// <summary>
|
||||
/// Combined error code of base and reason
|
||||
/// </summary>
|
||||
public UInt16 ResponseErrorCode = 0xFFFF;
|
||||
|
||||
/// <summary>
|
||||
/// Chunk position of error on multiple read/write access
|
||||
/// </summary>
|
||||
public UInt16 ResponseErrorPosition = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Data containing the request protocol
|
||||
/// </summary>
|
||||
public readonly List<Byte> RequestProtocolData;
|
||||
|
||||
/// <summary>
|
||||
/// Encoded with transmit protocol, ready to stream to port as is
|
||||
/// </summary>
|
||||
public List<Byte> EncodedRequestData { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Extracted payload of response
|
||||
/// </summary>
|
||||
public List<Byte> ResponsePayload;
|
||||
|
||||
/// <summary>
|
||||
/// Extracted payload of response
|
||||
/// </summary>
|
||||
public readonly Int32 ResponseTimeoutMs;
|
||||
|
||||
/// <summary>
|
||||
/// avoid logging for e.g. password
|
||||
/// </summary>
|
||||
public readonly Boolean HideDataInLog;
|
||||
|
||||
/// <summary>
|
||||
/// error mask to skip retries for functional errors <see cref="ResponseErrorCode"/>
|
||||
/// </summary>
|
||||
public readonly UInt16 SkipRetryErrorCode;
|
||||
|
||||
/// <summary>
|
||||
/// Ctor for an base command <see cref="Commands" />
|
||||
/// </summary>
|
||||
/// <param name="command"><see cref="Commands" /> as an byte</param>
|
||||
/// <param name="requestProtocolData">Request protocol data for logging</param>
|
||||
/// <param name="encodedRequestData"> Ready to send data encoded with transmit protocol retries</param>
|
||||
/// <param name="responseTimeoutMs">Timeout for response</param>
|
||||
/// <param name="register"><see cref="RegisterDefinition"/> to do command with</param>
|
||||
/// <param name="hideDataInLog">hiding data in log file to avoid spying of passwords</param>
|
||||
/// <param name="skipRetryErrorCode">error mask to skip retries <see cref="ResponseErrorCode"/></param>
|
||||
public RequestRecord(Byte command, List<Byte> requestProtocolData = null, List<Byte> encodedRequestData = null,
|
||||
Int32 responseTimeoutMs = 100, RegisterDefinition register = null, Boolean hideDataInLog = false,
|
||||
UInt16 skipRetryErrorCode = CommunicationConfig.SkipRetryErrorCode)
|
||||
{
|
||||
ResponseCommand = (Byte)(command + 1);
|
||||
Register = register;
|
||||
RequestProtocolData = requestProtocolData;
|
||||
EncodedRequestData = encodedRequestData;
|
||||
ResponseTimeoutMs = responseTimeoutMs;
|
||||
HideDataInLog = hideDataInLog;
|
||||
SkipRetryErrorCode = skipRetryErrorCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user