Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33ef914984 | ||
|
|
3eb0b87f0d | ||
|
|
711dfc03ef | ||
|
|
93459ccfab | ||
|
|
009aefb7c9 | ||
|
|
d2e18d615d | ||
|
|
69b475eb77 | ||
|
|
2210bb8240 | ||
|
|
65ab19290f | ||
|
|
94c7279da1 | ||
|
|
2136695b80 | ||
|
|
c0f9b0ffa0 | ||
|
|
c4ef22436f | ||
|
|
0b73871518 | ||
|
|
f7dc6fee5e | ||
|
|
ba848a167d | ||
|
|
dcb0bf9a88 | ||
|
|
f6433134c1 | ||
|
|
7e3df77b5b | ||
|
|
08dff1272b | ||
|
|
d5c8fd1c8f | ||
|
|
a34740e838 | ||
|
|
836ba5a41f | ||
|
|
23c83ab452 | ||
|
|
bc9e583c29 | ||
|
|
18cef617da | ||
|
|
4ee736cff5 | ||
|
|
4e40dc8601 | ||
|
|
a5a68ec457 | ||
|
|
e26d88d437 | ||
|
|
2522d1da9f | ||
|
|
8983c01591 | ||
|
|
61b1320d4f | ||
|
|
2c8a0922d1 | ||
|
|
b078d9586d | ||
|
|
6e35de5f4d | ||
|
|
2dbe4cded4 | ||
|
|
10865c6878 | ||
|
|
ee1353fe54 | ||
|
|
5cefd7fc3a | ||
|
|
ab0e5ea82a |
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("Sensus")]
|
[assembly: AssemblyCompany("Sensus")]
|
||||||
[assembly: AssemblyProduct("TestBenchFramework")]
|
[assembly: AssemblyProduct("TestBenchFramework")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2013 - 2023 Sensus Slovensko, a.s.")]
|
[assembly: AssemblyCopyright("Copyright © 2013 - 2025 Sensus Slovensko, a.s.")]
|
||||||
[assembly: AssemblyTrademark("")]
|
[assembly: AssemblyTrademark("")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
@@ -19,6 +19,9 @@ using System.Runtime.InteropServices;
|
|||||||
// COM, set the ComVisible attribute to true on that type.
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
[assembly: ComVisible(false)]
|
[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
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
[assembly: Guid("2c13538e-15ee-48b2-af0f-2c95afb67186")]
|
[assembly: Guid("2c13538e-15ee-48b2-af0f-2c95afb67186")]
|
||||||
|
|
||||||
@@ -29,5 +32,5 @@ using System.Runtime.InteropServices;
|
|||||||
// Build Number
|
// Build Number
|
||||||
// Revision
|
// Revision
|
||||||
//
|
//
|
||||||
[assembly: AssemblyVersion("3.9.2149.4")]
|
[assembly: AssemblyVersion("3.9.3056.1")]
|
||||||
[assembly: AssemblyFileVersion("3.9.2149.4")]
|
[assembly: AssemblyFileVersion("3.9.3056.1")]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
## Version History
|
## Version History
|
||||||
|
|
||||||
| Version | Target Environment | Title | Description |
|
| Version | Source of change | 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 |
|
| 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>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// 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
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// 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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Test in progress.
|
/// Looks up a localized string similar to Test in progress.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Generated
+5012
File diff suppressed because it is too large
Load Diff
@@ -1764,4 +1764,7 @@
|
|||||||
<data name="Units" xml:space="preserve">
|
<data name="Units" xml:space="preserve">
|
||||||
<value />
|
<value />
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Test_in_calculation" xml:space="preserve">
|
||||||
|
<value>Probíhá výpočet</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -2482,4 +2482,7 @@
|
|||||||
<data name="Units" xml:space="preserve">
|
<data name="Units" xml:space="preserve">
|
||||||
<value>Units</value>
|
<value>Units</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Test_in_calculation" xml:space="preserve">
|
||||||
|
<value>Test in calculation</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -259,4 +259,7 @@
|
|||||||
<data name="Units" xml:space="preserve">
|
<data name="Units" xml:space="preserve">
|
||||||
<value />
|
<value />
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Test_in_calculation" xml:space="preserve">
|
||||||
|
<value>Prebieha výpočet</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -111,6 +111,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
|
|||||||
|
|
||||||
public void TurnOff()
|
public void TurnOff()
|
||||||
{
|
{
|
||||||
|
log.DebugFormat("{0}.TurnOff()", Name);
|
||||||
if ((pumpFm1 != null) && (pumpFm2 != null))
|
if ((pumpFm1 != null) && (pumpFm2 != null))
|
||||||
{
|
{
|
||||||
pumpFm1.TurnOff();
|
pumpFm1.TurnOff();
|
||||||
@@ -120,6 +121,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem
|
|||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -332,6 +332,8 @@ namespace TBF.Rig.Danfoss.VLT2800
|
|||||||
Telegram.UpdateTelegramChecksum(msg);
|
Telegram.UpdateTelegramChecksum(msg);
|
||||||
}
|
}
|
||||||
SendData(msg);
|
SendData(msg);
|
||||||
|
|
||||||
|
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -264,6 +264,8 @@ namespace TBF.Rig.Modbus.PumpFM.DanfossVLT
|
|||||||
|
|
||||||
modbus.SendMessage(msg, Name);
|
modbus.SendMessage(msg, Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -416,6 +416,8 @@ namespace TBF.Rig.Modbus.PumpFM.Grundfoss
|
|||||||
// --- DIAGNOSTIKA ---
|
// --- DIAGNOSTIKA ---
|
||||||
LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg));
|
LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.DebugFormat("DONE {0}.TurnOff()", Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+547
@@ -0,0 +1,547 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.MeasurementRecords;
|
||||||
|
|
||||||
|
|
||||||
|
// ReSharper disable UnusedMember.Local
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data fields and definitions for GENESIS streaming protocol
|
||||||
|
/// </summary>
|
||||||
|
public class StreamingDecoder
|
||||||
|
{
|
||||||
|
private const Double MilliLitersToCmFactor = 1.0E-6;
|
||||||
|
private const Double CpuTimeToSecondsFactor = 1.0 / 0x10000;
|
||||||
|
public const Double CpuTimeOverflowS = 0x100000000 * CpuTimeToSecondsFactor;
|
||||||
|
private const Double LitersPerSecondToCmPerHourFactor = 3600.0 / 1000.0;
|
||||||
|
|
||||||
|
private const Double DefaultVolumeScaleRawPerMl = 1024.0;
|
||||||
|
private const Double DefaultVolumeFactorRawToCm = MilliLitersToCmFactor / DefaultVolumeScaleRawPerMl;
|
||||||
|
private const Double MaxGenesisAccuVolumeRaw = UInt32.MaxValue; //0x100000000; //2^32
|
||||||
|
public const Double DefaultAccuDutOverflowVolumeCm = MaxGenesisAccuVolumeRaw * DefaultVolumeFactorRawToCm;
|
||||||
|
|
||||||
|
private const Double DisplayMlSetupDutOverflowVolumeCm = 1000.0; //overflow of LCD if set to ml
|
||||||
|
private const Double TofToSecondsFactor38Bit = 1.0 / 0x4000000000; // 2^38
|
||||||
|
private const Double AmplitudeToVoltFactor = 1.0 / 0x400000 / 1000.0; // 2^22 100 0000 0000 0000 0000 0000b
|
||||||
|
private const Double PulseWidthToRelFactor = 1.0 / 0x100; // 2^8
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default data for bend detection tests of Genesis
|
||||||
|
/// </summary>
|
||||||
|
private readonly BendDetectionRecord _bendDetectionDefault = new BendDetectionRecord
|
||||||
|
{
|
||||||
|
StatusBendU0 = BendDetectionRecord.StatusBendU0Enum.OKAY,
|
||||||
|
InstallationType = BendDetectionRecord.InstallationTypeEnum.INSTALLATION_UNDISTURBED,
|
||||||
|
CorrectionFactor_percent = 0.0,
|
||||||
|
PreCorrectionVolumeRaw = 0.0,
|
||||||
|
PostCorrectionVolumeRaw = 0.0,
|
||||||
|
TimeS = 0.0,
|
||||||
|
OverflowTimeS = CpuTimeOverflowS,
|
||||||
|
Crc = 0xFFFF,
|
||||||
|
IsValid = false
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default data for flow tests of Genesis
|
||||||
|
/// </summary>
|
||||||
|
private readonly FlowTestRecord _dataDefault = new FlowTestRecord
|
||||||
|
{
|
||||||
|
VolumeCm = 0.0,
|
||||||
|
OverflowVolumeCm = DisplayMlSetupDutOverflowVolumeCm,
|
||||||
|
TimeS = 0.0,
|
||||||
|
OverflowTimeS = CpuTimeOverflowS,
|
||||||
|
Crc = 0xFFFF,
|
||||||
|
IsValid = false
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default data for calibration of Genesis
|
||||||
|
/// </summary>
|
||||||
|
private readonly CalibrationRecord _rawDataDefault = new CalibrationRecord
|
||||||
|
{
|
||||||
|
Channel = 0,
|
||||||
|
Validation = 0xFFFF,
|
||||||
|
TotalTimeOfFlightS = 0.0,
|
||||||
|
DeltaTimeOfFlightS = 0.0,
|
||||||
|
|
||||||
|
VolumeScaleRawPerMl = DefaultVolumeScaleRawPerMl,
|
||||||
|
VolumeFactorRawToQm = DefaultVolumeFactorRawToCm,
|
||||||
|
DeltaVolumeRaw = 0.0,
|
||||||
|
DeltaVolumeQm = 0.0,
|
||||||
|
AccuVolumeRaw = 0.0,
|
||||||
|
VolumeCm = 0.0,
|
||||||
|
OverflowVolumeCm = DefaultAccuDutOverflowVolumeCm,
|
||||||
|
|
||||||
|
SampleIntervalS = 0.0,
|
||||||
|
AmplitudeUpV = 0.0,
|
||||||
|
AmplitudeDownV = 0.0,
|
||||||
|
PulseWidthRatioUp = 0.0,
|
||||||
|
PulseWidthRatioDown = 0.0,
|
||||||
|
TemperatureRaw = 20.0,
|
||||||
|
TemperaturePowFactor = 1.0,
|
||||||
|
TemperatureDegC = 20.0,
|
||||||
|
TimeS = 0.0,
|
||||||
|
OverflowTimeS = CpuTimeOverflowS,
|
||||||
|
Crc = 0xFFFF,
|
||||||
|
IsValid = false
|
||||||
|
};
|
||||||
|
|
||||||
|
private CalibrationRecord _dataCalibRec;
|
||||||
|
private FlowTestRecord _dataFlowTestRec;
|
||||||
|
private BendDetectionRecord _dataBendDetectRec;
|
||||||
|
private readonly Boolean _ignoreCorruptedData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor initializes all decoded members with default values
|
||||||
|
/// </summary>
|
||||||
|
public StreamingDecoder(Boolean ignoreCorruptedData = true)
|
||||||
|
{
|
||||||
|
_dataFlowTestRec = _dataDefault;
|
||||||
|
_dataCalibRec = _rawDataDefault;
|
||||||
|
_dataBendDetectRec = _bendDetectionDefault;
|
||||||
|
_ignoreCorruptedData = ignoreCorruptedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calibration data
|
||||||
|
/// </summary>
|
||||||
|
public CalibrationRecord DataCalib
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flow test data
|
||||||
|
/// </summary>
|
||||||
|
public FlowTestRecord DataFlowTest
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Bend detection test data
|
||||||
|
/// </summary>
|
||||||
|
public BendDetectionRecord DataBendDetectTest
|
||||||
|
{
|
||||||
|
get; private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decoding the raw message
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rawMsg">message received as one line delimited with LF</param>
|
||||||
|
/// <returns>true if decoding was successful and data has been validated</returns>
|
||||||
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
||||||
|
/// - Modified using common CRC check before branching to the protocol specific decoder.
|
||||||
|
/// </remarks>
|
||||||
|
/// <remarks date="2023-Dec-06" author="T.Wiedebusch">
|
||||||
|
/// - Introduced protocol 'm' for bending detection.
|
||||||
|
/// </remarks>
|
||||||
|
public Boolean DecodeMsg(String rawMsg)
|
||||||
|
{
|
||||||
|
var rawRecordIsValid = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// save the raw message for CRC calculation before separation to fields
|
||||||
|
//_rawMsgForCrc = rawMsg;
|
||||||
|
// extract message and split it to fields
|
||||||
|
|
||||||
|
//DN50
|
||||||
|
//2022-07-21 07:22:06.9871 | @f 8497D 062E4216 9B2A
|
||||||
|
//2022-07-21 07:22:06.9871 | @h 1 0 0A1F59C4 00017A43 00115C45 72E1596B 00000400 00001998 7D91B652 7D4F37E6 000191E6 0C 062E4A9C 5331
|
||||||
|
//2022-07-21 07:22:07.0171 | @h 2 0 0A1B1FE8 00017B7F 00116B56 72B77427 00000400 0000199A 7DC09688 7B570006 000191E6 0C 062E5326 95BD
|
||||||
|
//rawMsg = "@h 3 0 0A1DF1D5 00017EA8 00118037 741F80F3 00000400 00001998 7E58A62E 7CC2C606 000191E6 0C 062E5BAE D40E";
|
||||||
|
|
||||||
|
//DN80
|
||||||
|
//2022-04-28 15:19:54.9167 | @f AA754B 4D0CEE78 5D89
|
||||||
|
//2022-04-28 15:19:54.9337 | @h 1 0 0EF4A130 0002AAB8 000DAC8C 02B98FBD 00000200 00000FFC 643BCF30 63C9CFF6 00015096 0C 4D0CF3CC 3E87
|
||||||
|
//2022-04-28 15:19:54.9497 | @h 2 0 0EFA3000 000283E7 000CE580 E3D5EFFA 00000200 00001000 6DC0A84E 6CD462C2 00015096 0C 4D0CF922 1646
|
||||||
|
//2022-04-28 15:19:54.9627 | @h 3 0 0EFF7F91 0002AC39 000DB43D FE1C0254 00000200 00000FFE 6C932DE6 6D20005D 00015096 0C 4D0CFE76 868A
|
||||||
|
//2022-04-28 15:19:54.9787 | @f AA7C01 4D0CFE76 B08F
|
||||||
|
// rawMsg = "@h 3 0 0EFF7F91 0002AC39 000DB43D FE1C0254 00000200 00000FFE 6C932DE6 6D20005D 00015096 0C 4D0CFE76 868A ";
|
||||||
|
|
||||||
|
// The received message is a string with a line delimiter.
|
||||||
|
rawMsg = rawMsg.Replace('\n', ' ');
|
||||||
|
|
||||||
|
// The raw message fields are the separated values from the received string with a blank as field separator
|
||||||
|
var rawMsgFields = rawMsg.Split(' ');
|
||||||
|
|
||||||
|
// The last element is the CRC, the CRC can be separated, calculated and validated before trying to decode the content
|
||||||
|
var rawRecordForCrc = "";
|
||||||
|
// get all fields excluding the CRC (length - 1)
|
||||||
|
for (var x = 0; x < rawMsgFields.Length - 1; x++)
|
||||||
|
{
|
||||||
|
rawRecordForCrc += rawMsgFields[x];
|
||||||
|
// add the field delimiter from raw data
|
||||||
|
rawRecordForCrc += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract bytes of raw message for CRC calculation each character, CRC field is already removed
|
||||||
|
var byteArraySize = rawRecordForCrc.Length;
|
||||||
|
var byteArray = new Byte[byteArraySize];
|
||||||
|
for (var i = 0; i < byteArraySize; i++)
|
||||||
|
{
|
||||||
|
byteArray[i] = (Byte)rawRecordForCrc[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate the CRC from the received data
|
||||||
|
var calculatedCrc = Crc16Ccitt.CalculateMsb1021(byteArray);
|
||||||
|
|
||||||
|
// extract received CRC
|
||||||
|
var receivedCrc = UInt16.Parse(rawMsgFields[rawMsgFields.Length - 1], NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
// compare received with calculated CRC and remind valid decoding
|
||||||
|
rawRecordIsValid = calculatedCrc == receivedCrc;
|
||||||
|
|
||||||
|
switch (rawMsgFields[0])
|
||||||
|
{
|
||||||
|
case "@m":
|
||||||
|
_dataBendDetectRec.IsValid = rawRecordIsValid;
|
||||||
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
DecodeProtocolM(ref _dataBendDetectRec, rawMsgFields);
|
||||||
|
DataBendDetectTest = _dataBendDetectRec;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "@f":
|
||||||
|
_dataFlowTestRec.IsValid = rawRecordIsValid;
|
||||||
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
DecodeProtocolF(ref _dataFlowTestRec, rawMsgFields);
|
||||||
|
DataFlowTest = _dataFlowTestRec;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "@g":
|
||||||
|
_dataCalibRec.IsValid = rawRecordIsValid;
|
||||||
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
DecodeProtocolG(ref _dataCalibRec, rawMsgFields);
|
||||||
|
DataCalib = _dataCalibRec;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "@h":
|
||||||
|
_dataCalibRec.IsValid = rawRecordIsValid;
|
||||||
|
if (rawRecordIsValid || !_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
DecodeProtocolH(ref _dataCalibRec, rawMsgFields);
|
||||||
|
DataCalib = _dataCalibRec;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
return rawRecordIsValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracting message from string fields for protocol 'm'
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataBendTestRec">reference to bend detection test record</param>
|
||||||
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
||||||
|
/// - Modified using common CRC check in advance.
|
||||||
|
/// </remarks>
|
||||||
|
private static void DecodeProtocolM(ref BendDetectionRecord dataBendTestRec, IList<String> fields)
|
||||||
|
{
|
||||||
|
// time stamp attachment
|
||||||
|
dataBendTestRec.DecodedTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
// extract received CRC
|
||||||
|
dataBendTestRec.Crc = ushort.Parse(fields[(Int32)ProtMsubString.Crc],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
// extract the status
|
||||||
|
dataBendTestRec.StatusBendU0 =
|
||||||
|
(BendDetectionRecord.StatusBendU0Enum)UInt32.Parse(fields[(Int32)ProtMsubString.Status],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
// extract the installation type
|
||||||
|
dataBendTestRec.InstallationType =
|
||||||
|
(BendDetectionRecord.InstallationTypeEnum)UInt32.Parse(fields[(Int32)ProtMsubString.Installation],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
// extract the correction factor in percent
|
||||||
|
dataBendTestRec.CorrectionFactor_percent =
|
||||||
|
UInt32.Parse(fields[(Int32)ProtMsubString.Factor],
|
||||||
|
NumberStyles.AllowHexSpecifier) * BendDetectionRecord.CorrectionFactorScale;
|
||||||
|
|
||||||
|
// build result values, the volume before and after correction can be positive or negative!
|
||||||
|
dataBendTestRec.PreCorrectionVolumeRaw = Int32.Parse(fields[(Int32)ProtMsubString.PreVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
// build result values, the volume before and after correction can be positive or negative!
|
||||||
|
dataBendTestRec.PostCorrectionVolumeRaw = Int32.Parse(fields[(Int32)ProtMsubString.PostVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracting message from string fields for protocol 'f'
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataFlowTestRec">reference to flow test record</param>
|
||||||
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
||||||
|
/// - Modified using common CRC check in advance.
|
||||||
|
/// </remarks>
|
||||||
|
private static void DecodeProtocolF(ref FlowTestRecord dataFlowTestRec, IList<String> fields)
|
||||||
|
{
|
||||||
|
// time stamp attachment
|
||||||
|
dataFlowTestRec.DecodedTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
// extract received CRC
|
||||||
|
dataFlowTestRec.Crc = ushort.Parse(fields[(Int32)ProtFsubString.Crc],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
// build result values, the display volume can be positive or negative!
|
||||||
|
dataFlowTestRec.VolumeCm = int.Parse(fields[(Int32)ProtFsubString.DisplayVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier) * MilliLitersToCmFactor;
|
||||||
|
dataFlowTestRec.TimeS = uint.Parse(fields[(Int32)ProtFsubString.CpuTime],
|
||||||
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracting message from string fields to individual raw channel for protocol 'g'
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataProtGRec">Reference to result structure for raw data for one channel</param>
|
||||||
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
||||||
|
/// <returns>true if protocol is valid</returns>
|
||||||
|
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
|
||||||
|
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
|
||||||
|
/// </remarks>
|
||||||
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
||||||
|
/// - Modified using common CRC check in advance.
|
||||||
|
/// </remarks>
|
||||||
|
private static void DecodeProtocolG(ref CalibrationRecord dataProtGRec, IList<String> fields)
|
||||||
|
{
|
||||||
|
// time stamp attachment
|
||||||
|
dataProtGRec.DecodedTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
// extract received CRC
|
||||||
|
dataProtGRec.Crc = ushort.Parse(fields[(Int32)ProtGsubString.Crc],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
dataProtGRec.Channel = ushort.Parse(fields[(Int32)ProtGsubString.ChanNo],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
dataProtGRec.Validation = ushort.Parse(fields[(Int32)ProtGsubString.Validation],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
// Delta time of flight
|
||||||
|
dataProtGRec.DeltaTimeOfFlightS = int.Parse(fields[(Int32)ProtGsubString.Dtof],
|
||||||
|
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
|
||||||
|
|
||||||
|
// Delta raw volume between last sample
|
||||||
|
dataProtGRec.DeltaVolumeRaw = uint.Parse(fields[(Int32)ProtGsubString.RawDVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// volume scaling
|
||||||
|
var volumeRawScale = uint.Parse(fields[(Int32)ProtGsubString.VolumeScale],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
dataProtGRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
|
||||||
|
dataProtGRec.VolumeFactorRawToQm = MilliLitersToCmFactor / dataProtGRec.VolumeScaleRawPerMl;
|
||||||
|
dataProtGRec.OverflowVolumeCm = MaxGenesisAccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
|
||||||
|
|
||||||
|
// Calculate volume in cubic meters out of the raw volume
|
||||||
|
dataProtGRec.DeltaVolumeQm = dataProtGRec.DeltaVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
|
||||||
|
|
||||||
|
// accumulated volume for each channel received from water meter scaled with volumeScale
|
||||||
|
// the volume can just be positive
|
||||||
|
dataProtGRec.AccuVolumeRaw = uint.Parse(fields[(Int32)ProtGsubString.AccuVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
dataProtGRec.VolumeCm = dataProtGRec.AccuVolumeRaw * dataProtGRec.VolumeFactorRawToQm;
|
||||||
|
|
||||||
|
// Sample interval
|
||||||
|
dataProtGRec.SampleIntervalS = uint.Parse(fields[(Int32)ProtGsubString.SampleInterval],
|
||||||
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
||||||
|
|
||||||
|
// amplitude for high threshold in V
|
||||||
|
dataProtGRec.AmplitudeUpV = uint.Parse(fields[(Int32)ProtGsubString.AmplitudeUp],
|
||||||
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
||||||
|
|
||||||
|
// amplitude for low threshold in V
|
||||||
|
dataProtGRec.AmplitudeDownV = uint.Parse(fields[(Int32)ProtGsubString.AmplitudeDown],
|
||||||
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
||||||
|
|
||||||
|
// pulse width ratio high threshold
|
||||||
|
dataProtGRec.PulseWidthRatioUp = uint.Parse(fields[(Int32)ProtGsubString.PulseWidthRatioUp],
|
||||||
|
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
|
||||||
|
|
||||||
|
// pulse width ratio low threshold
|
||||||
|
dataProtGRec.PulseWidthRatioDown = uint.Parse(fields[(Int32)ProtGsubString.PulseWidthRatioDown],
|
||||||
|
NumberStyles.AllowHexSpecifier) * PulseWidthToRelFactor;
|
||||||
|
|
||||||
|
// raw temperature
|
||||||
|
dataProtGRec.TemperatureRaw = int.Parse(fields[(Int32)ProtGsubString.RawTemperature],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// temperature scaling
|
||||||
|
dataProtGRec.TemperaturePowFactor = uint.Parse(fields[(Int32)ProtGsubString.TemperatureScale],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// Calculate temperature
|
||||||
|
dataProtGRec.TemperatureDegC = dataProtGRec.TemperatureRaw /
|
||||||
|
Math.Pow(2.0, dataProtGRec.TemperaturePowFactor);
|
||||||
|
|
||||||
|
// absolute CPU time, started at LED mode 3 activation
|
||||||
|
dataProtGRec.TimeS = uint.Parse(fields[(Int32)ProtGsubString.CpuTime],
|
||||||
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracting message from string fields to individual raw channel for protocol 'g'
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataProtHRec">Reference to result structure for raw data for one channel</param>
|
||||||
|
/// <param name="fields">Separated fields containing the measurement as string</param>
|
||||||
|
/// <returns>true if protocol is valid</returns>
|
||||||
|
/// <remarks date="2018-Mar-22" author="T.Wiedebusch">
|
||||||
|
/// - Usage of VolumeFactorRawToQm and calculation of AccuDutOverflowVolumeCm
|
||||||
|
/// </remarks>
|
||||||
|
/// <remarks date="2023-Mar-09" author="T.Wiedebusch">
|
||||||
|
/// - Modified using common CRC check in advance.
|
||||||
|
/// </remarks>
|
||||||
|
private static void DecodeProtocolH(ref CalibrationRecord dataProtHRec, IList<String> fields)
|
||||||
|
{
|
||||||
|
// time stamp attachment
|
||||||
|
dataProtHRec.DecodedTime = DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
// extract received CRC
|
||||||
|
dataProtHRec.Crc = ushort.Parse(fields[(Int32)ProtHsubString.Crc],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
dataProtHRec.Channel = ushort.Parse(fields[(Int32)ProtHsubString.ChanNo],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
dataProtHRec.Validation = ushort.Parse(fields[(Int32)ProtHsubString.Validation],
|
||||||
|
NumberStyles.HexNumber);
|
||||||
|
|
||||||
|
// Total time of flight
|
||||||
|
dataProtHRec.TotalTimeOfFlightS = int.Parse(fields[(Int32)ProtHsubString.Ttof],
|
||||||
|
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
|
||||||
|
// Delta time of flight
|
||||||
|
dataProtHRec.DeltaTimeOfFlightS = int.Parse(fields[(Int32)ProtHsubString.Dtof],
|
||||||
|
NumberStyles.AllowHexSpecifier) * TofToSecondsFactor38Bit;
|
||||||
|
|
||||||
|
dataProtHRec.RawTotalTimeOfFlight =
|
||||||
|
int.Parse(fields[(Int32)ProtHsubString.Ttof], NumberStyles.AllowHexSpecifier);
|
||||||
|
dataProtHRec.RawDeltaTimeOfFlight = int.Parse(fields[(Int32)ProtHsubString.Dtof], NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// Delta raw volume between two samples
|
||||||
|
dataProtHRec.DeltaVolumeRaw = uint.Parse(fields[(Int32)ProtHsubString.RawDVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// volume scaling
|
||||||
|
var volumeRawScale = uint.Parse(fields[(Int32)ProtHsubString.VolumeScale],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
dataProtHRec.VolumeScaleRawPerMl = volumeRawScale != 0 ? volumeRawScale : DefaultVolumeScaleRawPerMl;
|
||||||
|
dataProtHRec.VolumeFactorRawToQm = MilliLitersToCmFactor / dataProtHRec.VolumeScaleRawPerMl;
|
||||||
|
dataProtHRec.OverflowVolumeCm = MaxGenesisAccuVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
|
||||||
|
|
||||||
|
// Calculate volume in cubic meters out of the raw volume
|
||||||
|
dataProtHRec.DeltaVolumeQm = dataProtHRec.DeltaVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
|
||||||
|
|
||||||
|
// accumulated volume for each channel received from water meter scaled with volumeScale
|
||||||
|
// the volume can just be positive
|
||||||
|
dataProtHRec.AccuVolumeRaw = uint.Parse(fields[(Int32)ProtHsubString.AccuVolume],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
dataProtHRec.VolumeCm = dataProtHRec.AccuVolumeRaw * dataProtHRec.VolumeFactorRawToQm;
|
||||||
|
|
||||||
|
// Sample interval
|
||||||
|
dataProtHRec.SampleIntervalS = uint.Parse(fields[(Int32)ProtHsubString.SampleInterval],
|
||||||
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
||||||
|
|
||||||
|
// amplitude for high threshold in V
|
||||||
|
dataProtHRec.AmplitudeUpV = uint.Parse(fields[(Int32)ProtHsubString.AmplitudeUp],
|
||||||
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
||||||
|
|
||||||
|
// amplitude for low threshold in V
|
||||||
|
dataProtHRec.AmplitudeDownV = uint.Parse(fields[(Int32)ProtHsubString.AmplitudeDown],
|
||||||
|
NumberStyles.AllowHexSpecifier) * AmplitudeToVoltFactor;
|
||||||
|
|
||||||
|
// raw temperature
|
||||||
|
dataProtHRec.TemperatureRaw = int.Parse(fields[(Int32)ProtHsubString.RawTemperature],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// temperature scaling
|
||||||
|
dataProtHRec.TemperaturePowFactor = uint.Parse(fields[(Int32)ProtHsubString.TemperatureScale],
|
||||||
|
NumberStyles.AllowHexSpecifier);
|
||||||
|
|
||||||
|
// Calculate temperature
|
||||||
|
dataProtHRec.TemperatureDegC = dataProtHRec.TemperatureRaw / (
|
||||||
|
Math.Pow(2.0, dataProtHRec.TemperaturePowFactor));
|
||||||
|
|
||||||
|
// absolute CPU time, started at LED mode 3 activation
|
||||||
|
dataProtHRec.TimeS = uint.Parse(fields[(Int32)ProtHsubString.CpuTime],
|
||||||
|
NumberStyles.AllowHexSpecifier) * CpuTimeToSecondsFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// field position in protocol 'f'
|
||||||
|
private enum ProtFsubString
|
||||||
|
{
|
||||||
|
//do not remove this needed for position in record
|
||||||
|
ProtType,
|
||||||
|
DisplayVolume,
|
||||||
|
CpuTime,
|
||||||
|
Crc
|
||||||
|
}
|
||||||
|
/// field position in protocol 'm'
|
||||||
|
private enum ProtMsubString
|
||||||
|
{
|
||||||
|
//do not remove this needed for position in record
|
||||||
|
ProtType,
|
||||||
|
Status,
|
||||||
|
Installation,
|
||||||
|
Factor,
|
||||||
|
PreVolume,
|
||||||
|
PostVolume,
|
||||||
|
Crc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// field position in protocol 'g'
|
||||||
|
private enum ProtGsubString
|
||||||
|
{
|
||||||
|
//do not remove this needed for position in record
|
||||||
|
ProtType,
|
||||||
|
ChanNo,
|
||||||
|
Validation,
|
||||||
|
Dtof,
|
||||||
|
RawDVolume,
|
||||||
|
AccuVolume,
|
||||||
|
VolumeScale,
|
||||||
|
SampleInterval,
|
||||||
|
AmplitudeUp,
|
||||||
|
AmplitudeDown,
|
||||||
|
PulseWidthRatioUp,
|
||||||
|
PulseWidthRatioDown,
|
||||||
|
RawTemperature,
|
||||||
|
TemperatureScale,
|
||||||
|
CpuTime,
|
||||||
|
Crc
|
||||||
|
}
|
||||||
|
|
||||||
|
/// field position in protocol 'h'
|
||||||
|
private enum ProtHsubString
|
||||||
|
{
|
||||||
|
//do not remove this needed for position in record
|
||||||
|
ProtType,
|
||||||
|
ChanNo,
|
||||||
|
Validation,
|
||||||
|
Ttof,
|
||||||
|
Dtof,
|
||||||
|
RawDVolume,
|
||||||
|
AccuVolume,
|
||||||
|
VolumeScale,
|
||||||
|
SampleInterval,
|
||||||
|
AmplitudeUp,
|
||||||
|
AmplitudeDown,
|
||||||
|
RawTemperature,
|
||||||
|
TemperatureScale,
|
||||||
|
CpuTime,
|
||||||
|
Crc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.DataPackages.EventArguments;
|
||||||
|
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Ports.PortCore.EventArguments;
|
||||||
|
using TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Interfaces.Protocols.ProtocolCore;
|
||||||
|
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Protocols.StreamingProtocol
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <summary>
|
||||||
|
/// Layer between <see cref="StreamingDecoder" /> CRC16CCITT handler and Events
|
||||||
|
/// block trashy telegrams
|
||||||
|
/// </summary>
|
||||||
|
public class StreamingProtocol : BaseProtocol
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decode data and hold results
|
||||||
|
/// </summary>
|
||||||
|
private StreamingDecoder _streamingDecode;
|
||||||
|
private readonly Boolean _ignoreCorruptedData;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override event EventHandler<BaseDataEventArgs> OnRecordIsDecoded;
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void DecodeRecord(IPortDataEventArgs dataArgs)
|
||||||
|
{
|
||||||
|
var data = (String)dataArgs.GetData();
|
||||||
|
|
||||||
|
//Data decoding
|
||||||
|
_streamingDecode = new StreamingDecoder(_ignoreCorruptedData);
|
||||||
|
_streamingDecode.DecodeMsg(data);
|
||||||
|
|
||||||
|
//Event for new data
|
||||||
|
if (_streamingDecode.DataFlowTest != null)
|
||||||
|
{
|
||||||
|
_streamingDecode.DataFlowTest.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
|
||||||
|
_streamingDecode.DataFlowTest.ReceivedTime = dataArgs.GetReceivedTime();
|
||||||
|
|
||||||
|
if (_streamingDecode.DataFlowTest.IsValid)
|
||||||
|
{
|
||||||
|
OnRecordIsDecoded?.Invoke(this,
|
||||||
|
new FlowDataEventArgs { NewData = _streamingDecode.DataFlowTest, RawData = data });
|
||||||
|
}
|
||||||
|
else if (!_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
OnRecordIsDecoded?.Invoke(this,
|
||||||
|
new FlowDataEventArgs { NewData = _streamingDecode.DataFlowTest, RawData = data });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (_streamingDecode.DataCalib != null)
|
||||||
|
{
|
||||||
|
_streamingDecode.DataCalib.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
|
||||||
|
_streamingDecode.DataCalib.ReceivedTime = dataArgs.GetReceivedTime();
|
||||||
|
if (_streamingDecode.DataCalib.IsValid)
|
||||||
|
{
|
||||||
|
OnRecordIsDecoded?.Invoke(this, new CalibDataEventArgs
|
||||||
|
{
|
||||||
|
CalibChl = _streamingDecode.DataCalib,
|
||||||
|
RawData = data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (!_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
OnRecordIsDecoded?.Invoke(this, new CalibDataEventArgs
|
||||||
|
{
|
||||||
|
CalibChl = _streamingDecode.DataCalib,
|
||||||
|
RawData = data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (_streamingDecode.DataBendDetectTest != null)
|
||||||
|
{
|
||||||
|
_streamingDecode.DataBendDetectTest.SyncMarkRecord = dataArgs.GetSyncMarkRecord();
|
||||||
|
_streamingDecode.DataBendDetectTest.ReceivedTime = dataArgs.GetReceivedTime();
|
||||||
|
if (_streamingDecode.DataBendDetectTest.IsValid)
|
||||||
|
{
|
||||||
|
OnRecordIsDecoded?.Invoke(this, new BendDetectDataEventArgs
|
||||||
|
{
|
||||||
|
NewData = _streamingDecode.DataBendDetectTest,
|
||||||
|
RawData = data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (!_ignoreCorruptedData)
|
||||||
|
{
|
||||||
|
OnRecordIsDecoded?.Invoke(this, new BendDetectDataEventArgs
|
||||||
|
{
|
||||||
|
NewData = _streamingDecode.DataBendDetectTest,
|
||||||
|
RawData = data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//toDo: handle trashy telegrams
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public StreamingProtocol(String ident, Boolean ignoreCorruptedData = true) : base(ident)
|
||||||
|
{
|
||||||
|
_ignoreCorruptedData = ignoreCorruptedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration for all known access level
|
||||||
|
/// </summary>
|
||||||
|
public enum Access
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Not set, means no valid input/information
|
||||||
|
/// </summary>
|
||||||
|
NS,
|
||||||
|
/// <summary>
|
||||||
|
/// No Access
|
||||||
|
/// </summary>
|
||||||
|
NA,
|
||||||
|
/// <summary>
|
||||||
|
/// read only
|
||||||
|
/// </summary>
|
||||||
|
RO,
|
||||||
|
/// <summary>
|
||||||
|
/// Write only
|
||||||
|
/// </summary>
|
||||||
|
WO,
|
||||||
|
/// <summary>
|
||||||
|
/// Read and Write
|
||||||
|
/// </summary>
|
||||||
|
RW
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
public struct ByteArray
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
// internal struct Enum8
|
||||||
|
public struct Enum8
|
||||||
|
{
|
||||||
|
//todo find out struct definition and implement it
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
public struct Rpc
|
||||||
|
{
|
||||||
|
//todo find out struct definition and implement it
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static type to define restore capability
|
||||||
|
/// </summary>
|
||||||
|
public struct StaticType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The value of the static type qualifier
|
||||||
|
/// </summary>
|
||||||
|
public String StaticTypeValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Field identifier in configuration.json
|
||||||
|
/// </summary>
|
||||||
|
public const String StaticTypeFieldIdentifier = "statictype";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unknown to define restore capability
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once UnusedMember.Local
|
||||||
|
private const String UnknownAccess = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static type to define restore capability
|
||||||
|
/// </summary>
|
||||||
|
public const String RestoreRequired = "static";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static type approximate informs that the read back value may be not identical
|
||||||
|
/// to the written value and is approximated.
|
||||||
|
/// </summary>
|
||||||
|
public const String ReadBackApproximated = "approximate";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Denied restore capability
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once UnusedMember.Local
|
||||||
|
public const String RestoreDenied = "dynamic";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unpredictable restore capability
|
||||||
|
/// </summary>
|
||||||
|
// ReSharper disable once UnusedMember.Local
|
||||||
|
public const String RestoreUnpredictable = "infrequentlyupdated";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check restore capability
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if restore required</returns>
|
||||||
|
public Boolean CheckRestoreCapability()
|
||||||
|
{
|
||||||
|
return StaticTypeValue == RestoreRequired || StaticTypeValue == ReadBackApproximated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ctor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="staticType">input of initial type</param>
|
||||||
|
public StaticType(String staticType)
|
||||||
|
{
|
||||||
|
StaticTypeValue = staticType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
public struct StatusT
|
||||||
|
{
|
||||||
|
//todo find out struct definition and implement it
|
||||||
|
}
|
||||||
|
}
|
||||||
+117
@@ -0,0 +1,117 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class for Genesis TimeT
|
||||||
|
/// </summary>
|
||||||
|
public class TimeT
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ctor:
|
||||||
|
/// - Takes actual date time as preset value
|
||||||
|
/// </summary>
|
||||||
|
public TimeT()
|
||||||
|
{
|
||||||
|
DateTimeUtc = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 1. Get seconds since 01. Jan 2000 00:00:00 UTC previously set by any call by
|
||||||
|
/// <see cref="UtcNowToSecondsSince2000"/> or <see cref="UtcAnyToSecondsSince2000"/>
|
||||||
|
/// or directly by this routine delivering the seconds e.g. read from meter,
|
||||||
|
/// 2. Set UTC seconds to analyze the corresponding time in UTC and use the ToString to
|
||||||
|
/// read the converted result.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks date="2023-Jul-19" author="Thomas Wiedebusch">
|
||||||
|
/// - Used <see cref="TimeT"/> to calculate time in UTC based on 01. Jan 2000.
|
||||||
|
/// </remarks>
|
||||||
|
/// <remarks date="2024-May-02" author="Thomas Wiedebusch">
|
||||||
|
/// - Setting of seconds and conversion to corresponding UTC sine 2000 or return seconds
|
||||||
|
/// from set DataTimeUtc.
|
||||||
|
/// </remarks>
|
||||||
|
public Int32 SecondsSince2000
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var secondsToTimeSpan = TimeSpan.FromSeconds(value);
|
||||||
|
DateTimeUtc = _fixedDateTime2000.Add(secondsToTimeSpan);
|
||||||
|
}
|
||||||
|
// DateTimeUtc has to be set in advance with seconds or by the call
|
||||||
|
// of UtcNowToSecondsSince2000 or UtcAnyToSecondsSince2000
|
||||||
|
get => UtcToSecondsSince2000();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get actual seconds (UTC now) since 01. Jan 2000 00:00:00 UTC.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks date="2024-May-02" author="Thomas Wiedebusch">
|
||||||
|
/// - Initial.
|
||||||
|
/// </remarks>
|
||||||
|
public Int32 UtcNowToSecondsSince2000
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DateTimeUtc = DateTime.UtcNow;
|
||||||
|
return UtcToSecondsSince2000();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Common routine to convert given UTC timestamp to seconds since 01. Jan 2000 00:00:00 UTC.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks date="2024-May-02" author="Thomas Wiedebusch">
|
||||||
|
/// - Initial.
|
||||||
|
/// </remarks>
|
||||||
|
private Int32 UtcToSecondsSince2000()
|
||||||
|
{
|
||||||
|
var timeSince2000Utc = DateTimeUtc - _fixedDateTime2000;
|
||||||
|
var secondsSince2000 = Convert.ToInt32(timeSince2000Utc.TotalSeconds);
|
||||||
|
return secondsSince2000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the seconds for a given timestamp based on 01.Jan.2000 00:00:00 UTC
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dateTimeUtc">time stamp dateTime for conversion</param>
|
||||||
|
/// <remarks date="2023-Jul-19" author="Thomas Wiedebusch">
|
||||||
|
/// - Optional input of dateTime to convert this to the time in UTC based on 01. Jan 2000.
|
||||||
|
/// </remarks>
|
||||||
|
public Int32 UtcAnyToSecondsSince2000(DateTime dateTimeUtc)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// limit dateTime to 01. Jan 2000 00:00:00 UTC
|
||||||
|
DateTimeUtc = dateTimeUtc < _fixedDateTime2000 ? _fixedDateTime2000 : dateTimeUtc;
|
||||||
|
return UtcToSecondsSince2000();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The date and time of 01.Jan 2000 00:00:00 UTC
|
||||||
|
/// </summary>
|
||||||
|
private static readonly DateTime _fixedDateTime2000 = new DateTime(2000, 1, 1, 0, 0, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converted date and time to UTC using the FixedDateTime2000
|
||||||
|
/// and the SecondsSince2000
|
||||||
|
/// </summary>
|
||||||
|
public DateTime DateTimeUtc
|
||||||
|
{
|
||||||
|
private set;
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get string of UTC date and time universal language with 24 hours format
|
||||||
|
/// </summary>
|
||||||
|
public override String ToString()
|
||||||
|
{
|
||||||
|
return $@"{DateTimeUtc:yyyy-MM-dd HH:mm:ss} UTC";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
public struct UInt672
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
internal class st_radio_dewa
|
||||||
|
{
|
||||||
|
//todo find out struct definition and implement it
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.DataTypes
|
||||||
|
{
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
|
internal class st_radio_tfx
|
||||||
|
{
|
||||||
|
//todo find out struct definition and implement it
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class AppSection
|
||||||
|
{
|
||||||
|
public UInt16 Id { get; set; }
|
||||||
|
|
||||||
|
public FwVersion Version { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<String, List<Build>> Builds { get; set; }
|
||||||
|
|
||||||
|
public IDictionary<String, Register> Registers { get; set; }
|
||||||
|
|
||||||
|
public IDictionary<String, Status> Status { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class AppsDictionary : Dictionary<string, AppSection>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class Build
|
||||||
|
{
|
||||||
|
public Int32 Id { get; set; }
|
||||||
|
|
||||||
|
public String FW { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class Details
|
||||||
|
{
|
||||||
|
public String Type { get; set; }
|
||||||
|
|
||||||
|
public Privilege Privilege { get; set; }
|
||||||
|
|
||||||
|
public String Description { get; set; }
|
||||||
|
|
||||||
|
public FwVersion Version { get; set; }
|
||||||
|
|
||||||
|
public String StaticType { get; set; }
|
||||||
|
|
||||||
|
public Value Values { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class FwVersion
|
||||||
|
{
|
||||||
|
public Int32? First { get; set; }
|
||||||
|
|
||||||
|
public Int32? Last { get; set; }
|
||||||
|
|
||||||
|
public Int32[] Exclude { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class JsonVersionsConverter : JsonConverter<FwVersion>
|
||||||
|
{
|
||||||
|
public override FwVersion ReadJson(JsonReader reader, Type objectType, FwVersion existingValue, Boolean hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var jobject = JToken.Load(reader);
|
||||||
|
|
||||||
|
if (jobject.Type != JTokenType.Object)
|
||||||
|
{
|
||||||
|
if (int.TryParse($"{jobject}", out var version))
|
||||||
|
{
|
||||||
|
return new FwVersion
|
||||||
|
{
|
||||||
|
First = version
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return jobject.ToObject<FwVersion>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, FwVersion value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Representing the 8 access levels meter register can have
|
||||||
|
/// </summary>
|
||||||
|
public class Privilege
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Value for first level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl1 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for second level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl2 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for third level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl3 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for fourth level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl4 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for fifth level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl5 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for sixth level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl6 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for seventh level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl7 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Value for eighth level
|
||||||
|
/// </summary>
|
||||||
|
public Access Lvl8 { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace TBF.Rig.RegisterReaders.GenesisRegReader.communication.Genesis.Registers.Json
|
||||||
|
{
|
||||||
|
public class Register
|
||||||
|
{
|
||||||
|
public Byte Id { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<Details> Details { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user