ControlBoards, IControlBoard iface, Devices, test methods.

This commit is contained in:
Milan Hanajik
2020-05-21 15:55:02 +02:00
parent 6df9261697
commit 7b321abb30
222 changed files with 3995 additions and 1284 deletions
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using Config.Entities;
using Dirichlet.Numerics;
using TBF.BenchControl.Generic;
using TBF.BenchControl.GenericDevices;
namespace TBF.BenchControl.ControlBoard
{
public interface IControlBoard : IDevice, IValveControl
{
void InitializeComponent(object thirdPartyComponent, UInt128 valvesToInvert, double[] tankCapacities);
/// <summary>
/// Display value in a third party component, update UI
/// </summary>
/// <param name="sParam">String parameter</param>
/// <param name="iParam">Integer parameter</param>
/// <param name="value">Value to be displayed</param>
void UpdateUI(string sParam, int iParam, double value);
/// <summary>
/// Called on beginning of a test
/// </summary>
void ClearProcessValues();
///
/// Values valid always
///
UInt128 Route { get; } /// State of valves
UInt64 DigitalInputs { get; } /// Inputs
///
/// Values valid during a test
///
double RefFrequency { get; }
double RefFlow { get; }
///
/// Last test results
///
double TestTime { get; }
int RefPulses { get; }
double TestTimeWM(int wmNr1);
int RefPulsesWM(int wmNr1);
int PulsesWM(int wmNr1);
float ValveOpenCloseTime { get; } /// Start valve open or close time in sec
/// <summary>
/// Select devices used for control and measurement
/// </summary>
/// <param name="outPath"></param>
OutputPath Devices { set; get; }
/// <summary>
/// Returns operation which sets the flow. Events:
/// </summary>
/// <param name="flowLimLo">Lower flow limit</param>
/// <param name="flowLimHi">Upper flow limit</param>
/// <param name="measuredFlow">Reference to calss keeping current flow value</param>
/// <param name="timeout">Timeout in seconds</param>
/// <returns>Operation</returns>
IOperation SetFlowOp(double flowLimLo, double flowLimHi, TBF.Boxes.DoubleBox measuredFlow, int timeout);
IOperation StopFlowRegulationOp();
/// <summary>
/// Start a test, Events: MeasurementStarted
/// </summary>
/// <param name="iParam">Int32 parameter 1</param>
/// <param name="dParam1">Double parameter 1 (alternative lower flow limit)</param>
/// <param name="dParam2">Double parameter 2 (alternative upper flow limit)</param>
/// <returns>Operation</returns>
IOperation StartTestOp(Test test, int iParam = 0, double dParam1 = 0, double dParam2 = 0);
IOperation ReadDiverterTransitionOp(bool isStart, Sequences.Statistics plotter, int batchNr, string testName, int repetition);
/// <summary>
/// Returns operation with events: Event.MeasurementCompleted or Event.None
/// </summary>
/// <returns>Operation</returns>
IOperation QueryMeasurementEndOp();
IOperation StopPreviousOp();
}
}
@@ -0,0 +1,112 @@
///
/// Copyright (c) 2013-2016 Sensus Metering Systems
///
using System;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public enum Command
{
None = 0, /// Do nothing, just transfer data (route, etc.)
Start = 1, /// Start the measurement
Stop = 2, /// Stop the measurement
ReadDivTransition = 4,
ReadExtremasOfPulses = 6,
ReadShapesOfPulses = 7,
ResetShapesOfPulses = 8,
EnduranceCycleParamsUpload = 11,
EnduranceCycleStart = 12,
EnduranceCycleStop = 13,
}
#if FUZHOU_300 || IZRAEL_200 || TORINO_50 || BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL || BADGER_VELKA_TRAT || PUCHONG_200 || SLM_50 || GENESIS || MALTA_WSD25 || SLM_END || WARSAW_END || BERLIN || GELSENWASSER || SLM_150 || PETERSBURG_50 || PETERSBURG_200 || DEWA_300 || ROMA_200 || KEMPNO_50 || BAHRAIN_50 || RUM_MOB || KRAKOW_50 || JUZNA_AFRIKA_50 || IZRAEL_25 || ZAMBIA || ZODINO || FEWA_50 || FILIPINY_50 || SENTEC || FUZHOU_100 || CEVAK_200 || IZRAEL_50 || ALZIR_25
public enum StatusP : ulong
{
RefFlowMeterMask = 0x7, /// bits 0,1,2
TestCompleted = 0x10, /// bit 4
TestInProgress = 0x20, /// bit 5
//DiverterError = 0x20,
MsrmntWithDivrtr = 0x80, /// bit 7
RefPulsesMsrmnt = 0x100, /// bit 8
SynchroMethod = 0x800, /// bit 11
WaterMeterCalib = 0x1000, /// bit 12
EmgcyStopActive = 0x2000, /// bit 13
MsrmntGateActive = 0x4000, /// bit 14
TimeMeasurement = 0x8000, /// bit 15
FrequencyMeasured = 0x80000, /// bit 19
CoaxRegValveBusy = 0x200000, /// bit 21
ADCError = 0x80000000, /// bit 31
TimeoutOccured = 0x100000000, /// bit 32
MeretAddresses = 0x200000000, /// bit 33
SampleBuferFull = 0x400000000, /// bit 34
ErrorTempMeter = 0x10000000000, /// bit 40
ErrorPressMeter1 = 0x1000000000000, /// bit 48
ErrorPressMeter2 = 0x2000000000000, /// bit 49
ErrorPressMeter3 = 0x4000000000000, /// bit 50
ErrorPressMeter4 = 0x8000000000000, /// bit 51
EndurCycleRunning = 0x200000000000000, /// bit 57
}
#else // DN100 || MUNICH || FUZHOU_150 || TORUN_50 || CEVAK_40 || MURES_40 || FUZHOU_50
public enum StatusP : ulong
{
RefFlowMeterMask = 0x7,
TestInProgress = 0x8,
TestCompleted = 0x10,
DiverterError = 0x20,
MsrmntWithDivrtr = 0x80,
RefPulsesMsrmnt = 0x100,
SynchroMethod = 0x800,
WaterMeterCalib = 0x1000,
EmgcyStopActive = 0x2000,
MsrmntGateActive = 0x4000,
TimeMeasurement = 0x8000,
FrequencyMeasured = 0x80000,
CoaxRegValveBusy = 0x200000, /// bit 21
ADCError = 0x80000000,
TimeoutOccured = 0x100000000,
MeretAddresses = 0x200000000,
SampleBuferFull = 0x400000000,
ErrorTempMeter = 0x10000000000,
ErrorPressMeter1 = 0x1000000000000,
ErrorPressMeter2 = 0x2000000000000,
ErrorPressMeter3 = 0x4000000000000,
ErrorPressMeter4 = 0x8000000000000,
}
#endif
public enum StopDevs
{
None = 0,
Reference = 0x01, /// bit 0
Diverter = 0x02, /// bit 1
GatePulse = 0x08, /// bit 3
TimeMeasurement = 0x10, /// bit 4
BypassDevCoupled2Div = 0x20, /// bit 5
RegValveRegulation = 0x80, /// bit 7
All = 0xFF,
}
public enum TestMethods
{
Diverter = 0x01, /// bit 0: 1=Mass method (diverters are used), 0=Volume method
FixedStart = 0x02, /// bit 1: 1=fixed start method, 0=flying start method
Synchro = 0x04, /// bit 2: 1=Synchro method, 0=Pulse counting
MassAndContinue = 0x10, /// bit 4: 1=Method with mass measurement followed by a volume method
}
public enum RegulValveMode : byte
{
None = 0,
PulseWidth = 1, /// Pulse width 0.05 .. 2 sec.
TargetPosition = 2, /// Low and high position (=DAC value) limits (0..100%) are specified
TargetFrequency = 3, /// Low and high frequency limits (0..2000Hz,0..2200Hz) are specified
Stop = 4, /// Stop regulation
}
public enum RegulValveState
{
Idle = 0,
PwOrFreqRegul = 2,
DacValueRegul = 3,
}
}
@@ -0,0 +1,46 @@
///
/// Copyright (c) 2013-2016 Sensus Metering Systems
///
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ControlBoardCfg : ComponentCfgBase, IComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(ControlBoardCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl() { return new ControlBoardCfgCtrl(); }
///
/// Serialized parameters
///
public int ComPortNr;
public int VirtualValvesRangeLo;
public int VirtualValvesRangeHi;
/// Private parameterless constructor invoked by all other (public) constructors
ControlBoardCfg()
{
Name = "CB";
ParentName = string.Empty;
ComPortNr = 1;
VirtualValvesRangeLo = 50;
VirtualValvesRangeHi = 59;
}
public ControlBoardCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("Name={0}, Com{1}, VVRangeLo={2}, VVRangeHi={3}", Name, ComPortNr, VirtualValvesRangeLo, VirtualValvesRangeHi);
}
}
}
@@ -0,0 +1,155 @@
///
/// Copyright (c) 2013-2015 Sensus Metering Systems
///
namespace TBF.BenchControl.ControlBoard.Legacy
{
partial class ControlBoardCfgCtrl
{
/// <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.comPortNrLabel = new System.Windows.Forms.Label();
this.comPortNrTextBox = new System.Windows.Forms.TextBox();
this.classNameLabel = new System.Windows.Forms.Label();
this.nameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeLoTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeLoLabel = new System.Windows.Forms.Label();
this.vValveRangeHiTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeHiLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// comPortNrLabel
//
this.comPortNrLabel.AutoSize = true;
this.comPortNrLabel.Location = new System.Drawing.Point(42, 85);
this.comPortNrLabel.Name = "comPortNrLabel";
this.comPortNrLabel.Size = new System.Drawing.Size(95, 13);
this.comPortNrLabel.TabIndex = 0;
this.comPortNrLabel.Text = "Serial port number:";
//
// comPortNrTextBox
//
this.comPortNrTextBox.Enabled = false;
this.comPortNrTextBox.Location = new System.Drawing.Point(166, 82);
this.comPortNrTextBox.Name = "comPortNrTextBox";
this.comPortNrTextBox.Size = new System.Drawing.Size(80, 20);
this.comPortNrTextBox.TabIndex = 1;
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(163, 26);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 2;
this.classNameLabel.Text = "ComonentName";
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(42, 60);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 3;
this.nameLabel.Text = "Name";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(166, 57);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(80, 20);
this.nameTextBox.TabIndex = 4;
//
// vValveRangeLoTextBox
//
this.vValveRangeLoTextBox.Enabled = false;
this.vValveRangeLoTextBox.Location = new System.Drawing.Point(166, 107);
this.vValveRangeLoTextBox.Name = "vValveRangeLoTextBox";
this.vValveRangeLoTextBox.Size = new System.Drawing.Size(80, 20);
this.vValveRangeLoTextBox.TabIndex = 6;
//
// vValveRangeLoLabel
//
this.vValveRangeLoLabel.AutoSize = true;
this.vValveRangeLoLabel.Location = new System.Drawing.Point(42, 110);
this.vValveRangeLoLabel.Name = "vValveRangeLoLabel";
this.vValveRangeLoLabel.Size = new System.Drawing.Size(118, 13);
this.vValveRangeLoLabel.TabIndex = 5;
this.vValveRangeLoLabel.Text = "Virtual valves range Lo:";
//
// vValveRangeHiTextBox
//
this.vValveRangeHiTextBox.Enabled = false;
this.vValveRangeHiTextBox.Location = new System.Drawing.Point(166, 132);
this.vValveRangeHiTextBox.Name = "vValveRangeHiTextBox";
this.vValveRangeHiTextBox.Size = new System.Drawing.Size(80, 20);
this.vValveRangeHiTextBox.TabIndex = 8;
//
// vValveRangeHiLabel
//
this.vValveRangeHiLabel.AutoSize = true;
this.vValveRangeHiLabel.Location = new System.Drawing.Point(42, 135);
this.vValveRangeHiLabel.Name = "vValveRangeHiLabel";
this.vValveRangeHiLabel.Size = new System.Drawing.Size(116, 13);
this.vValveRangeHiLabel.TabIndex = 7;
this.vValveRangeHiLabel.Text = "Virtual valves range Hi:";
//
// ControlBoardCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.vValveRangeHiTextBox);
this.Controls.Add(this.vValveRangeHiLabel);
this.Controls.Add(this.vValveRangeLoTextBox);
this.Controls.Add(this.vValveRangeLoLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Controls.Add(this.comPortNrTextBox);
this.Controls.Add(this.comPortNrLabel);
this.Name = "ControlBoardCfgCtrl";
this.Size = new System.Drawing.Size(300, 200);
this.Load += new System.EventHandler(this.ControlBoardCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label comPortNrLabel;
private System.Windows.Forms.TextBox comPortNrTextBox;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.TextBox vValveRangeLoTextBox;
private System.Windows.Forms.Label vValveRangeLoLabel;
private System.Windows.Forms.TextBox vValveRangeHiTextBox;
private System.Windows.Forms.Label vValveRangeHiLabel;
}
}
@@ -0,0 +1,105 @@
///
/// Copyright (c) 2013-2015 Sensus Slovensko a.s.
///
using System;
using System.Windows.Forms;
using Config.Entities;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public partial class ControlBoardCfgCtrl : UserControl, IComponentCfgCtrl
{
public bool ShowMore { get { return false; } }
ControlBoardCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as ControlBoardCfg;
Redraw();
}
}
public ControlBoardCfgCtrl()
{
InitializeComponent();
}
private void ControlBoardCfgCtrl_Load(object sender, EventArgs e)
{
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;
comPortNrTextBox.Text = config.ComPortNr.ToString();
vValveRangeLoTextBox.Text = config.VirtualValvesRangeLo.ToString();
vValveRangeHiTextBox.Text = config.VirtualValvesRangeHi.ToString();
}
public void Unlock()
{
nameTextBox.Enabled = true;
comPortNrTextBox.Enabled = true;
vValveRangeLoTextBox.Enabled = true;
vValveRangeHiTextBox.Enabled = true;
}
/// <summary>
/// Verify current user controls for validity.
/// Called when user wants to close/save the configuration.
/// </summary>
/// <returns>See VerifyFlags</returns>
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (!int.TryParse(comPortNrTextBox.Text, out dummy) || dummy <= 0 || dummy > 999)
{
message += "Control board serial port number is invalid";
flags |= CfgUpdateFlags.Error;
}
if (!int.TryParse(vValveRangeLoTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
message += "Virtual valves range Lo is invalid";
flags |= CfgUpdateFlags.Error;
}
if (!int.TryParse(vValveRangeHiTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
message += "Virtual valves range Hi is invalid";
flags |= CfgUpdateFlags.Error;
}
return flags;
}
/// <summary>
/// Save settings to the configuration class.
/// </summary>
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
this.Name = nameTextBox.Text;
this.config.ComPortNr = int.Parse(comPortNrTextBox.Text);
this.config.VirtualValvesRangeLo = int.Parse(vValveRangeLoTextBox.Text);
this.config.VirtualValvesRangeHi = int.Parse(vValveRangeHiTextBox.Text);
return flags;
}
}
}
@@ -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,859 @@
///
/// Copyright (c) 2013-2020 Sensus Slovensko a.s.
///
using System;
using System.Text;
using System.IO.Ports;
using System.Collections.Generic;
using log4net;
using Config.Entities;
using Dirichlet.Numerics;
using TBF.BenchControl.Generic;
using TBF.BenchControl.GenericDevices;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ControlBoardDev : ComponentBase, IControlBoard
{
private static readonly ILog log = LogManager.GetLogger(typeof(ControlBoardDev));
public override string ToString() { return string.Format("ControlBoard({0})", Cfg.ToString(1)); }
const int FiltersCount = 8;
readonly ControlBoardCfg controlBoardCfg;
///
/// Reference to the control board component
///
IControlCom controlCom;
/// <summary>
/// Emergency stop signal state updated by RunDevice().
/// 'modelessDlg' is notified about the changes and uses this information to change its visibility.
/// </summary>
//UiBridge.EmergencyStopEventArgs.State emergencyStopState;
///
/// Public read only wrappers for Elde software component fields
///
/// rvNr1 = 1 .. nrRegulValves (one based)
/// wmNr0 = 0 .. nrWaterMeters-1 (zero based)
/// tmpNr0 = 0 .. nrTempMeters-1 (zero based)
/// prsNr0 = 0 .. nrPressureMeters-1 (zero based)
///
public StatusP StatusP { get { return controlCom.StatusP; } }
public int EtPulsesK { get { return controlCom.EtPulsesK; } }
public int EtPulses2 { get { return controlCom.EtPulses2; } } /// DEWA_300: pulses from I12
public int EtPulses3 { get { return controlCom.EtPulses3; } } /// DEWA_300: pulses from I13
public int WMeterReference(int wmNr0) { return controlCom.WMeterReference(wmNr0); }
public uint RegulValveDAC(int rvNr0) { return controlCom.RegulValveDAC(rvNr0); }
public float Pressure(int prsNr0) { return controlCom.Pressure(prsNr0); }
public float Temperature(int tmpNr0) { return controlCom.Temperature(tmpNr0); }
public RegulValveState RegulValveState(int rvNr1) { return (RegulValveState)controlCom.RegulValveState(rvNr1); }
public UInt128 RRoute { get { return controlCom.RRoute; } }
public uint DivTime(int id) { return controlCom.DivTime(id); }
public uint FmState { get { return controlCom.FmState; } }
public uint BeginState(int wmNr0) { return controlCom.BeginState(wmNr0); }
public double RefFrequency { get { return controlCom.ReferenceFreq; } } /// In [Hz] 0..2500, nom.=2000
public double ReferenceFreqs(int i) { return controlCom.ReferenceFreqs(i); }
public double RefFlow { get { return controlCom.ReferenceFlow; } }
public float RValvePosition(int rvNr1) { return controlCom.RValvePosition(rvNr1); }
public float DivSamples(int time, int divIx0) { return controlCom.DivSamples(time, divIx0); } /// Time unit is 2 ms
public int ScopeSamples(int i, int j, int k) { return controlCom.ScopeSamples(i, j, k); }
public ulong DigitalInputs { get { return controlCom.DigitalInputs; } }
public float AnalogInput(int adcNr0) { return controlCom.AnalogInput(adcNr0); }
public uint AnalogInputRaw(int adcNr0, int bank) { return controlCom.AnalogInputRaw(adcNr0, bank); } /// adcNr0: 0=RV1, 1=RV2,... bank: 0=RV, 1=Temp
public float ReferenceVolume { get { return controlCom.TotalRefVolume; } }
public float VyslExt(int wmNr0, int what) { return controlCom.VyslExt(wmNr0, what); }
public uint[] CyclePar { get { return controlCom.CyclePar; } set { controlCom.CyclePar = value; } }
public uint[] WMeterCont { get { return controlCom.WMeterCont; } }
public bool ManualUIAllowed { get { return controlCom.ManualUIAllowed; } set { controlCom.ManualUIAllowed = value; } }
public float ValveOpenCloseTime { get { return controlCom.ValveOpenCloseTime; } }
/// <summary> Extracted from the StatusP in RunDeviceBefore </summary>
public bool EmergencyStop { get { return emergencyStop; } }
bool emergencyStop;
bool previousEmergencyStop;
///
/// IControlBoard interface
///
public int RefPulses { get { return controlCom.EtPulses(0); } }
public double TestTime { get { return (double)controlCom.TTime; } }
public int RefPulsesWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? controlCom.EtPulses(wmNr1) : 0; }
public int PulsesWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? unwrappedWMPulses[wmNr1] : 0; }
public double TestTimeWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? (double)controlCom.ImpulseTime(wmNr1) : 0; }
public void DiverterToTank(int flowMtrNr)
{
cbCommandQueue.Enqueue(new SendCommandArgs(Command.Start, flowMtrNr, int.MaxValue, int.MaxValue, TestMethods.Diverter | TestMethods.Synchro, StopDevs.None));
}
///
public void DiverterToSink(int flowMtrNr)
{
cbCommandQueue.Enqueue(new SendCommandArgs(Command.Stop, 0, 0, 0, TestMethods.Diverter,
StopDevs.Diverter | StopDevs.GatePulse | StopDevs.Reference | StopDevs.RegValveRegulation));
}
///
public void ReadDiverterTransition(int flowMtrNr)
{
cbCommandQueue.Enqueue(new SendCommandArgs(Command.ReadDivTransition, flowMtrNr, 0, 0, TestMethods.Diverter, 0));
}
///
/// Initialization data updated by constructors of children components
/// and sent to 'controlCom2panel' by SendCalibData() method.
///
#if DEWA_300 || ROMA_200
public float[,] TempCalibData = new float[16, 5];
#else
public float[,] TempCalibData = new float[8, 5];
#endif
public uint[] DiverterEdge; /// Created and initialized in this.Initialize()
public byte MeretProtocol = 0;
// Array length is the reference flowmeters count plus one
#if DN100 || FUZHOU_150 || FUZHOU_300 || IZRAEL_200 || SLM_150 || SENTEC || CEVAK_200
public uint[,] RegValveCalib = new uint[8,2] {{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500}};
public byte[] MeretRS485Address = new byte[4];
public float[] EtCalib = new float[6] { 1, 1, 1, 1, 1, 1 };
#elif PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER || PETERSBURG_200 || RUM_MOB
public uint[,] RegValveCalib = new uint[16, 2] { {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500},
{100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500} };
public byte[] MeretRS485Address = new byte[4];
public float[] EtCalib = new float[7] { 1, 1, 1, 1, 1, 1, 1 };
#elif DEWA_300
public uint[,] RegValveCalib = new uint[16, 2] { {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500},
{100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500} };
public byte[] MeretRS485Address = new byte[4];
public float[] EtCalib = new float[8] { 1, 1, 1, 1, 1, 1, 1, 1 };
#elif ROMA_200
public uint[,] RegValveCalib = new uint[16, 2] { {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500},
{100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500}, {100,500} };
public byte[] MeretRS485Address = new byte[5];
public float[] EtCalib = new float[8] { 1, 1, 1, 1, 1, 1, 1, 1 };
#elif MUNICH || TORINO_50 || BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || TORUN_50 || CEVAK_40 || TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL || MURES_40 || FUZHOU_50 || SLM_50 || PETERSBURG_50 || KEMPNO_50 || BAHRAIN_50 || KRAKOW_50 || JUZNA_AFRIKA_50 || IZRAEL_25 || ZAMBIA || ZODINO || FEWA_50 || FILIPINY_50 || FUZHOU_100 || ALZIR_25
public uint[,] RegValveCalib = new uint[8,2] {{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500}};
#if IZRAEL_50
public byte[] MeretRS485Address = new byte[6];
#else
public byte[] MeretRS485Address = new byte[4];
#endif
public float[] EtCalib = new float[5] { 1, 1, 1, 1, 1 };
#elif IZRAEL_50 || SLM_END
public uint[,] RegValveCalib = new uint[8,2] {{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500}};
public byte[] MeretRS485Address = new byte[7]; /// Modbus addresses of Pr1, Pr2, Pr3, Pr4, Pr5, Pr6, T9 (Izrael) or Pr1, Pr2, Pr3, Pr4, T9, T10 (SLM END)
public float[] EtCalib = new float[5] { 1, 1, 1, 1, 1 };
#elif WARSAW_END
public uint[,] RegValveCalib = new uint[8,2] {{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500}};
public byte[] MeretRS485Address = new byte[4];
public float[] EtCalib = new float[5] { 1, 1, 1, 1, 1 };
#elif MALTA_WSD25
public uint[,] RegValveCalib = new uint[8,2] {{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500}};
public byte[] MeretRS485Address = new byte[12];
public float[] EtCalib = new float[9] { 1, 1, 1, 1, 1, 1, 1, 1, 1 };
#elif BADGER_VELKA_TRAT
public uint[,] RegValveCalib = new uint[8,2] {{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500},{100,500}};
public byte[] MeretRS485Address = new byte[4];
public float[] EtCalib = new float[8] { 1, 1, 1, 1, 1, 1, 1, 1 };
#endif
public uint[] BalanceRange = new uint[2];
Queue<SendCommandArgs> cbCommandQueue;
/// <summary>
/// Route: 128-bit word representing the current state of all valves and pumps
/// </summary>
public UInt128 Route { get { return benchModelRoute; } }
UInt128 benchModelRoute;
UInt128 valvesToInvert; /// Route = valesToInvert ^ RequiredRouteWithoutInvertedVlaves
readonly UInt128 routeMask; /// This mask masks out virtual valves, routeMask is set in the constructor
public OutputPath Devices
{
set { devices = value; }
get { return devices; }
}
OutputPath devices;
int[] filters;
float[] FMFreq;
int regConst;
int shortImp;
private int[] unwrappedWMPulses; /// Initialized to zeros
///
private void UnwrapPulses()
{
for (int i = 1; i <= Config.Data.WMsCount; i++)
{
UInt16 lowerWord = controlCom.WMeterPulses(i);
int uncorrected = (int)(((uint)unwrappedWMPulses[i] & 0xFFFF0000) | lowerWord);
if (Math.Abs(uncorrected - unwrappedWMPulses[i]) <= Int16.MaxValue)
{
unwrappedWMPulses[i] = uncorrected;
}
else if (Math.Abs(uncorrected + 65536 - unwrappedWMPulses[i]) <= Int16.MaxValue)
{
unwrappedWMPulses[i] = uncorrected + 65536;
}
else if (Math.Abs(uncorrected - 65536 - unwrappedWMPulses[i]) <= Int16.MaxValue)
{
unwrappedWMPulses[i] = uncorrected - 65536;
}
}
}
///
public void ClearProcessValues()
{
for (int i = 1; i <= Config.Data.WMsCount; i++)
{
unwrappedWMPulses[i] = 0;
}
UnwrapPulses();
}
/// <summary>
/// Set values to be applied during a test
/// </summary>
/// <param name="filters">Filters for meter pulses</param>
/// <param name="pid">PID coefficient for flow control</param>
/// <param name="shortPulses">Short pulses mode (0 = off)</param>
public void SetFiltersPidShortPulses(int[] filters, float pid, int shortPulses)
{
this.filters = filters;
this.regConst = (int)pid;
this.shortImp = shortPulses;
}
/// <summary>
/// Sets the power of the selected pump
/// </summary>
/// <param name="index">Pump index 0 .. 5, invalid pump index sets all pumps to 0%</param>
/// <param name="power">Power (0.0f .. 100.0f)</param>
public void SetFMFreq(int index, float power)
{
bool pumpPowerChanged = false;
#if DN100 || MUNICH || FUZHOU_150
if ((FMFreq == null) || (FMFreq.Length != 2))
{
FMFreq = new float[2];
pumpPowerChanged = true; /// Forces sending a command using Command.None
}
#elif SLM_END || WARSAW_END
if ((FMFreq == null) || (FMFreq.Length != 4))
{
FMFreq = new float[4];
pumpPowerChanged = true; /// Forces sending a command using Command.None
}
#else /// all newer benches
if ((FMFreq == null) || (FMFreq.Length != 6))
{
FMFreq = new float[6];
pumpPowerChanged = true; /// Forces sending a command using Command.None
}
#endif
if (index >= 0 && index < FMFreq.Length)
{
if (FMFreq[index] != power)
{
FMFreq[index] = power;
pumpPowerChanged = true; /// Forces sending a command using Command.None
string s = string.Format("FMFreq=[{0}{1}{2}{3}{4}{5}]", FMFreq[0].ToString(),
(FMFreq.Length > 1) ? ("," + FMFreq[1].ToString()) : "",
(FMFreq.Length > 2) ? ("," + FMFreq[2].ToString()) : "",
(FMFreq.Length > 3) ? ("," + FMFreq[3].ToString()) : "",
(FMFreq.Length > 4) ? ("," + FMFreq[4].ToString()) : "",
(FMFreq.Length > 5) ? ("," + FMFreq[5].ToString()) : "");
log.Warn(s);
}
}
else
{
log.ErrorFormat("Wrong FM pump: index = {0}, power = {1}", index, power);
}
}
public void UpdateWeights()
{
/// Mettler-Toledo scales
for (int i = 0; i < MettlerToledo.Standard.BalanceDev.BalancesCount; i++)
{
controlCom.SetWeight(i, MettlerToledo.Standard.BalanceDev.Masses[i]);
}
/// Mettler-Toledo-multi scales (Badger Brno, ...)
for (int i = 0; i < MettlerToledo.Multi.BalanceDev.BalancesCount; i++)
{
controlCom.SetWeight(i, MettlerToledo.Multi.BalanceDev.Masses[i]);
}
/// Tanks (CEVAK, ...)
int firstTankIx = Math.Max(MettlerToledo.Standard.BalanceDev.BalancesCount, MettlerToledo.Multi.BalanceDev.BalancesCount);
for (int i = 0; i < Various.TankWithLevelMsrmnt.Tank.TanksCount; i++)
{
controlCom.SetWeight(firstTankIx + i, Various.TankWithLevelMsrmnt.Tank.Tanks[i].Volume);
}
}
/// <summary>
/// Display value in ELDE component, update UI
/// </summary>
/// <param name="sParam">String parameter</param>
/// <param name="iParam">Integer parameter</param>
/// <param name="value">Value to be displayed</param>
public void UpdateUI(string sParam, int iParam, double value)
{
if (sParam == "ReservoirTemp")
{
controlCom.SetReservoirTemp(iParam, (float)value);
return;
}
}
public ControlBoardDev()
{
}
/// <summary>
/// Constructor
/// </summary>
public ControlBoardDev(Generic.IComponentCfg cfg)
: base(cfg)
{
controlBoardCfg = cfg as ControlBoardCfg;
/// Buffers used in UnwrapPulses() and PulsesWM(wmNr1)
unwrappedWMPulses = new int[Config.Data.WMsCount + 1];
cbCommandQueue = new Queue<SendCommandArgs>();
lastSentFilters = new uint[FiltersCount];
#if DN100 || MUNICH || FUZHOU_150
FMFreq = new float[2] { 0, 0 }; /// Nr. of pumps: Fuzhou150=2, Fuzhou300=6, newer=6
#elif SLM_END || WARSAW_END
FMFreq = new float[4] { 0, 0, 0, 0 };
#else /// all newer benches
FMFreq = new float[6] { 0, 0, 0, 0, 0, 0 };
#endif
emergencyStop = false;
previousEmergencyStop = false;
routeMask = 0;
for (int i = 0; i < 128; i++)
{
if (i < controlBoardCfg.VirtualValvesRangeLo || i > controlBoardCfg.VirtualValvesRangeHi)
{
routeMask = routeMask | ((UInt128)1 << i);
}
}
log.Debug(this.ToString());
}
/// <summary>
/// Specific pre-initialization for control board only
/// </summary>
/// <param name="eldeComponent"></param>
public void InitializeComponent(object eldeComponent, UInt128 valvesToInvert, double[] tankCapacities)
{
this.valvesToInvert = valvesToInvert;
this.benchModelRoute = valvesToInvert;
if (controlBoardCfg.DebugLevel == DebugMode.Simulate)
{
controlCom = new ControlComSim();
}
else
{
controlCom = new ControlComWrap(eldeComponent);
/// Default number of scales (and BalanceRange length) is 2.
/// If there are more scales, BalanceRange length is larger.
int scalesCnt = Math.Max(2, tankCapacities.Length);
BalanceRange = new uint[scalesCnt];
for (int i = 0; i < scalesCnt; i++)
{
BalanceRange[i] = (uint)((i < tankCapacities.Length) ? tankCapacities[i] : 0);
}
}
}
/// <summary>Initialize this device</summary>
public void Initialize()
{
int divertersCount = BenchControl.Elde.Diverter.Diverter.DivertersCount;
DiverterEdge = new uint[divertersCount];
for (int i = 0; i < divertersCount; i++)
{
DiverterEdge[i] = (uint)BenchControl.Elde.Diverter.Diverter.Diverters[i].ThresholdGate;
controlCom.SetDivLimit(i, (uint)BenchControl.Elde.Diverter.Diverter.Diverters[i].ThresholdLo,
(uint)BenchControl.Elde.Diverter.Diverter.Diverters[i].ThresholdHi);
}
controlCom.SendCalibData(RegValveCalib,
MeretRS485Address,
controlBoardCfg.ComPortNr,
TempCalibData,
EtCalib,
DiverterEdge,
BalanceRange,
MeretProtocol);
log.FatalFormat("Successfully initialized device {0}", ToString());
}
/// <summary>
/// Run this device
/// </summary>
public void RunDeviceBefore()
{
if (DebugLevel == DebugMode.Simulate) return;
controlCom.RunDeviceBefore();
UnwrapPulses();
}
///
/// Used in RunDeviceAfter() when sending commands
///
UInt128 lastSentRoute;
uint[] lastSentFilters;
float[] lastSentFMFreq;
int lastsentRegConst;
int lastSentShortImp;
/// <summary>
/// Run this device
/// </summary>
public void RunDeviceAfter()
{
if (DebugLevel == DebugMode.Simulate) return;
previousEmergencyStop = emergencyStop;
emergencyStop = (controlCom.StatusP & StatusP.EmgcyStopActive) != 0;
///
if (emergencyStop)
{
/// Modify the queue in case of an emergency stop
cbCommandQueue.Clear();
cbCommandQueue.Enqueue(new SendCommandArgs(Command.Stop, 0, 0, 0, 0, StopDevs.All));
}
///
if (emergencyStop && !previousEmergencyStop)
{
/// Stop the cycle - Press 'STOP' button in the BenchControlPanel
TBF.UiBridge.Bridge.Ui2Bench(TBF.UiBridge.UI2BenchCmd.Stop);
Program.MainWnd.Invoke(new ShowEmergencyStopFormDlgt(ShowEmergencyStopForm));
}
else if (!emergencyStop && previousEmergencyStop)
{
UiBridge.Bridge.OnEmergencyStopChanged(this, UiBridge.EmergencyStopEventArgs.State.CloseForm);
}
UpdateWeights();
/// Prepare uintFilters array and evaluate changes
uint[] uintFilters = new uint[FiltersCount];
uintFilters[0] = (filters != null && filters.Length > 0) ? (uint)filters[0] : 0;
bool filtersAreDifferent = (uintFilters[0] != lastSentFilters[0]);
for (int i = 1; i < FiltersCount; i++)
{
uintFilters[i] = (filters != null && filters.Length > i) ? (uint)filters[i] : uintFilters[i - 1];
filtersAreDifferent |= (uintFilters[i] != lastSentFilters[i]);
}
/// Evaluate FMFreq changes
bool fmFreqsAreDifferent = false;
if ((FMFreq == null) || (lastSentFMFreq == null) || (FMFreq.Length != lastSentFMFreq.Length))
{
fmFreqsAreDifferent = true;
}
else
{
for (int i = 0; i < FMFreq.Length; i++)
{
if (FMFreq[i] != lastSentFMFreq[i])
{
fmFreqsAreDifferent = true;
break;
}
}
}
if (cbCommandQueue.Count > 0)
{
///
/// There is a command in the queue
///
controlCom.SendCommand(cbCommandQueue.Dequeue(), (benchModelRoute & routeMask), uintFilters, FMFreq, regConst, shortImp);
lastSentRoute = (benchModelRoute & routeMask);
lastsentRegConst = regConst;
lastSentShortImp = shortImp;
/// Deep copy of arrays lastSentFilters and lastSentFMFreq
if ((lastSentFilters == null) || (lastSentFilters.Length != uintFilters.Length)) lastSentFilters = new uint[(uintFilters == null) ? 0 : uintFilters.Length];
for (int i = 0; i < lastSentFilters.Length; i++) lastSentFilters[i] = uintFilters[i];
if ((lastSentFMFreq == null) || (lastSentFMFreq.Length != FMFreq.Length)) lastSentFMFreq = new float[(FMFreq == null) ? 0 : FMFreq.Length];
for (int i = 0; i < lastSentFMFreq.Length; i++) lastSentFMFreq[i] = FMFreq[i];
}
else if (((benchModelRoute & routeMask) != lastSentRoute) ||
filtersAreDifferent ||
fmFreqsAreDifferent ||
(lastsentRegConst != regConst) ||
(lastSentShortImp != shortImp))
{
///
/// Command queue is mepty, but 'route', pump frequency, PID coef., filters or shortImp have changed
///
controlCom.SendCommand(new SendCommandArgs(Command.None, 0, 0, 0, 0, 0), (benchModelRoute & routeMask), uintFilters, FMFreq, regConst, shortImp);
lastSentRoute = (benchModelRoute & routeMask);
lastsentRegConst = regConst;
lastSentShortImp = shortImp;
/// Deep copy of arrays lastSentFilters and lastSentFMFreq
if ((lastSentFilters == null) || (lastSentFilters.Length != uintFilters.Length)) lastSentFilters = new uint[(uintFilters == null) ? 0 : uintFilters.Length];
for (int i = 0; i < lastSentFilters.Length; i++) lastSentFilters[i] = uintFilters[i];
if ((lastSentFMFreq == null) || (lastSentFMFreq.Length != FMFreq.Length)) lastSentFMFreq = new float[(FMFreq == null) ? 0 : FMFreq.Length];
for (int i = 0; i < lastSentFMFreq.Length; i++) lastSentFMFreq[i] = FMFreq[i];
}
controlCom.RunDeviceAfter();
}
/// <summary>Stop this device</summary>
public void StopDevice() { }
public void StopDevice2() { }
delegate void ShowEmergencyStopFormDlgt();
///
void ShowEmergencyStopForm()
{
(new TBF.UI.Shared.EmergencyStopForm()).Show();
}
/// <summary>
/// Sends a command to the control board.
/// </summary>
/// <param name="cmd">See ControlBoard.Command enum</param>
/// <param name="refFlowmtrNr">Number of the etalon/reference (1..5)</param>
/// <param name="route">State of valves, 128-bit word</param>
/// <param name="totalRefPulses">kolko impulzov ma trvat skuska</param>
/// <param name="testMethods">See ControlBoard.TestMethods enum</param>
/// <param name="filterConstant">0 = No filtering (0..255)</param>
/// <param name="regConst">Coefficient used to control reg. valves (1..100)</param>
/// <param name="shortImp">0 = default value, 1 = spec. processing of very short pulses</param>
/// <param name="stopDevs">What to stop</param>
public void SendCommand(Command cmd, int refFlowmtrNr, int totalRefPulses, int massRefPulses,
TestMethods testMethods, StopDevs stopDevs)
{
cbCommandQueue.Enqueue(new SendCommandArgs(cmd, refFlowmtrNr, totalRefPulses, massRefPulses, testMethods, stopDevs));
}
/// <summary>
/// Set valves to new positions.
/// </summary>
/// <param name="valvesToOpen">'ulong' with bits of valves to open set to '1'</param>
/// <param name="valvesToClose">'ulong' with bits of valves to close set to '1'</param>
/// <returns>'ulong' with bits of valves that have changed set to '1'</returns>
public UInt128 SetValves(UInt128 valvesToOpen, UInt128 valvesToClose, IList<TBF.BenchControl.BuiltIn.ValveEx.Valve> extendedValves)
{
UInt128 oldRoute = benchModelRoute;
benchModelRoute = (((benchModelRoute ^ valvesToInvert) | valvesToOpen) & (~valvesToClose)) ^ valvesToInvert;
foreach (var extV in extendedValves) extV.UpdateRoute(ref benchModelRoute);
log.DebugFormat("SetValves(x,x), new route = {0}", Utils.RouteToStr(benchModelRoute));
return oldRoute ^ benchModelRoute;
}
/// <summary>
/// Set Route to value returned by RRoute. Take virtual valves into account.
/// </summary>
/// <returns>'ulong' with bits of valves that have changed set to '1'</returns>
public UInt128 SyncRoute()
{
UInt128 oldRoute = benchModelRoute;
benchModelRoute = (RRoute & routeMask) | (benchModelRoute & (~routeMask));
log.DebugFormat("SyncRoute(), new route = {0}", Utils.RouteToStr(benchModelRoute));
return oldRoute ^ benchModelRoute;
}
/// <summary>
/// Regulation valves control.
/// </summary>
/// <param name="valveNo">Regulation valve nr. (1..5)</param>
/// <param name="valveMode">Valve regulation mode, see the enum</param>
/// <param name="valveValue">Target position or target frequency or pulse duration</param>
/// <param name="stableTime">Stabilization time when setting the flow: 0=200ms, step 50ms, max. 1.5 sec.</param>
public void ValveMove(int valveNo, RegulValveMode valveMode, float[] valveValue, int stableTime)
{
controlCom.ValveMove(valveNo, valveMode, valveValue, stableTime);
}
/// <summary>
/// Open one valve and/or close one valve.
/// Events: ValvesSet
/// </summary>
/// <param name="valveOpen">Valve to be opened</param>
/// <param name="valveClose">Valve to be closed</param>
/// <returns>Created operation</returns>
public IOperation SetValvesOp(IValve valveOpen, IValve valveClose)
{
return new BuiltIn.SetValvesOp(this, valveOpen, valveClose);
}
/// <summary>
/// Open and/or close multiple valves.
/// Events: ValvesSet
/// </summary>
/// <param name="valvesOpen">A list of valves to be opened</param>
/// <param name="valvesClose">A list of valves to be closed</param>
/// <returns>Created operation</returns>
public IOperation SetValvesOp(IList<IValve> valvesOpen, IList<IValve> valvesClose)
{
return new BuiltIn.SetValvesOp(this, valvesOpen, valvesClose);
}
/// <summary>
/// Open start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation OpenStartValveOp()
{
return new BuiltIn.SetValvesOp(this, true, devices);
}
/// <summary>
/// Close start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation CloseStartValveOp()
{
return new BuiltIn.SetValvesOp(this, false, devices);
}
public IOperation SetFlowOp(double reqrdFlowLo, double reqrdFlowHi, TBF.Boxes.DoubleBox measuredFlow, int timeout)
{
if (Devices.RegulValve == null || Devices.FlowMeter == null) return null;
return Devices.RegulValve.SetFlowOp(Devices.FlowMeter, reqrdFlowLo, reqrdFlowHi, measuredFlow, timeout);
}
/// <summary>
/// Start a test, Events: MeasurementStarted
/// </summary>
/// <param name="iParam">Int32 parameter 1</param>
/// <param name="dParam1">Double parameter 1 (alternative lower flow limit)</param>
/// <param name="dParam2">Double parameter 2 (alternative upper flow limit)</param>
/// <returns>Operation</returns>
public IOperation StartTestOp(Test test, int iParam = 0, double dParam1 = 0, double dParam2 = 0)
{
if (controlBoardCfg.DebugLevel == DebugMode.Simulate)
{
return new Operations.ReturnGivenEventOp(Event.MeasurementStarted);
}
/// Get total number of pulses
int totalPulses = Convert.ToInt32(Math.Round(test.Volume / devices.FlowMeter.LtrPerPulse));
/// Get method class string
string methodClass;
if (!StateMachine.TestMethod2Class.TryGetValue(test.Name, out methodClass)) return null;
if (methodClass.Contains("TestMethods.Adjustment."))
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, totalPulses);
}
if (methodClass.Contains("TestMethods.CombinedWithDetection."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), dParam1, dParam2, totalPulses);
}
if (methodClass == "TestMethods.DiverterTest")
{
if (iParam == 0) return null;
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, totalPulses / iParam);
}
if (methodClass == "TestMethods.Dummy")
{
}
if (methodClass == "TestMethods.Endurance")
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, totalPulses);
}
if (methodClass.Contains("TestMethods.FixedStart."))
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, 2 * totalPulses);
}
if (methodClass.Contains("TestMethods.FixedStartAdvanced."))
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, 2 * totalPulses);
}
if (methodClass.Contains("TestMethods.FixedStartDefferedEval."))
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, 2 * totalPulses);
}
if (methodClass.Contains("TestMethods.FixedStartMassCollDeferredEval."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, 2 * totalPulses);
}
if (methodClass.Contains("TestMethods.FixedStartMassCollection."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.FixedStart), test.Qfrom, test.Qto, 2 * totalPulses);
}
if (methodClass.Contains("TestMethods.FixedStartTankCollection."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, (totalPulses * 5 / 4));
}
if (methodClass.Contains("TestMethods.FlyingStart."))
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, totalPulses);
}
if (methodClass.Contains("TestMethods.FlyingStartFirstRepetWithMassColl."))
{
TestMethods tm = (iParam == 1) ? (TestMethods.Diverter | TestMethods.Synchro) : TestMethods.Synchro;
return new StartMeasurementOp(this, devices, tm, test.Qfrom, test.Qto, totalPulses);
}
if (methodClass.Contains("TestMethods.FlyingStartMassCollComparative."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, totalPulses);
}
if (methodClass.Contains("TestMethods.FlyingStartMassCollection."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, totalPulses);
}
if (methodClass.Contains("TestMethods.FlyingStartMassCollProlonged."))
{
if (iParam == 0) return null;
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, totalPulses, iParam);
}
if (methodClass.Contains("TestMethods.FlyingStartTankCollection."))
{
return new StartMeasurementOp(this, devices, (TestMethods.Diverter | TestMethods.Synchro), test.Qfrom, test.Qto, totalPulses);
}
if (methodClass == "TestMethods.PulsesTest")
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, totalPulses);
}
if (methodClass == "TestMethods.PulsesTestManual")
{
return new StartMeasurementOp(this, devices, TestMethods.Synchro, test.Qfrom, test.Qto, totalPulses);
}
return null;
}
/// <summary>
/// Starts the measurement.
/// Events: MeasurementStarted
/// </summary>
/// <param name="flowMeter">Flow meter</param>
/// <param name="readers">An array of register readers</param>
/// <param name="refPulses">Number of reference pulses to complete the test</param>
/// <returns>Created operation</returns>
public IOperation StartProlongedMeasurementOp(OutputPath outputPath,
double requiredFlowLo, double requiredFlowHi,
int totalRefPulses, int massRefPulses)
{
if (controlBoardCfg.DebugLevel == DebugMode.Simulate)
{
return new Operations.ReturnGivenEventOp(Event.MeasurementStarted);
}
else
{
return new StartMeasurementOp(this, outputPath, TestMethods.Diverter | TestMethods.Synchro | TestMethods.MassAndContinue,
requiredFlowLo, requiredFlowHi, totalRefPulses, massRefPulses);
}
}
/// <summary>
/// Read diverter samples from the control board and calculated the transition time.
/// Events: Event.None
/// </summary>
/// <param name="div">Diverter</param>
/// <param name="runsCountWhenSendingCmd">State machine ticks from test start/end when diverter read command is issued (obsolete)</param>
/// <param name="runsCountWhenReadingDiv">State machine ticks from test start/end when diverter samples are read</param>
/// <param name="transitionTimeSec">FloatBox for the resulting transition time in s</param>
/// <param name="plotter">Statistics for plotting diverter transitions</param>
/// <param name="batchNr">Batch number</param>
/// <param name="testName">Test name (without repetitions)</param>
/// <param name="repetition">Repetition number</param>
/// <returns>Created operation</returns>
public IOperation ReadDiverterTransitionOp(bool isStart, Sequences.Statistics plotter, int batchNr, string testName, int repetition)
{
if (controlBoardCfg.DebugLevel == DebugMode.Simulate) return new Operations.ReturnGivenEventOp(Event.None);
return new ReadDiverterTransitionOp(this, devices.Diverter, 1, 3, isStart, plotter, batchNr, testName, repetition);
}
/// <summary>
/// Queries th econtrol board whether the measurement was completed.
/// Events: MeasurementCompleted or None
/// </summary>
/// <returns>Created operation</returns>
public IOperation QueryMeasurementEndOp()
{
return new QueryMeasurementEndOp(this);
}
/// <summary>
/// Stop controls board flow regulation operation
/// Events: FlowRegulationStopped
/// </summary>
/// <returns>Created operation</returns>
public IOperation StopFlowRegulationOp()
{
return new StopFlowRegulationOp(this);
}
/// <summary>
/// Stop the previous control board operation (diverter, gate, etc.)
/// Events: PreviousStopped
/// </summary>
/// <returns>Created operation</returns>
public IOperation StopPreviousOp()
{
return new StopPreviousOp(this);
}
/// <summary>
/// Sends commands to the control board.
/// Events: Event.None, Event.CommandCompleted
/// </summary>
/// <param name="commandOnStartOp">Command sent to CB when this operation starts</param>
/// <param name="commandOnStopOp">Command sent to CB when this operation stops</param>
/// <param name="testMethod">Test method parameter</param>
/// <returns>Created operation</returns>
public IOperation ExecuteCommandOp(Command commandOnStartOp, Command commandOnStopOp, TestMethods testMethod)
{
return new ExecuteCommandOp(this, commandOnStartOp, commandOnStopOp, testMethod);
}
}
}
@@ -0,0 +1,36 @@
///
/// Copyright (c) 2013-2020 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ControlBoardFactory : IComponentFactory
{
public string ClassName { get { return "ControlBoard"; } }
/// <summary>Max. number of components of this class in the system</summary>
public int MaxCount { get { return 1; } }
public IComponent DummyComponent() { return new ControlBoardDev(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new ControlBoardDev(cfg); }
public IComponentCfg DefaultConfig() { return new ControlBoardCfg(this); }
public IComponentCfgCtrl CreateCfgCtrl() { return new ControlBoardCfgCtrl(); }
public IComponent ComponentFromCmpntCfg(IComponentCfg config, IList<Generic.IComponent> components)
{
return new ControlBoardDev((ControlBoardCfg)config);
}
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(ControlBoardCfg.Serializer, component, this);
}
public void ResetStaticProperties() { ControlBoardDev.ResetStaticProperties(); }
}
}
@@ -0,0 +1,389 @@
///
/// Copyright (c) 2013-2019 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Text;
using Dirichlet.Numerics;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ControlComSim : IControlCom
{
private static readonly ILog log = LogManager.GetLogger(typeof(ControlComSim));
public override string ToString() { return string.Format("ControlComSim"); }
StatusP statusP;
public StatusP StatusP { get { return statusP; } }
double referenceFreq = 650.0;
public double ReferenceFreq { get { return referenceFreq; } }
public double ReferenceFreqs(int i) { return 0; }
int[] etPulses = new int[Config.Data.WMsCount + 1];
public int EtPulses(int wmNr1)
{
return etPulses[wmNr1];
}
public int EtPulsesK { get { return 1; } }
public int EtPulses2 { get { return 0; } }
public int EtPulses3 { get { return 0; } }
double[] errFactor = new double[Config.Data.WMsCount + 1]; /// Internal
double[] wMeterPulsesF = new double[Config.Data.WMsCount + 1]; /// Internal
UInt16[] wMeterPulses = new UInt16[Config.Data.WMsCount + 1]; /// Rounded from wMeterPulsesF
public UInt16 WMeterPulses(int wmNr1)
{
return wMeterPulses[wmNr1];
}
int[] wMeterReference = new int[Config.Data.WMsCount + 1];
public int WMeterReference(int wmNr1)
{
return wMeterReference[wmNr1];
}
public uint RegulValveDAC(int rvNr0) { return 0; }
public float Pressure(int prsNr0) { return 0; }
public float Temperature(int tmpNr0) { return (float)(20 + tmpNr0); }
public RegulValveState RegulValveState(int rvNr1) { return 0; }
public UInt128 RRoute { get { return simRoute; } }
public uint DivTime(int id) { return 0; }
public void SetDivLimit(int divNo, uint limLoPct, uint limHiPct) { }
float tTime = 1;
public float TTime { get { return tTime; } }
public float ImpulseTime(int wmNr1) { return tTime; }
public uint FmState { get { return 0; } }
public uint BeginState(int wmNr0) { return 0; }
double referenceFlow;
public double ReferenceFlow { get { return referenceFlow; } }
public float RValvePosition(int rvNr1) { return 0; }
public float DivSamples(int ms, int divIx0) { return 0; }
public int ScopeSamples(int i, int j, int k) { return 0; }
public ulong DigitalInputs { get { return 0; } }
public float AnalogInput(int adcNr0) { return 0; }
public uint AnalogInputRaw(int adcNr0, int bank) { return 0; } /// bank0=RV, bank1=Temp
public float TotalRefVolume { get { return 0; } }
public float VyslExt(int wmNr0, int what) { return 0; }
public void SetWeight(int pos, double mass) { return; } /// not available in sim. mode
public uint[] CyclePar { get { return cyclePar; } set { cyclePar = value; } } /// dummy
public uint[] WMeterCont { get { return wMeterCont; } } /// dummy
public float ValveOpenCloseTime { get { return 0.001f; } }
///
public bool ManualUIAllowed
{
get { return manualUIAllowed; }
set { manualUIAllowed = value; }
}
bool manualUIAllowed = true;
public void SetReservoirTemp(int nr, float temp) { }
///
/// Private initialization data updated by contructors of children components
/// and sent to 'controlCom2panel' by SendCalibData() method.
///
uint[,] regValveCalib;
byte[] meretRS485Address;
float[,] tempCalibData;
float[] etCalib;
uint[] diverterEdge;
uint[] balanceRange;
byte meretProtocol;
uint[] cyclePar;
uint[] wMeterCont;
///
/// Private values set by SendCommand()
///
Command cmd;
int refFlowmtrNr;
UInt128 simRoute;
int totalRefPulses;
int massRefPulses;
TestMethods testMethods;
uint[] filterConstants;
float[] FMFreq;
int regConst;
int shortImp;
StopDevs stopDevs;
///
/// Private values set by ValveMove()
///
int regulValveNo;
RegulValveMode regulValveMode;
float[] regulValveValue;
Random rand;
/// <summary>
/// Constructor
/// </summary>
public ControlComSim()
{
rand = new Random();
cyclePar = new uint[163];
wMeterCont = new uint[3] { 1, 2, 3 };
}
/// <summary>
/// Sends calibration and configuration data to the control board.
/// </summary>
/// <param name="rvCalib">Regulating valves calib.coefs, ix1 = valve nr. (1..5), ix2 = coef.nr. (0..1), values typ. c0 = 0, c1 = 0.2</param>
/// <param name="meretA">Meret address, index = 0..1, value typ. 99 and 100</param>
/// <param name="usedCom">COM port number of the control board (typ. 1)</param>
/// <param name="tempCalib">Temp.calc.coefs, ix1 = temp.sens.nr. (0..7 ???), ix2 = coef.nr</param>
/// <param name="etCalib">Etalon nominal values</param>
/// <param name="divEdge">Percentages for switching of diverters, ix = diverter nr.(0,1), typ.value 50 (%)</param>
public void SendCalibData(uint[,] rvCalib, byte[] meretA, int usedCom, float[,] tempCalib, float[] etCalib, uint[] divEdge, uint[] balanceRange, byte meretProtocol)
{
log.Info("SendCalibData(...)");
this.regValveCalib = rvCalib;
this.meretRS485Address = meretA;
this.tempCalibData = tempCalib;
this.etCalib = etCalib;
this.diverterEdge = divEdge;
this.balanceRange = balanceRange;
this.meretProtocol = meretProtocol;
}
/// <summary>
/// Sends a command to the control board.
/// </summary>
/// <param name="cmd">See ControlBoard.Command enum</param>
/// <param name="refFlowmtrNr">Number of the etalon/reference (1..5)</param>
/// <param name="route">State of valves, 128-bit word</param>
/// <param name="refPulses">kolko impulzov ma trvat skuska</param>
/// <param name="testMethods">See ControlBoard.TestMethods enum</param>
/// <param name="filterConstant">0 = No filtering (0..255)</param>
/// <param name="fmFreq">Freq.inverter control (not applicable in DT100, Munich)</param>
/// <param name="regConst">Coefficient used to control reg. valves (1..100)</param>
/// <param name="shortImp">0 = default value, 1 = spec. processing of very short pulses</param>
/// <param name="stopDevs">What to stop</param>
public void SendCommand(SendCommandArgs sendCommandArgs, UInt128 route, uint[] filterConstants, float[] fmFreq, int regConst, int shortImp)
{
log.InfoFormat("SendCommand(Cmd={0},Ref#={1},Route={2} {3} {4} {5},TotalPls={6},MassPls={7},TstM={8},filt={9},FM=[{10}{11}{12}{13}{14}{15}],Reg={16},ShrtImp={17},Stop={18})",
sendCommandArgs.Cmd, sendCommandArgs.RefFlowmtrNr, /// 0, 1
((route >> 48) & 0xFFFF).ToString("X4"), /// 2
((route >> 32) & 0xFFFF).ToString("X4"), /// 3
((route >> 16) & 0xFFFF).ToString("X4"), /// 4
(route & 0xFFFF).ToString("X4"), /// 5
totalRefPulses, massRefPulses, testMethods, filterConstants, /// 6, 7, 8, 9
fmFreq[0].ToString(), /// 10
(fmFreq.Length > 1) ? ("," + fmFreq[1].ToString()) : "", /// 11
(fmFreq.Length > 2) ? ("," + fmFreq[2].ToString()) : "", /// 12
(fmFreq.Length > 3) ? ("," + fmFreq[3].ToString()) : "", /// 13
(fmFreq.Length > 4) ? ("," + fmFreq[4].ToString()) : "", /// 14
(fmFreq.Length > 5) ? ("," + fmFreq[5].ToString()) : "", /// 15
regConst, shortImp, stopDevs); /// 16, 17, 18
///
/// Simulation
///
Command lastCmd = this.cmd;
this.cmd = sendCommandArgs.Cmd;
this.refFlowmtrNr = sendCommandArgs.RefFlowmtrNr;
#if FUZHOU_300
///
/// The following code deals with the situation that all FM controlled pums share bit #39.
/// In settings the bits of pumps should be sent as follows: P1=50, P2=51, P3=52, P4=53, P5=54 and P7=55
/// (in FUZHOU_300 the FM pump bit should be set set to value FMIndex+50).
///
bool bit39 = (route & 0x00FC000000000000) != 0; /// true if any of bits 50 through 55 is set
route = route & 0xFF03FFFFFFFFFFFF;
if (bit39) route = route | 0x0000008000000000;
log.DebugFormat("SendCommand route = {0}", Utils.RouteToStr(route));
#endif
TestBenchSim.SimRoute = this.simRoute = route;
this.totalRefPulses = sendCommandArgs.TotalRefPulses;
this.massRefPulses = sendCommandArgs.MassRefPulses;
this.testMethods = sendCommandArgs.TestMethods;
this.stopDevs = sendCommandArgs.StopDevs;
this.filterConstants = filterConstants;
this.FMFreq = fmFreq;
this.regConst = regConst;
this.shortImp = shortImp;
#pragma warning disable
statusP = (StatusP)(((ulong)statusP & (ulong)0xFFFFFFFFFFFFFFF8L) | (ulong)refFlowmtrNr);
#pragma warning restore
if (cmd == Command.Start && lastCmd != Command.Start )
{
statusP |= StatusP.TestInProgress;
///
/// Clear all counters
///
if (etPulses != null) { for (int i = 0; i < etPulses.Length; i++) etPulses[i] = 0; }
if (wMeterPulses != null) { for (int i = 0; i < wMeterPulses.Length; i++) wMeterPulses[i] = 0; }
if (wMeterPulsesF != null) { for (int i = 0; i < wMeterPulsesF.Length; i++) wMeterPulsesF[i] = 0; }
if (wMeterReference != null) { for (int i = 0; i < wMeterReference.Length; i++) wMeterReference[i] = 0; }
if (errFactor != null)
{
for (int i = 0; i < errFactor.Length; i++) errFactor[i] = ((float)rand.Next(100) + 950.0f) / 1000.0f;
}
tTime = 0;
}
else if (cmd == Command.Stop)
{
statusP = 0;
//statusP &= ~StatusP.Running;
}
}
/// <summary>
/// Control of regulating valves.
/// </summary>
/// <param name="regulValveNo">Regulating valve nr. (1..5)</param>
/// <param name="regulValveMode">???</param>
/// <param name="regulValveValue">???</param>
/// <param name="stableTime">Stabilization time when setting the flow: 0=200ms, step 50ms, max. 1.5 sec.</param>
public void ValveMove(int regulValveNo, RegulValveMode regulValveMode, float[] regulValveValue, int stableTime)
{
log.InfoFormat("ValveMove({0}, {1}, [{2},{3}], {4})", regulValveNo, regulValveMode, regulValveValue[0], regulValveValue[1], stableTime);
this.regulValveNo = regulValveNo;
this.regulValveMode = regulValveMode;
this.regulValveValue = regulValveValue;
}
public void RunDeviceBefore()
{
log.DebugFormat("RunDeviceBefore() ... regV#={0}, flowM#{1}, statusP={2}", regulValveNo, refFlowmtrNr, statusP.ToString("X"));
TestBenchSim.RunDevice(regulValveNo, refFlowmtrNr, statusP);
log.DebugFormat("refFreq={0}", referenceFreq);
tTime += 1.0f;
float Qnom = (regulValveNo > 0) ? etCalib[regulValveNo] : 0;
switch (cmd)
{
case Command.None:
break;
case Command.Start:
if ((testMethods & TestMethods.Diverter) == TestMethods.Diverter)
{
TestBenchSim.SetSimDiverter(true);
}
break;
case Command.Stop:
if ((testMethods & TestMethods.Diverter) == TestMethods.Diverter)
{
TestBenchSim.SetSimDiverter(false);
}
break;
}
switch (regulValveMode)
{
case RegulValveMode.TargetFrequency:
float targetFlow = ((regulValveValue[0] + regulValveValue[1]) / 2.0f) * (Qnom / 2000.0f);
TestBenchSim.UpdateSimFlow(targetFlow);
//if (referenceFreq < valveValue[0]) referenceFreq += ((float)rand.Next(20) + 10.0f) / 2.0f;
//else if (referenceFreq > valveValue[1]) referenceFreq -= ((float)rand.Next(20) + 10.0f) / 2.0f;
//else referenceFreq += ((float)rand.Next(10) - 5.0f) / 2.0f;
referenceFreq = TestBenchSim.GetSimFlow() * 2000.0 / Qnom;
if (referenceFreq < 0) referenceFreq = 0;
if (referenceFreq > 2500) referenceFreq = 2500;
break;
default:
break;
}
referenceFlow = referenceFreq / 2000.0;
bool stopTest = false;
if (cmd == Command.Start)
{
double pwFactor = 1.0;
/// Increment reference flow meters
for (int i = 0; i <= Config.Data.WMsCount; i++)
{
etPulses[i] += (int)referenceFreq;
if (etPulses[i] > totalRefPulses)
{
pwFactor = 1.0 - (etPulses[i] - totalRefPulses) / referenceFreq;
etPulses[i] = totalRefPulses;
stopTest = true;
}
}
/// Increment water meters
double flowLtrPerSec = TestBenchSim.GetSimFlow() / 1800.0;
for (int i = 1; i <= Config.Data.WMsCount; i++)
{
float wmPulsesPerLiter = 0;
if ((BenchControl.Sequences.ProcessData.RegisterReaders[i - 1] != null) &&
(BenchControl.Sequences.ProcessData.BatchRslts.Batch.WaterMeters.Count >= i) &&
(BenchControl.Sequences.ProcessData.BatchRslts.Batch.WaterMeters[i - 1] != null))
{
wmPulsesPerLiter = (float)BenchControl.Sequences.ProcessData.RegisterReaders[i-1].PulsesPerLtr;
}
wMeterPulsesF[i] += errFactor[i] * pwFactor * ((float)rand.Next(100) + 450.0f) * wmPulsesPerLiter * flowLtrPerSec;
wMeterPulses[i] = (UInt16)wMeterPulsesF[i];
}
if (etPulses[0] > totalRefPulses)
{
statusP |= StatusP.TestCompleted;
statusP &= ~StatusP.TestInProgress;
}
}
if (stopTest)
{
TestBenchSim.SetSimDiverter(false);
statusP = (statusP | StatusP.TestCompleted);
statusP = (statusP & ~StatusP.TestInProgress);
}
}
StatusP lastStatusP;
UInt128 lastRoute;
public void RunDeviceAfter()
{
StatusP bufferedStatusP = StatusP;
if (lastStatusP != bufferedStatusP)
{
string statusPStr = ((ulong)bufferedStatusP).ToString("x16");
log.InfoFormat("RunDeviceAfter() StatusP = {0}", statusPStr);
Program.MainWnd.UpdateStatusP(statusPStr);
lastStatusP = bufferedStatusP;
}
UInt128 bufferedRoute = simRoute;
if (lastRoute != bufferedRoute)
{
string routeStr = bufferedRoute.ToString("x16");
log.InfoFormat("RunDeviceAfter() Route = {0}", routeStr);
Program.MainWnd.UpdateRoute(routeStr);
lastRoute = bufferedRoute;
}
}
}
}
@@ -0,0 +1,534 @@
///
/// Copyright (c) 2013-2016 Sensus Metering Systems
///
using System;
using Dirichlet.Numerics;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ControlComWrap : IControlCom
{
private static readonly ILog log = LogManager.GetLogger(typeof(ControlComWrap));
/// Fatal: SendCalibData(), DoSendCalibData()
/// Info: SendCommand(), ValveMove(), StatusP
/// Debug: Weight[ix]
///
/// Properties
///
public StatusP StatusP { get { return (StatusP)ctrlBrdComponent.StatusP; } }
#if DN100 || MUNICH || FUZHOU_150
public double ReferenceFreq { get { return 1000.0f * ctrlBrdComponent.referenceFreq; } } /// Returns in [Hz] 0..2500, nom.=2000
public double ReferenceFreqs(int i) { return 0; }
#else
/// All newer benches
public double ReferenceFreq { get { return 1000.0 * ctrlBrdComponent.referenceFreq[0]; } } /// Returns in [Hz] 0..2500, nom.=2000
///
/// <summary>
/// In case of I11, I12
/// cBrd.referenceFreq[0] = I11 + I12
/// cBrd.referenceFreq[1] = I11
/// cBrd.referenceFreq[2] = I12
/// otherwise
/// cBrd.referenceFreq[0] = I2 or I3 or I4
/// </summary>
/// <returns>Reference frequency in Hz</returns>
public double ReferenceFreqs(int i)
{
return ((i >= 0) && (i < ctrlBrdComponent.referenceFreq.Length)) ? (1000.0 * ctrlBrdComponent.referenceFreq[i]) : 0;
}
#endif
public int EtPulses(int wmNr1) { return (int)ctrlBrdComponent.etPulses[wmNr1]; }
#if DN100 || MUNICH || BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || BADGER_VELKA_TRAT || TORINO_50 || TORUN_50 || CEVAK_40 || MURES_40 || IZRAEL_200 || MALTA_WSD25
public int EtPulsesK { get { return 1; } }
#else /// all new benches
public int EtPulsesK { get { return (int)ctrlBrdComponent.etPulsesK; } }
#endif
#if DEWA_300
public int EtPulses2 { get { return (int)ctrlBrdComponent.EtPulses2; } }
public int EtPulses3 { get { return (int)ctrlBrdComponent.EtPulses3; } }
#elif ROMA_200
public int EtPulses2 { get { return (int)ctrlBrdComponent.EtPulses2; } }
public int EtPulses3 { get { return 0; } }
#else
public int EtPulses2 { get { return 0; } }
public int EtPulses3 { get { return 0; } }
#endif
public UInt16 WMeterPulses(int wmNr1) { return (UInt16)ctrlBrdComponent.wMeterPuls[wmNr1]; }
public int WMeterReference(int wmNr0) { return (int)ctrlBrdComponent.wMeterReference[wmNr0]; }
public uint RegulValveDAC(int rvNr0)
{
#if CEVAK_200
return ctrlBrdComponent.StavDA[rvNr0];
#else
return ctrlBrdComponent.StavDA;
#endif
}
public float Pressure(int prsNr0) { return ctrlBrdComponent.pressure[prsNr0]; }
public float Temperature(int tmpNr0) { return (tmpNr0 >= 0 && tmpNr0 < ctrlBrdComponent.temperature.Length) ? ctrlBrdComponent.temperature[tmpNr0] : 0; }
public RegulValveState RegulValveState(int rvNr1) { return (RegulValveState)ctrlBrdComponent.rValveStatus[rvNr1 - 1]; }
#if GENESIS || BERLIN || GELSENWASSER || PETERSBURG_200 || DEWA_300 || ROMA_200
public UInt128 RRoute { get { return ((UInt128)ctrlBrdComponent.rRouteH << 64) | (UInt128)ctrlBrdComponent.rRoute; } }
#else
public UInt128 RRoute { get { return ctrlBrdComponent.rRoute; } }
#endif
#if GENESIS || KEMPNO_50 || KRAKOW_50 || BAHRAIN_50 || BADGER_STREDNA_TRAT || JUZNA_AFRIKA_50 || IZRAEL_25 || ZAMBIA || ZODINO || FEWA_50 || FILIPINY_50 || SENTEC || FUZHOU_100 || CEVAK_200 || IZRAEL_50 || DEWA_300 || ROMA_200 || PUCHONG_200 || TURA_IPERL_NEW || ALZIR_25 || GELSENWASSER
public uint DivTime(int id) { return ctrlBrdComponent.divTime[id]; }
public void SetDivLimit(int divNo, uint limLoPct, uint limHiPct)
{
ctrlBrdComponent.DIVLimit[divNo, 0] = limLoPct;
ctrlBrdComponent.DIVLimit[divNo, 1] = limHiPct;
}
#else
public uint DivTime(int id) { return ctrlBrdComponent.divTime; }
public void SetDivLimit(int divNo, uint limLoPct, uint limHiPct) { }
#endif
public float TTime { get { return ctrlBrdComponent.Ttime; } }
#if DN100 || MUNICH
public float ImpulseTime(int wmNr1) { return ctrlBrdComponent.Ttime; }
#else
public float ImpulseTime(int wmNr1) { return ctrlBrdComponent.ImpulseTime[wmNr1]; }
#endif
#if DN100 || MUNICH || FUZHOU_150
public uint FmState { get { return ctrlBrdComponent.fMState; } }
#else //if everything newer
public uint FmState { get { return ctrlBrdComponent.fMState[0]; } } /// TODO: index
#endif
public uint BeginState(int wmNr0) { return ctrlBrdComponent.beginSt[wmNr0]; }
public double ReferenceFlow { get { return (double)ctrlBrdComponent.referenceFlow; } }
public float RValvePosition(int rvNr1)
{
//return ctrlBrdComponent.RValvePosition[rvNr1 - 1];
return ctrlBrdComponent.Analogy[rvNr1 - 1]; /// Fixes bug: RValvePosition returns 0 all the time
}
public float DivSamples(int time, int divIx0)
{
#if MUNICH || IZRAEL_200
return ctrlBrdComponent.DIVSamples[time, 0];
#else
return ctrlBrdComponent.DIVSamples[time, divIx0];
#endif
} /// Time unit is 2 ms
public int ScopeSamples(int i, int j, int k) { return ctrlBrdComponent.ScopeSamples[i, j, k]; }
public ulong DigitalInputs { get { return (ulong)ctrlBrdComponent.Vstupy; } }
public float AnalogInput(int adcNr0) { return ctrlBrdComponent.Analogy[adcNr0]; }
public uint AnalogInputRaw(int adcNr0, int bank) { return ctrlBrdComponent.AnalogyRaw[adcNr0, bank]; } /// bank0=RV, bank1=Temp
#if DN100 || MUNICH || BADGER_MALA_TRAT || BADGER_VELKA_TRAT || FUZHOU_50 || FUZHOU_150 || FUZHOU_300 || IZRAEL_200 || TORINO_50 || TORUN_50 || CEVAK_40 || MURES_40
public float TotalRefVolume { get { return ctrlBrdComponent.ReferenceVolume; } }
public float MassRefVolume { get { return ctrlBrdComponent.ReferenceVolume; } }
#else /// all new test benches
public float TotalRefVolume { get { return ctrlBrdComponent.ReferenceVolume[0]; } }
public float MassRefVolume { get { return ctrlBrdComponent.ReferenceVolume[1]; } }
#endif
#if SLM_END || WARSAW_END
public uint[] CyclePar { get { return ctrlBrdComponent.CyclePar; } set { ctrlBrdComponent.CyclePar = value; } }
public uint[] WMeterCont { get { return ctrlBrdComponent.wMeterCont; } }
#else
public uint[] CyclePar { get { return new uint[163]; } set { return; }} /// Dummy
public uint[] WMeterCont { get { return new uint[3]; } } /// Dummy
#endif
#if KEMPNO_50 || KRAKOW_50 || JUZNA_AFRIKA_50 || IZRAEL_25 || ZAMBIA || ZODINO || FEWA_50 || FILIPINY_50 || FUZHOU_100 || CEVAK_200 || IZRAEL_50 || ALZIR_25
public float ValveOpenCloseTime { get { return ctrlBrdComponent.ValveMoveTime[0]; } }
#else
public float ValveOpenCloseTime { get { return 0.001f; } }
#endif
public bool ManualUIAllowed
{
get { return ctrlBrdComponent.Dovolene; }
set { ctrlBrdComponent.Dovolene = value; }
}
public float VyslExt(int wmNr0, int what) { return ctrlBrdComponent.VyslExt[wmNr0, what]; }
public void SetWeight(int pos, double mass)
{
ctrlBrdComponent.Weight[pos] = (float)mass;
log.DebugFormat("SetWeight(pos={0}, mass={1}", pos, mass);
}
public void SetReservoirTemp(int nr, float temp)
{
#if GENESIS
if (nr > 0 && nr <= 3) ctrlBrdComponent.TankTemp[nr - 1] = temp;
#endif
}
///
/// Reference to the control board component
///
#if DN100
private ControlCom2VB.ControlCom2panel ctrlBrdComponent;
#elif MUNICH || FUZHOU_150
private ControlComponent3Munich.UserControl1 ctrlBrdComponent;
#elif FUZHOU_300
private ControlComponent3F300.UserControl1 ctrlBrdComponent;
#elif IZRAEL_200 || PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER || SLM_150 || PETERSBURG_200
private ControlComponent_Izrael2014.UserControl1 ctrlBrdComponent;
#else /// newer benches
private ControlComponent_Torino2015.UserControl1 ctrlBrdComponent;
#endif
/// <summary>
/// Constructor that initializes the control board component reference
/// </summary>
/// <param name="ctrlBrdComponent"></param>
public ControlComWrap(object eldeComponent)
{
#if DN100
ctrlBrdComponent = eldeComponent as ControlCom2VB.ControlCom2panel;
#elif MUNICH || FUZHOU_150
ctrlBrdComponent = eldeComponent as ControlComponent3Munich.UserControl1;
#elif FUZHOU_300
ctrlBrdComponent = eldeComponent as ControlComponent3F300.UserControl1;
#elif IZRAEL_200 || PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER || SLM_150 || PETERSBURG_200
ctrlBrdComponent = eldeComponent as ControlComponent_Izrael2014.UserControl1;
#else /// all newer benches
ctrlBrdComponent = eldeComponent as ControlComponent_Torino2015.UserControl1;
#endif
if (ctrlBrdComponent == null) throw new Exception("Wrong ELDE component");
}
/// <summary>
/// Sends calibration and configuration data to the control board.
/// </summary>
/// <param name="rvCalib">Regulating valves calib.coefs, ix1 = valve nr. (1..5), ix2 = coef.nr. (0..1), values typ. c0 = 0, c1 = 0.2</param>
/// <param name="meretA">Meret address, index = 0..1, value typ. 99 and 100</param>
/// <param name="usedCom">COM port number of the control board (typ. 1)</param>
/// <param name="tempCalib">Temp.calc.coefs, ix1 = temp.sens.nr. (0..7 ???), ix2 = coef.nr</param>
/// <param name="etCalib">Etalon nominal values</param>
/// <param name="divEdge">Percentages for switching of diverters, ix = diverter nr.(0,1), typ.value 50 (%)</param>
/// <param name="balanceRange">Balance range = volume of the tanks on the balaces (ix=0,1)</param>
public void SendCalibData(uint[,] rvCalib, byte[] meretA, int usedCom, float[,] tempCalib,
float[] etCalib, uint[] divEdge, uint[] balanceRange, byte meretProtocol)
{
if (Program.MainWnd.InvokeRequired)
{
/// When SendCalibData(...) called from the worker thread
object[] args = new object[] { rvCalib, meretA, usedCom, tempCalib, etCalib, divEdge, balanceRange };
Program.MainWnd.Invoke((SendCalibDataDlgt)DoSendCalibData, args); /// Invoke required as component was created in UI thread
}
else
{
/// When SendCalibData(...) called from the UI thread
DoSendCalibData(rvCalib, meretA, usedCom, tempCalib, etCalib, divEdge, balanceRange, meretProtocol);
}
}
///
delegate void SendCalibDataDlgt(uint[,] rvCalib, byte[] meretA, int usedCom, float[,] tempCalib,
float[] etCalib, uint[] divEdge, uint[] balanceRange, byte meretProtocol);
///
private void DoSendCalibData(uint[,] rvCalib, byte[] meretA, int usedCom, float[,] tempCalib,
float[] etCalib, uint[] divEdge, uint[] balanceRange, byte meretProtocol)
{
//TBF.UiBridge.Bridge.OnLog(this, string.Format("{0} SendCalibData(COM{1}) rvCalib[0,0]={2} rvCalib[0,1]={3} rvCalib[1,0]={4} rvCalib[1,1]={5}\r\n",
// DateTime.Now.ToLongTimeString(), usedCom,
// rvCalib[0,0], rvCalib[0,1], rvCalib[1,0], rvCalib[1,1]));
#if DN100
log.FatalFormat("Component: {0}", ControlCom2VB.ControlCom2panel.ProgVersion);
#elif MUNICH || FUZHOU_150
log.FatalFormat("Component: {0}", ControlComponent3Munich.UserControl1.ProgVersion);
#elif FUZHOU_300
log.FatalFormat("Component: {0}", ControlComponent3F300.UserControl1.ProgVersion);
#elif IZRAEL_200 || PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER || SLM_150 || PETERSBURG_200
log.FatalFormat("Component: {0}", ControlComponent_Izrael2014.UserControl1.ProgVersion);
#else /// newer benches
log.FatalFormat("Component: {0}", ControlComponent_Torino2015.UserControl1.ProgVersion);
#endif
log.Fatal("SendCalibData(...)");
for (int i = 0; i < rvCalib.GetLength(0); i++)
for (int j = 0; j < rvCalib.GetLength(1); j++)
log.FatalFormat(" rvCalib[{0},{1}] = {2}", i, j, rvCalib[i,j]);
for (int i = 0; i < meretA.Length; i++) log.FatalFormat(" meretA[{0}] = {1}", i, meretA[i]);
log.FatalFormat(" usedCom = {0}", usedCom);
for (int i = 0; i < tempCalib.GetLength(0); i++)
for (int j = 0; j < tempCalib.GetLength(1); j++)
log.FatalFormat(" tempCalib[{0},{1}] = {2}", i, j, tempCalib[i, j]);
for (int i = 0; i < etCalib.Length; i++) log.FatalFormat(" etCalib[{0}] = {1}", i, etCalib[i]);
for (int i = 0; i < divEdge.Length; i++) log.FatalFormat(" divEdge[{0}] = {1}", i, divEdge[i]);
for (int i = 0; i < balanceRange.Length; i++) log.FatalFormat(" balanceRange[{0}] = {1}", i, balanceRange[i]);
ctrlBrdComponent.ExternalCom = true;
#if SENTEC || CEVAK_200 || FUZHOU_100 || IZRAEL_25 || ALZIR_25
ctrlBrdComponent.SendCalibData(rvCalib, meretA, (byte)usedCom, tempCalib, etCalib, divEdge, balanceRange, meretProtocol);
#else
ctrlBrdComponent.SendCalibData(rvCalib, meretA, (byte)usedCom, tempCalib, etCalib, divEdge, balanceRange);
#endif
}
/// <summary>
/// Control of regulating valves.
///
/// </summary>
/// <param name="valveNo">Regulating valve nr. (1..5)</param>
/// <param name="valveMode">Valve regulation mode, see the enum</param>
/// <param name="valveValue">Target position or target frequency or pulse duration</param>
/// <param name="stableTime">Stabilization time when setting the flow: 0=200ms, step 50ms, max. 1.5 sec.</param>
public void ValveMove(int valveNo, RegulValveMode valveMode, float[] valveValue, int stableTime)
{
if (Program.MainWnd.InvokeRequired)
{
/// When ValveMove(...) called from the worker thread
object[] args = new object[] { valveNo, valveMode, valveValue, stableTime };
Program.MainWnd.Invoke((ValveMoveDlgt)DoValveMove, args); /// When ValveMove called from a worker thread
}
else
{
/// When ValveMove(...) called from the UI thread
DoValveMove(valveNo, valveMode, valveValue, stableTime); /// When ValveMove called from the UI thread
}
}
///
delegate void ValveMoveDlgt(int valveNo, RegulValveMode valveMode, float[] valveValue, int stableTime);
///
private void DoValveMove(int valveNo, RegulValveMode valveMode, float[] valveValue, int stableTime)
{
#if DN100 || MUNICH || FUZHOU_150
log.InfoFormat("ValveMove(#={0}, md={1}, [{2}, {3}])", valveNo, valveMode, valveValue[0], valveValue[1]);
ctrlBrdComponent.ValveMove((byte)valveNo, (byte)valveMode, valveValue);
#else /// all newer benches
log.InfoFormat("ValveMove(#={0}, md={1}, [{2}, {3}], {4})", valveNo, valveMode, valveValue[0], valveValue[1], stableTime);
ctrlBrdComponent.ValveMove((byte)valveNo, (byte)valveMode, valveValue, stableTime);
#endif
}
/// <summary>
/// Sends a command to the control board.
/// </summary>
/// <param name="sendCommandArgs">Arguments</param>
/// <param name="route">State of valves, 128-bit word</param>
/// <param name="filterConstants">Array with length=8 of filter costants (0..255, 0 = no filtering)</param>
/// <param name="fmFreq">Freq.inverter control (not applicable in DT100, Munich)</param>
/// <param name="regConst">PID coefficient for flow control</param>
/// <param name="shortImp">0 = default value, 1 = spec. processing of very short pulses</param>
public void SendCommand(SendCommandArgs sendCommandArgs, UInt128 route, uint[] filterConstants, float[] fmFreq, int regConst, int shortImp)
{
if (Program.MainWnd.InvokeRequired)
{
/// From another thread ... use Invoke()
Program.MainWnd.Invoke((SendCommandDlgt)DoSendCommand, new object[] { sendCommandArgs, route, filterConstants, fmFreq, regConst, shortImp });
}
else
{
/// Call DoSendCommand() directly
DoSendCommand(sendCommandArgs, route, filterConstants, fmFreq, regConst, shortImp);
}
}
///
delegate void SendCommandDlgt(SendCommandArgs sendCommandArgs, UInt128 route, uint[] filterConstants, float[] fmFreq, int regConst, int shortImp);
///
private void DoSendCommand(SendCommandArgs sendCommandArgs, UInt128 route, uint[] filterConstants, float[] fmFreq, int regConst, int shortImp)
{
#if FUZHOU_300
///
/// The following code deals with the situation that all FM controlled pumps share bit #39.
/// In settings the bits of pumps should be sent as follows: P1=50, P2=51, P3=52, P4=53, P5=54 and P7=55
/// (in FUZHOU_300 the FM pump bit should be set to value FMIndex+50).
///
bool bit39 = (route & 0x00FC000000000000) != 0; /// true if any of bits 50 through 55 is set
route = route & 0xFF03FFFFFFFFFFFF;
if (bit39) route = route | 0x0000008000000000;
#elif PETERSBURG_200
///
/// The following code deals with the situation that all pumps share bit #80.
/// In settings the bits of pumps should be sent as follows: P1=64, P2=65, P3=66, P4=67 and P5=68
///
bool bit80 = (route & (((UInt128)0x1F) << 64)) != 0; /// true if any of bits 64 through 68 is set
if (bit80)
route = route | (((UInt128)1) << 80);
else
route = route & (~(((UInt128)1) << 80));
#endif
log.InfoFormat("DoSendCommand(Cmd={0},Ref#={1},Route={2},TotalPls={3},MassPls={4},TM={5},filt={6},FM=[{7}{8}{9}{10}{11}{12}],Reg={13},ShrtImp={14},Stop={15})",
sendCommandArgs.Cmd, /// 0
sendCommandArgs.RefFlowmtrNr, /// 1
Utils.RouteToStr(route), /// 2
sendCommandArgs.TotalRefPulses,
sendCommandArgs.MassRefPulses,
sendCommandArgs.TestMethods,
filterConstants, /// 3, 4, 5, 6
fmFreq[0].ToString(), /// 7
(fmFreq.Length > 1) ? ("," + fmFreq[1].ToString()) : "", /// 8
(fmFreq.Length > 2) ? ("," + fmFreq[2].ToString()) : "", /// 9
(fmFreq.Length > 3) ? ("," + fmFreq[3].ToString()) : "", /// 10
(fmFreq.Length > 4) ? ("," + fmFreq[4].ToString()) : "", /// 11
(fmFreq.Length > 5) ? ("," + fmFreq[5].ToString()) : "", /// 12
regConst, /// 13
shortImp, /// 14
sendCommandArgs.StopDevs); /// 15
#if DN100 || MUNICH
///
/// 64-bit route, single pump FM frequency (no array), single filter conts. (no array)
///
ctrlBrdComponent.SendCommand((uint)sendCommandArgs.Cmd,
(byte)sendCommandArgs.RefFlowmtrNr,
(ulong)route,
(uint)sendCommandArgs.TotalRefPulses,
(uint)sendCommandArgs.TestMethods,
Convert.ToSingle(filterConstants[0]),
fmFreq[0],
(uint)regConst,
(uint)shortImp,
(uint)sendCommandArgs.StopDevs);
#elif BADGER_MALA_TRAT || BADGER_VELKA_TRAT || FUZHOU_50 || FUZHOU_150 || FUZHOU_300 || IZRAEL_200 || TORINO_50 || TORUN_50 || CEVAK_40 || MURES_40
///
/// 64-bit route, single filter conts. (no array)
///
ctrlBrdComponent.SendCommand((uint)sendCommandArgs.Cmd,
(byte)sendCommandArgs.RefFlowmtrNr,
(ulong)route,
(uint)sendCommandArgs.TotalRefPulses,
(uint)sendCommandArgs.TestMethods,
Convert.ToSingle(filterConstants[0]),
fmFreq,
(uint)regConst,
(uint)shortImp,
(uint)sendCommandArgs.StopDevs);
#elif BERLIN || SLM_150 || PETERSBURG_200
///
/// 128-bit route, prolonged method support, single filter conts. (no array)
///
ctrlBrdComponent.SendCommand((uint)sendCommandArgs.Cmd,
(byte)sendCommandArgs.RefFlowmtrNr,
(ulong)(route & (UInt128)0xFFFFFFFFFFFFFFFF),
(ulong)(route >> 64),
new uint[] { (uint)sendCommandArgs.TotalRefPulses, (uint)sendCommandArgs.MassRefPulses },
(uint)sendCommandArgs.TestMethods,
Convert.ToSingle(filterConstants[0]),
fmFreq,
(uint)regConst,
(uint)shortImp,
(uint)sendCommandArgs.StopDevs);
#elif GENESIS || DEWA_300 || ROMA_200 || GELSENWASSER
///
/// 128-bit route, prolonged method support
///
ctrlBrdComponent.SendCommand((uint)sendCommandArgs.Cmd,
(byte)sendCommandArgs.RefFlowmtrNr,
(ulong)(route & (UInt128)0xFFFFFFFFFFFFFFFF),
(ulong)(route >> 64),
new uint[] { (uint)sendCommandArgs.TotalRefPulses, (uint)sendCommandArgs.MassRefPulses },
(uint)sendCommandArgs.TestMethods,
filterConstants,
fmFreq,
(uint)regConst,
(uint)shortImp,
(uint)sendCommandArgs.StopDevs);
#elif JUZNA_AFRIKA_50 || IZRAEL_25 || ZAMBIA || ZODINO || FEWA_50 || FILIPINY_50 || SENTEC || FUZHOU_100 || CEVAK_200 || IZRAEL_50 || ALZIR_25
///
/// 64-bit route, prolonged method support
///
ctrlBrdComponent.SendCommand((uint)sendCommandArgs.Cmd,
(byte)sendCommandArgs.RefFlowmtrNr,
(ulong)route,
new uint[] { (uint)sendCommandArgs.TotalRefPulses, (uint)sendCommandArgs.MassRefPulses },
(uint)sendCommandArgs.TestMethods,
filterConstants,
fmFreq,
(uint)regConst,
(uint)shortImp,
(uint)sendCommandArgs.StopDevs);
#else /// if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL || MALTA_WSD25 || SLM_50 || SLM_END || WARSAW_END || PETERSBURG_50 || KEMPNO_50 || BAHRAIN_50 || RUM_MOB || KRAKOW_50 || BADGER_STREDNA_TRAT || PUCHONG_200
///
/// 64-bit route, prolonged method support, single filter conts. (no array)
///
ctrlBrdComponent.SendCommand((uint)sendCommandArgs.Cmd,
(byte)sendCommandArgs.RefFlowmtrNr,
(ulong)route,
new uint[] { (uint)sendCommandArgs.TotalRefPulses, (uint)sendCommandArgs.MassRefPulses },
(uint)sendCommandArgs.TestMethods,
Convert.ToSingle(filterConstants[0]),
fmFreq,
(uint)regConst,
(uint)shortImp,
(uint)sendCommandArgs.StopDevs);
#endif
log.DebugFormat("DoSendCommand() Route = {0}", Utils.RouteToStr(route));
if (lastRoute != route) /// This is to update the MainWnd status bar on changes only
{
UInt128 rRoute = RRoute;
Program.MainWnd.UpdateRoute(string.Format("{0} ({1})", Utils.RouteToStr(route), Utils.RouteToStr(rRoute)));
lastRoute = route;
}
}
enum CommType
{
Rx, /// Receive data only
TxRx, /// Transmit and receive data
}
private StatusP lastStatusP; /// This is to update the MainWnd status bar on changes only
private UInt128 lastRoute; /// This is to update the MainWnd status bar on changes only
public void RunDeviceBefore()
{
object[] args = new object[] { CommType.Rx };
MakeCommDlgt dlgt = MakeComm;
Program.MainWnd.Invoke(dlgt, args); /// Invoke MakeExtComm(.) in the UI thread
StatusP bufferedStatusP = StatusP;
if (lastStatusP != bufferedStatusP)
{
string statusPStr = ((ulong)bufferedStatusP).ToString("x16");
log.DebugFormat("RunDeviceBefore() StatusP = {0}", statusPStr);
//Program.MainWnd.UpdateStatusP(statusPStr); /// TODO 170309
lastStatusP = bufferedStatusP;
}
}
public void RunDeviceAfter()
{
object[] args = new object[] { CommType.TxRx };
MakeCommDlgt dlgt = MakeComm;
Program.MainWnd.Invoke(dlgt, args); /// Invoke MakeExtComm(.) in the UI thread
StatusP bufferedStatusP = StatusP;
if (lastStatusP != bufferedStatusP)
{
string statusPStr = ((ulong)bufferedStatusP).ToString("x16");
log.DebugFormat("RunDeviceAfter() StatusP = {0}", statusPStr);
//Program.MainWnd.UpdateStatusP(statusPStr); /// TODO 170309
lastStatusP = bufferedStatusP;
}
}
/// <summary>
/// A delegate to execute the SendCommand() in the UI thread
/// </summary>
delegate void MakeCommDlgt(CommType extComm);
/// <summary>
/// Executes the SendCommand(...) and MakeExtCom() in the UI thread
/// </summary>
void MakeComm(CommType commType)
{
if (ctrlBrdComponent.ExternalCom)
{
ctrlBrdComponent.MakeExtCom((commType == CommType.Rx) ? false : true);
}
}
}
}
@@ -0,0 +1,106 @@
///
/// Copyright (c) 2016 Sensus Metering Systems
///
using System;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ExecuteCommandOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(ExecuteCommandOp));
public override string ToString() { return string.Format("ExecuteCommandOp( command={0}, testMethod={1} )", commandOnStartOp, testMethod); }
/// Set by the constructor
readonly ControlBoardDev controlBoard;
readonly Command commandOnStartOp;
readonly Command commandOnStopOp;
readonly TestMethods testMethod;
bool started = false;
///
/// Internal states of this operation
///
enum OpState
{
StartingCommand,
CommandStarted,
OpCompleted,
}
OpState opState;
/// <summary>
/// Starts the measurement.
/// Events: MeasurementStarted
/// </summary>
/// <param name="controlBoard">Control board component reference</param>
/// <param name="outputPath">Specifies used components</param>
/// <param name="testMethod">Test method</param>
/// <param name="refPulses">Test duration in number of reference flowmeter pulses</param>
/// <param name="filterConstant">Specifies filter for watermeter pulses</param>
public ExecuteCommandOp(ControlBoardDev controlBoard, Command commandOnStartOp, Command commandOnStopOp, TestMethods testMethod)
{
if (controlBoard == null) throw new ArgumentNullException("controlBoardDev");
this.controlBoard = controlBoard;
this.commandOnStartOp = commandOnStartOp;
this.commandOnStopOp = commandOnStopOp;
this.testMethod = testMethod;
log.Debug(this.ToString());
}
/// <summary>Start this operation</summary>
public void Start()
{
controlBoard.SyncRoute();
controlBoard.SendCommand(commandOnStartOp, 0, 0, 0, testMethod, StopDevs.None);
opState = OpState.StartingCommand;
}
/// <summary>Run this operation</summary>
/// <returns>
/// Event.FlowInDone or Event.FlowOutDone
/// </returns>
public Event Run()
{
if (opState == OpState.StartingCommand)
{
StatusP statusP = controlBoard.StatusP;
log.DebugFormat("statusP = {0} {1} {2} {3}",
(((ulong)statusP >> 48) & 0xFFFF).ToString("X4"),
(((ulong)statusP >> 32) & 0xFFFF).ToString("X4"),
(((ulong)statusP >> 16) & 0xFFFF).ToString("X4"),
((ulong)statusP & 0xFFFF).ToString("X4"));
log.Info("Run() - StartingCommand");
opState = OpState.CommandStarted;
return Event.None;
}
else if (opState == OpState.CommandStarted)
{
log.InfoFormat("Run() - CommandStarted");
opState = OpState.OpCompleted;
return Event.None;
}
else if (opState == OpState.OpCompleted)
{
return Event.CommandCompleted;
}
return Event.None;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
if (commandOnStopOp != Command.None)
{
controlBoard.SyncRoute();
controlBoard.SendCommand(commandOnStopOp, 0, 0, 0, testMethod, StopDevs.None);
}
}
}
}
@@ -0,0 +1,84 @@
///
/// Copyright (c) 2013-2019 Sensus Slovensko a.s.
///
using System;
using Dirichlet.Numerics;
namespace TBF.BenchControl.ControlBoard.Legacy
{
interface IControlCom
{
StatusP StatusP { get; }
int EtPulses(int wmNr1);
int EtPulsesK { get; }
int EtPulses2 { get; }
int EtPulses3 { get; }
UInt16 WMeterPulses(int wmNr1);
int WMeterReference(int wmNr0);
uint RegulValveDAC(int rvNr0);
float Pressure(int prsNr0);
float Temperature(int tmpNr0);
RegulValveState RegulValveState(int rvNr1);
UInt128 RRoute { get; }
uint DivTime(int id); /// 50% id=0, 10% id=1, 90% id=2
float TTime { get; }
float ImpulseTime(int WMNr1);
uint FmState { get; }
uint BeginState(int wmNr0);
double ReferenceFreq { get; }
double ReferenceFreqs(int i); /// Frequences for doble/tripple flowmeter
double ReferenceFlow { get; }
float RValvePosition(int rvNr1);
float DivSamples(int time, int divIx0); /// time unit is 2 ms
int ScopeSamples(int i, int j, int k);
ulong DigitalInputs { get; }
float AnalogInput(int adcNr0);
uint AnalogInputRaw(int adcNr0, int bank);
float TotalRefVolume { get; }
float VyslExt(int wmNr0, int what);
void SetWeight(int pos, double mass);
uint[] CyclePar { get; set; }
uint[] WMeterCont { get; }
bool ManualUIAllowed { get; set; }
float ValveOpenCloseTime { get; }
void SetDivLimit(int divNr, uint limLoPct, uint limHiPct);
void SetReservoirTemp(int nr, float temp);
/// <summary>
/// Sends calibration and configuration data to the control board.
/// </summary>
/// <param name="rvCalib">Regulating valves calib.coefs, ix1 = valve nr. (1..5), ix2 = coef.nr. (0..1), values typ. c0 = 0, c1 = 0.2</param>
/// <param name="meretA">Meret address, index = 0..1, value typ. 99 and 100</param>
/// <param name="usedCom">COM port number of the control board (typ. 1)</param>
/// <param name="tempCalib">Temp.calc.coefs, ix1 = temp.sens.nr. (0..7 ???), ix2 = coef.nr</param>
/// <param name="etCalib">Etalon nominal values</param>
/// <param name="divEdge">Percentages for switching of diverters, ix = diverter nr.(0,1), typ.value 50 (%)</param>
/// <param name="balanceRange">TODO</param>
/// <param name="meretProtocol">TODO</param>
void SendCalibData(uint[,] rvCalib, byte[] meretA, int usedCom, float[,] tempCalib, float[] etCalib, uint[] divEdge, uint[] balanceRange, byte meretProtocol);
/// <summary>
/// Updates a command that is regularly sent to the control board by SendCommand.
/// </summary>
/// <param name="sendCommandArgs">Arguments, see SendCommandArgs class</param>
/// <param name="route">State of valves, 128-bit word</param>
/// <param name="filterConstants">Array with length=8 of filter costants (0..255, 0 = no filtering)</param>
/// <param name="fmFreq">Freq.inverter control (not applicable in DT100, Munich)</param>
/// <param name="regConst">PID coefficient for flow control</param>
/// <param name="shortImp">0 = default value, 1 = spec. processing of very short pulses</param>
void SendCommand(SendCommandArgs sendCommandArgs, UInt128 route, uint[] filterConstants, float[] fmFreq, int regConst, int shortImp);
/// <summary>
/// Control of regulating valves.
/// </summary>
/// <param name="valveNo">Regulating valve nr. (1..5)</param>
/// <param name="valveMode">???</param>
/// <param name="valveValue">???</param>
/// <param name="stableTime">Stabilization time when setting the flow: 0=200ms, step 50ms, max. 1.5 sec.</param>
void ValveMove(int valveNo, RegulValveMode valveMode, float[] valveValue, int stableTime);
void RunDeviceBefore();
void RunDeviceAfter();
}
}
@@ -0,0 +1,59 @@
///
/// Copyright (c) 2013-2020 Sensus Slovensko a.s.
///
using System;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class QueryMeasurementEndOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(QueryMeasurementEndOp));
public override string ToString() { return string.Format("QueryMeasurementEndOp()"); }
/// Set by the constructor
readonly ControlBoardDev controlBoard;
bool done;
/// <summary>
/// Events: MeasurementCompleted
/// </summary>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public QueryMeasurementEndOp(ControlBoardDev controlBoardDev)
{
if (controlBoardDev == null) throw new ArgumentNullException("controlBoardDev");
this.controlBoard = controlBoardDev;
done = false;
log.Debug(this.ToString());
}
/// <summary> Start this operation </summary>
public void Start()
{
done = false;
}
/// <summary> Run this operation </summary>
/// <returns>
/// Event.FlowInDone or Event.FlowOutDone
/// </returns>
public Event Run()
{
if (done) return Event.MeasurementCompleted;
if (0 != (controlBoard.StatusP & StatusP.TestCompleted))
{
done = true;
return Event.MeasurementCompleted;
}
return Event.None;
}
/// <summary> Stop this operation </summary>
public void Stop()
{
}
}
}
@@ -0,0 +1,203 @@
///
/// Copyright (c) 2017 Sensus Metering Systems
///
using System;
using log4net;
using TBF.Boxes;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ReadDiverterTransitionOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(ReadDiverterTransitionOp));
public override string ToString()
{
return string.Format("ReadDiverterTransitionOp(.,{0},{1},{2},...)", div.Name, runsCountWhenSendingCmd, runsCountWhenReadingDiv);
}
const int SamplesCount = 240; /// Should be less then or equal to 256
float[] samples;
/// Set by the constructor
readonly ControlBoardDev controlBoard;
readonly GenericDevices.IDiverter div;
readonly bool toTank;
readonly int runsCountWhenSendingCmd;
readonly int runsCountWhenReadingDiv;
readonly Sequences.Statistics plotter;
readonly int batchNr;
readonly string testName;
readonly int repetition;
int counter;
bool cmdSent = false;
bool divRead = false;
/// <summary>
/// Starts the measurement.
/// Events: MeasurementStarted
/// </summary>
/// <param name="controlBoard">Control board component reference</param>
/// <param name="outputPath">Specifies used components</param>
/// <param name="method">Test method</param>
/// <param name="refPulses">Test duration in number of reference flowmeter pulses</param>
/// <param name="filterConstant">Specifies filter for watermeter pulses</param>
public ReadDiverterTransitionOp(ControlBoardDev controlBoard, GenericDevices.IDiverter div, int runsCountWhenSendingCmd, int runsCountWhenReadingDiv, bool toTank,
Sequences.Statistics plotter, int batchNr, string testName, int repetition)
{
if (controlBoard == null) throw new ArgumentNullException("controlBoardDev");
if (div == null) throw new ArgumentNullException("div");
if (runsCountWhenSendingCmd < 0) throw new ArgumentNullException("runsCountWhenSendingCmd");
if (runsCountWhenReadingDiv <= runsCountWhenSendingCmd) throw new ArgumentNullException("runsCountWhenReadingDiv");
this.controlBoard = controlBoard;
this.div = div;
this.runsCountWhenSendingCmd = runsCountWhenSendingCmd;
this.runsCountWhenReadingDiv = runsCountWhenReadingDiv;
this.toTank = toTank;
this.plotter = plotter;
this.batchNr = batchNr;
this.testName = testName;
this.repetition = repetition;
samples = new float[SamplesCount];
log.Debug(this.ToString());
}
/// <summary>Start this operation</summary>
public void Start()
{
counter = 0;
if (runsCountWhenSendingCmd == 0 && div.DiverterNr > 0)
{
SendCommand();
log.DebugFormat("Start() ... Sending a command to read diverter {0}", div.Name);
}
if (plotter != null)
{
plotter.Start(batchNr, testName, repetition);
log.DebugFormat("Start() ... plotter.Start({0}, {1}, {2})", batchNr, testName, repetition);
}
}
/// <summary>Run this operation</summary>
/// <returns>
/// Event.FlowInDone or Event.FlowOutDone
/// </returns>
public Event Run()
{
counter++;
if (counter == runsCountWhenSendingCmd && div.DiverterNr > 0)
{
SendCommand();
log.DebugFormat("Run() ... Sending a command to read diverter {0}", div.Name);
}
else if (counter == runsCountWhenReadingDiv && div.DiverterNr > 0)
{
if (plotter == null)
{
ReadDiverterTransitionTimes();
log.DebugFormat("Run() ... Reading diverter {0} transition times: {1}s", div.Name, (toTank ? div.SwitchTimeStart : div.SwitchTimeEnd).Val);
}
else
{
log.DebugFormat("Run() ... Reading diverter {0} samples", div.Name);
for (int i = 0; i < SamplesCount; i++)
{
float oneSample = controlBoard.DivSamples(i, div.DiverterNr - 1);
samples[i] = oneSample;
plotter.Update(oneSample);
}
plotter.Stop();
log.DebugFormat("Run() ... Calculating diverter {0} transition times: {1}s", div.Name, (toTank ? div.SwitchTimeStart : div.SwitchTimeEnd).Val);
CalculateDiverterTransitionTimes();
}
}
return Event.None;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
log.DebugFormat("Stop()");
}
void SendCommand()
{
controlBoard.SyncRoute();
controlBoard.SendCommand(Command.ReadDivTransition, 0, 0, 0, 0, StopDevs.None);
cmdSent = true;
}
/// <summary>
/// Diverter transition time is read from the control board
/// </summary>
/// <returns>true (always)</returns>
bool ReadDiverterTransitionTimes()
{
(toTank ? div.SwitchTimeStart : div.SwitchTimeEnd).Val = (float)controlBoard.DivTime(2) - (float)controlBoard.DivTime(1);
return true;
}
/// <summary>
/// Diverter transition time is calculated from ADC samples and threshold levels
/// </summary>
/// <returns>true when transition time calculated OK</returns>
bool CalculateDiverterTransitionTimes()
{
bool startedRising = false;
bool startedFalling = false;
bool stopped = false;
int transitionStartIdx = 0;
int transitionEndIdx = 0;
float level = samples[0];
for (int i = 1; i < SamplesCount; i++)
{
if (!startedRising && !startedFalling)
{
if (level <= div.ThresholdLo && samples[i] > div.ThresholdLo)
{
transitionStartIdx = i;
startedRising = true;
}
else if (level >= div.ThresholdHi && samples[i] < div.ThresholdHi)
{
transitionStartIdx = i;
startedFalling = true;
}
}
else if (startedRising && !stopped && level < div.ThresholdHi && samples[i] >= div.ThresholdHi)
{
transitionEndIdx = i;
stopped = true;
break;
}
else if (startedFalling && !stopped && level > div.ThresholdLo && samples[i] <= div.ThresholdLo)
{
transitionEndIdx = i;
stopped = true;
break;
}
}
if ((startedRising || startedFalling) && stopped)
{
(toTank ? div.SwitchTimeStart : div.SwitchTimeEnd).Val = 0.002f * (float)(transitionEndIdx - transitionStartIdx); /// 2 ms per sample
return true;
}
else
{
return false;
}
}
}
}
@@ -0,0 +1,70 @@
///
/// Copyright (c) 2017 Sensus Slovensko a.s.
///
using System;
using log4net;
using TBF.Boxes;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class ReadFlowOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(ReadFlowOp));
public override string ToString() { return string.Format("ReadFlowOp({0},.,.,{1})", flowMeter.Idx1, eventDone); }
/// Set by the constructor
readonly GenericDevices.IFlowMeter flowMeter;
readonly ControlBoardDev controlBoard;
readonly DoubleBox flowBox;
readonly Event eventDone;
/// <summary>
/// Events: FlowInDone, FlowOutDone or Error
/// </summary>
/// <param name="flowMeter">Flow meter reference</param>
/// <param name="flowBox">Reference to the measured flow variable, value is in bar</param>
/// <param name="eventDone">Event to be returned by Run() when completed OK</param>
public ReadFlowOp(GenericDevices.IFlowMeter flowMeter, ControlBoardDev controlBoard, ref DoubleBox flowBox, Event eventDone)
{
if (flowMeter == null || controlBoard == null) throw new ArgumentNullException();
this.flowMeter = flowMeter;
this.controlBoard = controlBoard;
this.eventDone = eventDone;
this.flowBox = flowBox;
log.Debug(this.ToString());
}
public ReadFlowOp(GenericDevices.IFlowMeter flowMeter, ControlBoardDev controlBoard, ref DoubleBox flowBox)
: this(flowMeter, controlBoard, ref flowBox, Event.FlowMeasurementDone)
{
}
/// <summary>Start this operation</summary>
public void Start()
{
}
/// <summary>Run this operation</summary>
/// <returns>
/// Event.FlowInDone or Event.FlowOutDone
/// </returns>
public Event Run()
{
if ((int)(controlBoard.StatusP & StatusP.RefFlowMeterMask) == flowMeter.Idx1)
{
flowBox.Val = flowMeter.ReadFlow();
return eventDone;
}
else
{
return Event.Error;
}
}
/// <summary>Stop this operation</summary>
public void Stop()
{
}
}
}
@@ -0,0 +1,33 @@
///
/// Copyright (c) 2019 Sensus Slovensko a.s.
///
using System;
using Dirichlet.Numerics;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class SendCommandArgs
{
public Command Cmd;
public int RefFlowmtrNr;
public int TotalRefPulses;
public int MassRefPulses;
public TestMethods TestMethods;
public StopDevs StopDevs;
public SendCommandArgs()
{
}
public SendCommandArgs(Command cmd, int refFlowmtrNr, int totalRefPulses, int massRefPulses,
TestMethods testMethods, StopDevs stopDevs)
{
Cmd = cmd;
RefFlowmtrNr = refFlowmtrNr;
TotalRefPulses = totalRefPulses;
MassRefPulses = massRefPulses;
TestMethods = testMethods;
StopDevs = stopDevs;
}
}
}
@@ -0,0 +1,217 @@
///
/// Copyright (c) 2013-2019 Sensus Slovensko a.s.
///
using System;
using log4net;
using TBF.BenchControl.GenericDevices;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class StartMeasurementOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(StartMeasurementOp));
public override string ToString() { return string.Format("StartMeasurementOp(ref={0},pulses={1})", flowMeterNr, totalRefPulses); }
/// Set by the constructor
readonly ControlBoardDev controlBoard;
readonly IRegulValve regulValve;
readonly int regulValveNr;
readonly int regulValveStableTime;
readonly int flowMeterNr;
readonly double nominalFlow;
readonly double maximalFlow;
readonly double reqFlowLo;
readonly double reqFlowHi;
readonly int totalRefPulses; /// Number of reference pulses for a complete test
readonly int massRefPulses; /// Number of reference pulses for mass collection
readonly TestMethods testMethod;
readonly int diverterNr;
///
/// Internal states of this operation
///
enum OpState
{
StartingTest,
ResendCommand,
TestStarted,
ValveMoveToFlow,
OpCompleted,
}
OpState opState;
/// <summary>
/// Starts the measurement.
/// Events: MeasurementStarted
/// </summary>
/// <param name="controlBoard">Control board component reference</param>
/// <param name="outputPath">Specifies used components</param>
/// <param name="method">Test method</param>
/// <param name="totalRefPulses">Test duration in number of reference flowmeter pulses</param>
/// <param name="filterConstant">Specifies filter for watermeter pulses</param>
public StartMeasurementOp(ControlBoardDev controlBoard, OutputPath outputPath, TestMethods method,
double requiredFlowLo, double requiredFlowHi, int refPulses)
: this(controlBoard, outputPath, method, requiredFlowLo, requiredFlowHi, refPulses, refPulses)
{
}
/// <summary>
/// Starts the measurement.
/// Events: MeasurementStarted
/// </summary>
/// <param name="controlBoard">Control board component reference</param>
/// <param name="outputPath">Specifies used components</param>
/// <param name="method">Test method</param>
/// <param name="refPulses">Test duration in number of reference flowmeter pulses</param>
/// <param name="filterConstant">Specifies filter for watermeter pulses</param>
public StartMeasurementOp(ControlBoardDev controlBoard, OutputPath outputPath, TestMethods method,
double requiredFlowLo, double requiredFlowHi, int totalRefPulses, int massRefPulses)
{
if (controlBoard == null) throw new ArgumentNullException("controlBoardDev");
this.controlBoard = controlBoard;
if (outputPath.FlowMeter == null) throw new ArgumentNullException("Flow meter is missing");
flowMeterNr = outputPath.FlowMeter.Idx1;
this.regulValve = outputPath.RegulValve;
if (regulValve is Elde.RegulValve.RegulValve)
{
Elde.RegulValve.RegulValve rv = outputPath.RegulValve as Elde.RegulValve.RegulValve;
regulValveNr = rv.Idx1;
regulValveStableTime = rv.StableTime;
}
else if (regulValve is Elde.RegulValveCoax.RegulValve)
{
Elde.RegulValveCoax.RegulValve rv = outputPath.RegulValve as Elde.RegulValveCoax.RegulValve;
regulValveNr = rv.Idx1;
regulValveStableTime = rv.StableTime;
}
else if (!(regulValve is Dummy.RegulValve.RegulValve))
{
throw new ArgumentNullException("Regulation valve is not compatible with Elde Control Board");
}
if (outputPath.Diverter is Elde.Diverter.Diverter)
{
diverterNr = (outputPath.Diverter as Elde.Diverter.Diverter).DiverterNr;
}
nominalFlow = outputPath.FlowMeter.NominalFlow;
maximalFlow = 1.25 * nominalFlow;
this.reqFlowLo = requiredFlowLo;
this.reqFlowHi = requiredFlowHi;
this.totalRefPulses = totalRefPulses;
this.massRefPulses = massRefPulses;
this.testMethod = method;
log.Debug(this.ToString());
}
void SendStartCommand()
{
controlBoard.SyncRoute();
#if TURA_IPERL || TURA_IPERL_NEW || TURA_SPECIAL || BADGER_VELKA_TRAT || SLM_50
int flowMeterIdxAndDiv = flowMeterNr + 16 * diverterNr;
#elif BADGER_STREDNA_TRAT
int flowMeterIdxAndDiv = flowMeterNr + 8 * (diverterNr - 1);
#elif FILIPINY_50
int flowMeterIdxAndDiv = flowMeterNr + ((flowMeterNr == 3) ? (4 * (diverterNr - 1)) : 0);
#elif GELSENWASSER
int flowMeterIdxAndDiv = flowMeterNr + ((diverterNr == 2) ? 16 : 0);
#else
int flowMeterIdxAndDiv = flowMeterNr;
#endif
log.InfoFormat("Start() : SendCommand(Cmd.Start, {0}, route, {1}, {1}, {2}, Stop.None)",
flowMeterIdxAndDiv, totalRefPulses, testMethod);
controlBoard.SendCommand(Command.Start, flowMeterIdxAndDiv, totalRefPulses, massRefPulses, testMethod, StopDevs.None);
}
/// <summary>Start this operation</summary>
public void Start()
{
SendStartCommand();
opState = OpState.StartingTest;
}
/// <summary>Run this operation</summary>
/// <returns>
/// Event.FlowInDone or Event.FlowOutDone
/// </returns>
public Event Run()
{
if (opState == OpState.StartingTest)
{
StatusP statusP = controlBoard.StatusP;
log.DebugFormat("statusP = {0} {1} {2} {3}",
(((ulong)statusP >> 48) & 0xFFFF).ToString("X4"),
(((ulong)statusP >> 32) & 0xFFFF).ToString("X4"),
(((ulong)statusP >> 16) & 0xFFFF).ToString("X4"),
((ulong)statusP & 0xFFFF).ToString("X4"));
if ((statusP & StatusP.TestInProgress) != 0)
{
log.Info("Test started");
opState = OpState.TestStarted;
}
else
{
//opState = OpState.ResendCommand;
}
return Event.None;
}
else if (opState == OpState.ResendCommand)
{
SendStartCommand();
opState = OpState.StartingTest;
return Event.None;
}
else if (opState == OpState.TestStarted)
{
///
/// Done once, issues an appropriate ValveMove(...) command
///
if ((reqFlowLo >= reqFlowHi) || (reqFlowLo > maximalFlow) || (reqFlowHi <= 0))
{
return Event.OpArgumentError;
}
log.InfoFormat("Run(): rv#={0} flowMtr#={1} TARGET: flowLo={2} flowHi={3}",
regulValveNr, flowMeterNr, reqFlowLo, reqFlowHi);
double freqLo = 2000.0 * reqFlowLo / nominalFlow;
double freqHi = 2000.0 * reqFlowHi / nominalFlow;
if (regulValve.IsCoax)
{
controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetFrequency,
new float[2] { (float)((freqLo + freqHi) / 2), (float)((freqLo + freqHi) / 2) },
regulValveStableTime);
}
else
{
controlBoard.ValveMove(regulValveNr, RegulValveMode.TargetFrequency,
new float[2] { (float)freqLo, (float)freqHi },
regulValveStableTime);
}
opState = OpState.OpCompleted;
return Event.None;
}
else if (opState == OpState.OpCompleted)
{
return Event.MeasurementStarted;
}
return Event.None;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
}
}
}
@@ -0,0 +1,62 @@
///
/// Copyright (c) 2019 Sensus Slovensko a.s.
///
using System;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
class StopFlowRegulationOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(StopFlowRegulationOp));
public override string ToString() { return string.Format("StopFlowRegulationOp(.)"); }
/// Set by the constructor
readonly ControlBoardDev controlBoard;
readonly int flowMeterNr;
/// <summary>
/// Stop the previous control board operation (diverter, gate, etc.)
/// Events: None or PreviousStopped
/// </summary>
/// <param name="controlBoardDev">Control board component reference</param>
public StopFlowRegulationOp(ControlBoardDev controlBoardDev)
{
if (controlBoardDev == null) throw new ArgumentNullException("controlBoardDev");
this.controlBoard = controlBoardDev;
if (controlBoardDev.Devices.FlowMeter == null) throw new ArgumentNullException("Flow meter is missing");
flowMeterNr = controlBoardDev.Devices.FlowMeter.Idx1;
log.Debug(this.ToString());
}
void SendStopFlowRegulationCommand()
{
controlBoard.SyncRoute();
controlBoard.SendCommand(Command.Stop, flowMeterNr, 0, 0, 0, StopDevs.RegValveRegulation);
}
/// <summary>Start this operation</summary>
public void Start()
{
SendStopFlowRegulationCommand();
}
/// <summary>Run this operation</summary>
/// <returns>Event.FlowRegulationStopped</returns>
public Event Run()
{
return Event.FlowRegulationStopped;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
}
}
}
@@ -0,0 +1,107 @@
///
/// Copyright (c) 2013-2015 Sensus Metering Systems
///
using System;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
public class StopPreviousOp : IOperation
{
private static readonly ILog log = LogManager.GetLogger(typeof(StopPreviousOp));
public override string ToString() { return string.Format("StopPreviousOp(.)"); }
/// Set by the constructor
readonly ControlBoardDev controlBoard;
///
/// Internal states of this operation
///
enum OpState
{
Idle = 0,
CheckIfStopped,
ResendCommand,
Stopped,
}
OpState opState;
/// <summary>
/// Stop the previous control board operation (diverter, gate, etc.)
/// Events: None or PreviousStopped
/// </summary>
/// <param name="controlBoardDev">Control board component reference</param>
public StopPreviousOp(ControlBoardDev controlBoardDev)
{
if (controlBoardDev == null) throw new ArgumentNullException("controlBoardDev");
this.controlBoard = controlBoardDev;
log.Debug(this.ToString());
}
void SendStopCommand()
{
controlBoard.SyncRoute();
controlBoard.SendCommand(Command.Stop, 0, 0, 0, TestMethods.Diverter,
StopDevs.Diverter | StopDevs.GatePulse | StopDevs.Reference | StopDevs.RegValveRegulation);
}
/// <summary>Start this operation</summary>
public void Start()
{
SendStopCommand();
opState = OpState.CheckIfStopped;
}
/// <summary>Run this operation</summary>
/// <returns>
/// Event.None or Event.PreviousStopped
/// </returns>
public Event Run()
{
if (opState == OpState.CheckIfStopped)
{
if ((controlBoard.StatusP & (StatusP.TestCompleted | StatusP.TestInProgress)) == 0)
{
opState = OpState.Stopped;
return Event.PreviousStopped;
}
else
{
opState = OpState.ResendCommand;
return Event.None;
}
}
else if (opState == OpState.ResendCommand)
{
if ((controlBoard.StatusP & (StatusP.TestCompleted | StatusP.TestInProgress)) == 0)
{
opState = OpState.Stopped;
return Event.PreviousStopped;
}
else
{
SendStopCommand();
opState = OpState.CheckIfStopped;
return Event.None;
}
}
else if (opState == OpState.Stopped)
{
return Event.PreviousStopped;
}
return Event.None;
}
/// <summary>Stop this operation</summary>
public void Stop()
{
opState = OpState.Idle;
}
}
}
@@ -0,0 +1,774 @@
///
/// Copyright (c) 2013-2020 Sensus Slovensko a.s.
///
using System;
using Dirichlet.Numerics;
using log4net;
namespace TBF.BenchControl.ControlBoard.Legacy
{
/// <summary>
/// Test bench model inherited by all classes for the simulation of devices.
/// Define DN100, MUNICH or nothing.
/// </summary>
public static class TestBenchSim
{
private static readonly ILog log = LogManager.GetLogger(typeof(TestBenchSim));
///
/// Incremented in RunDevice() depending on diverters and valves
///
public static double Mass1sim; /// Volume in [ltr] or mass in [kg] in the tank1 on the balance1, filled by the diverter1
public static double Mass2sim; /// Volume in [ltr] or mass in [kg] in the tank2 on the balance2, filled by the diverter2
///
/// Set by regulation valves
///
public static double Flow1sim; /// flow in [m3/h] through RV1
public static double Flow2sim; /// flow in [m3/h] through RV2
public static double Flow3sim; /// flow in [m3/h] through RV3
public static double Flow4sim; /// flow in [m3/h] through RV4
public static double Flow5sim; /// flow in [m3/h] through RV5
public static double Flow6sim; /// flow in [m3/h] through RV5
///
/// Valves and diverters
///
public static UInt128 SimRoute;
public static bool Div1sim;
public static bool Div2sim;
public static bool Div3sim;
static int regulValveNo; /// 0=undefined, 1..5
static int refFlowmtrNo; /// 0=undefined, 1..4
public static void Initialize()
{
Mass1sim = 0; /// Initialize tanks
Mass2sim = 0;
Flow1sim = 0; /// Initialize regulation valves
Flow2sim = 0;
Flow3sim = 0;
Flow4sim = 0;
Flow5sim = 0;
Flow6sim = 0;
SimRoute = 0; /// Initialize other valves
Div1sim = false;
Div2sim = false;
Div3sim = false;
}
#if DN100
/// Paths modulating the flows
const ulong K5 = (1L << 0);
const ulong K7 = (1L << 1);
const ulong K10 = (1L << 2);
const ulong K13 = (1L << 3);
const ulong K16 = (1L << 4);
const ulong K2 = (1L << 5);
const ulong EmptyValve1 = (1L << 17);
const ulong EmptyValve2 = (1L << 18);
const ulong Vl5 = (1L << 21);
const ulong Path1 = K2 | K5 | K7;
const ulong Path2 = K2 | K5 | K10;
const ulong Path3 = K2 | K5 | K13;
const ulong Path4 = K2 | K5 | K16;
const ulong Path5 = K2 | K5 | Vl5;
#elif MUNICH
const ulong VL11 = (1L << 0);
const ulong VL13 = (1L << 1);
const ulong VL21 = (1L << 2);
const ulong VL23 = (1L << 3);
const ulong VZ1 = (1L << 4);
const ulong VP2 = (1L << 5);
const ulong VP4 = (1L << 6);
const ulong VP5 = (1L << 7);
const ulong VP6 = (1L << 8);
const ulong VL12 = (1L << 9);
const ulong VL22 = (1L << 10);
const ulong VI11 = (1L << 11);
const ulong VI12 = (1L << 12);
const ulong VI13 = (1L << 13);
const ulong VI21 = (1L << 14);
const ulong VI22 = (1L << 15);
const ulong VI2P = (1L << 16);
const ulong VI31 = (1L << 17);
const ulong VI32 = (1L << 18);
const ulong VI3P = (1L << 19);
const ulong VI41 = (1L << 20);
const ulong VI42 = (1L << 21);
const ulong VI43 = (1L << 22);
const ulong VI4P = (1L << 23);
const ulong VB1 = (1L << 24);
const ulong VB1a = (1L << 25);
const ulong VB2 = (1L << 26);
const ulong VB2a = (1L << 27);
const ulong VM1 = (1L << 28);
const ulong VM2 = (1L << 29);
const ulong VM3 = (1L << 30);
const ulong VM4 = ((ulong)1 << 31);
const ulong Pump1 = ((ulong)1 << 32);
const ulong Block = ((ulong)1 << 33);
const ulong VZ8 = ((ulong)1 << 37);
const ulong EmptyValve1 = VB1 | VB1a;
const ulong EmptyValve2 = VB2 | VB2a;
const ulong Path1 = VZ1 | VL12 | VI11 | VI13;
const ulong Path2 = VZ1 | VL11 | VL12 | VI21 | VI22 | VI13;
const ulong Path3 = VZ1 | VL11 | VL12 | VI31 | VI32;
const ulong Path4 = VZ1 | VL11 | VL12 | VI41 | VI42;
const ulong Path5 = VZ1 | VL11 | VL12 | VI41 | VI43;
#elif FUZHOU_150
const ulong VL11 = (1L << 2);
const ulong VL13 = (1L << 3);
const ulong VL4 = (1L << 4);
const ulong VZ6 = (1L << 5);
const ulong VZ7 = (1L << 6);
const ulong VP5 = (1L << 7);
const ulong VP6 = (1L << 8);
const ulong VL12 = (1L << 9);
const ulong VZ14 = (1L << 10);
const ulong VI11 = (1L << 11);
const ulong VI12 = (1L << 12);
const ulong VI13 = (1L << 13);
const ulong VI21 = (1L << 14);
const ulong VI22 = (1L << 15);
const ulong VI2p = (1L << 16);
const ulong VI31 = (1L << 17);
const ulong VI32 = (1L << 18);
const ulong VI3p = (1L << 19);
const ulong VI41 = (1L << 20);
const ulong VI42 = (1L << 21);
const ulong VI43 = (1L << 22);
const ulong VI4p = (1L << 23);
const ulong VB1 = (1L << 24);
const ulong VB1a = (1L << 25);
const ulong VB2 = (1L << 26);
const ulong VB2a = (1L << 27);
const ulong VM1 = (1L << 28);
const ulong VM2 = (1L << 29);
const ulong VM3 = (1L << 20);
const ulong Pump1 = ((ulong)1 << 32);
const ulong Pump4 = ((ulong)1 << 33);
const ulong Pump2 = ((ulong)1 << 37);
const ulong Pump3 = ((ulong)1 << 38);
const ulong Pump5 = ((ulong)1 << 39);
const ulong EmptyValve1 = VB1 | VB1a;
const ulong EmptyValve2 = VB2 | VB2a;
const ulong Path1 = VL12 | VI11 | VI13;
const ulong Path2 = VL12 | VI21 | VI22 | VI13;
const ulong Path3 = VL12 | VI31 | VI32;
const ulong Path4 = VL12 | VI41 | VI42;
const ulong Path5 = VL12 | VI41 | VI43;
#elif FUZHOU_300
const ulong VL11 = (1L << 2);
const ulong VL13 = (1L << 3);
const ulong VL4 = (1L << 4);
const ulong VZ6 = (1L << 5);
const ulong VZ7 = (1L << 6);
const ulong VP5 = (1L << 7);
const ulong VP6 = (1L << 8);
const ulong VL12 = (1L << 9);
const ulong VZ14 = (1L << 10);
const ulong VI11 = (1L << 11);
const ulong VI12 = (1L << 12);
const ulong VI13 = (1L << 13);
const ulong VI21 = (1L << 14);
const ulong VI22 = (1L << 15);
const ulong VI2p = (1L << 16);
const ulong VI31 = (1L << 17);
const ulong VI32 = (1L << 18);
const ulong VI3p = (1L << 19);
const ulong VI41 = (1L << 20);
const ulong VI42 = (1L << 21);
const ulong VI43 = (1L << 22);
const ulong VI4p = (1L << 23);
const ulong VB1 = (1L << 24);
const ulong VB1a = (1L << 25);
const ulong VB2 = (1L << 26);
const ulong VB2a = (1L << 27);
const ulong VM1 = (1L << 28);
const ulong VM2 = (1L << 29);
const ulong VM3 = (1L << 20);
const ulong Pump1 = ((ulong)1 << 32);
const ulong Pump4 = ((ulong)1 << 33);
const ulong Pump2 = ((ulong)1 << 37);
const ulong Pump3 = ((ulong)1 << 38);
const ulong Pump5 = ((ulong)1 << 39);
const ulong EmptyValve1 = VB1 | VB1a;
const ulong EmptyValve2 = VB2 | VB2a;
const ulong Path1 = VL12 | VI11 | VI13;
const ulong Path2 = VL12 | VI21 | VI22 | VI13;
const ulong Path3 = VL12 | VI31 | VI32;
const ulong Path4 = VL12 | VI41 | VI42;
const ulong Path5 = VL12 | VI41 | VI43;
#elif IZRAEL_200 || PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER
const ulong VM11 = (1L << 4);
const ulong VM12 = (1L << 5);
const ulong VM21 = (1L << 6);
const ulong VM22 = (1L << 7);
const ulong VZ14 = (1L << 8);
const ulong VZ13 = (1L << 9);
const ulong VL11 = (1L << 10);
const ulong VL13 = (1L << 11);
const ulong VL4 = (1L << 12);
const ulong VZ6 = (1L << 13);
const ulong VZ7 = (1L << 14);
const ulong VP5 = (1L << 15);
const ulong VP6 = (1L << 16);
const ulong VL12 = (1L << 17);
const ulong VL51 = (1L << 18);
const ulong VL53 = (1L << 19);
const ulong VI11 = (1L << 20);
const ulong VI12 = (1L << 21);
const ulong VI13 = (1L << 22);
const ulong VV1 = (1L << 23);
const ulong VI21 = (1L << 25);
const ulong VI22 = (1L << 26);
const ulong VI2p = (1L << 27);
const ulong VI31 = (1L << 28);
const ulong VI32 = (1L << 29);
const ulong VI3p = (1L << 30);
const ulong VI41 = (1L << 31);
const ulong VI42 = (1L << 32);
const ulong VI4p = (1L << 33);
const ulong VI43 = (1L << 37);
const ulong VM1 = (1L << 50);
const ulong VM2 = (1L << 51);
const ulong VM3 = (1L << 52);
const ulong VB1 = (1L << 41);
const ulong VB1a = (1L << 42);
const ulong VB2 = (1L << 43);
const ulong VB2a = (1L << 44);
const ulong Pump1 = (1L << 48);
const ulong Pump4 = (1L << 49);
const ulong Pump2 = (1L << 38);
const ulong Pump3 = (1L << 39);
const ulong Pump5 = (1L << 40);
const ulong EmptyValve1 = VB1 | VB1a;
const ulong EmptyValve2 = VB2 | VB2a;
const ulong Path1 = VL12 | VI11 | VI13;
const ulong Path2 = VL12 | VI21 | VI22 | VI13;
const ulong Path3 = VL12 | VI31 | VI32;
const ulong Path4 = VL12 | VI41 | VI42;
const ulong Path5 = VL12 | VI41 | VI43;
#elif BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || BADGER_VELKA_TRAT
const ulong VZ34 = (1L << 0);
const ulong VZ35 = (1L << 1);
const ulong VZ39 = (1L << 2);
const ulong VZ37 = (1L << 3);
const ulong VZ38 = (1L << 4);
const ulong VL31 = (1L << 5);
const ulong VV1 = (1L << 6);
const ulong VS33 = (1L << 7);
const ulong VS31 = (1L << 8);
const ulong VS32 = (1L << 9);
const ulong VV2 = (1L << 10);
const ulong VL32 = (1L << 11);
const ulong VI31 = (1L << 12);
const ulong VI32 = (1L << 13);
const ulong VI33 = (1L << 14);
const ulong VI34 = (1L << 15);
const ulong DIV1 = (1L << 16);
const ulong DIV2 = (1L << 17);
const ulong VB1 = (1L << 18);
const ulong VB1a = (1L << 19);
const ulong VB2 = (1L << 20);
const ulong VB2a = (1L << 21);
const ulong P1 = (1L << 22);
const ulong P2 = (1L << 23);
const ulong EmptyValve1 = VB1 | VB1a;
const ulong EmptyValve2 = VB2 | VB2a;
const ulong Path1 = VI31;
const ulong Path2 = VI32;
const ulong Path3 = VI33;
const ulong Path4 = VI34;
const ulong Path5 = 0;
#elif CEVAK_40 || MURES_40 || FUZHOU_50 || SLM_50 || MALTA_WSD25
const ulong VL11 = (1L << 0);
const ulong VL13 = (1L << 1);
const ulong VL18 = (1L << 2);
const ulong VZ12 = (1L << 3);
const ulong VL15 = (1L << 4);
const ulong VL21 = (1L << 5);
const ulong VL23 = (1L << 6);
const ulong VL28 = (1L << 7);
const ulong VZ5 = (1L << 8);
const ulong VI1 = (1L << 9);
const ulong VI2 = (1L << 10);
const ulong VI3 = (1L << 11);
const ulong VZ9 = (1L << 12);
const ulong VBa = (1L << 13);
const ulong VB = (1L << 14);
const ulong DIV1 = (1L << 15);
const ulong P1 = (1L << 16);
const ulong P2 = (1L << 17);
const ulong VL9 = (1L << 18);
const ulong VV1 = (1L << 19);
const ulong VV2 = (1L << 20);
const ulong VZ6 = (1L << 21);
const ulong VV3 = (1L << 22);
const ulong Gate = (1L << 23);
const ulong EmptyValve1 = VB | VBa;
const ulong EmptyValve2 = 0x0000FFFFFFFFFFFF;
const ulong Path1 = VI1;
const ulong Path2 = VI2;
const ulong Path3 = VI3;
const ulong Path4 = 0;
const ulong Path5 = 0;
#elif TORINO_50
const ulong nic1 = (1L << 0);
const ulong nic2 = (1L << 1);
const ulong VL11 = (1L << 2);
const ulong VL13 = (1L << 3);
const ulong VL18 = (1L << 4);
const ulong VZ13 = (1L << 5);
const ulong VL15 = (1L << 6);
const ulong VZ10 = (1L << 7);
const ulong VZ6 = (1L << 8);
const ulong VV1 = (1L << 9);
const ulong VV4 = (1L << 10);
const ulong VL12 = (1L << 11);
const ulong VI1 = (1L << 12);
const ulong VI2 = (1L << 13);
const ulong VI3 = (1L << 14);
const ulong VI4 = (1L << 15);
const ulong VB1 = (1L << 16);
const ulong VB1a = (1L << 17);
const ulong VB2 = (1L << 18);
const ulong VB2a = (1L << 19);
const ulong DIV1 = (1L << 20);
const ulong DIV2 = (1L << 21);
const ulong P1 = (1L << 22);
const ulong P3 = (1L << 23);
const ulong P2 = (1L << 24);
const ulong EmptyValve1 = VB1 | VB1a;
const ulong EmptyValve2 = VB2 | VB2a;
const ulong Path1 = VI1;
const ulong Path2 = VI2;
const ulong Path3 = VI3;
const ulong Path4 = VI4;
const ulong Path5 = 0;
#elif TORUN_50 || TURA_IPERL || TURA_IPERL_NEW
const ulong VL11 = (1L << 0);
const ulong VL13 = (1L << 1);
const ulong VL15 = (1L << 2);
const ulong VL21 = (1L << 3);
const ulong VL23 = (1L << 4);
const ulong VL18 = (1L << 5);
const ulong VL28 = (1L << 6);
const ulong VZ5 = (1L << 7);
const ulong VZ6 = (1L << 8);
const ulong DIV1 = (1L << 9);
const ulong DIV2 = (1L << 10);
const ulong P1 = (1L << 11);
const ulong P2 = (1L << 12);
const ulong P3 = (1L << 13);
const ulong DIV3 = (1L << 14);
const ulong rez1 = (1L << 15);
const ulong nic1 = (1L << 16);
const ulong nic2 = (1L << 17);
const ulong VI1 = (1L << 18);
const ulong VI2 = (1L << 19);
const ulong VI3 = (1L << 20);
const ulong VI4 = (1L << 21);
const ulong VI5 = (1L << 22);
const ulong VB1 = (1L << 23);
const ulong VV12 = (1L << 24);
const ulong VV22 = (1L << 25);
const ulong VI6 = (1L << 26);
const ulong VV3 = (1L << 27);
const ulong VB2 = (1L << 28);
const ulong EmptyValve1 = VB1;
const ulong EmptyValve2 = VB2;
const ulong Path1 = VI1;
const ulong Path2 = VI2;
const ulong Path3 = VI3;
const ulong Path4 = VI4;
const ulong Path5 = VI5;
const ulong Path6 = VI6;
#elif TURA_SPECIAL
const ulong VL11 = (1L << 0);
const ulong VL13 = (1L << 1);
const ulong VL15 = (1L << 2);
const ulong VL21 = (1L << 3);
const ulong VL23 = (1L << 4);
const ulong VL18 = (1L << 5);
const ulong VL28 = (1L << 6);
const ulong VZ5 = (1L << 7);
const ulong VZ6 = (1L << 8);
const ulong DIV1 = (1L << 9);
const ulong DIV2 = (1L << 10);
const ulong P1 = (1L << 11);
const ulong P2 = (1L << 12);
const ulong P3 = (1L << 13);
const ulong DIV3 = (1L << 14);
const ulong rez1 = (1L << 15);
const ulong nic1 = (1L << 16);
const ulong nic2 = (1L << 17);
const ulong VI1 = (1L << 18);
const ulong VI2 = (1L << 19);
const ulong VI3 = (1L << 20);
const ulong VI4 = (1L << 21);
const ulong VI5 = (1L << 22);
const ulong VB1 = (1L << 23);
const ulong VV12 = (1L << 24);
const ulong VV22 = (1L << 25);
const ulong VI6 = (1L << 26);
const ulong VV3 = (1L << 27);
const ulong VB2 = (1L << 28);
const ulong EmptyValve1 = VB1;
const ulong EmptyValve2 = VB2;
const ulong Path1 = VI1;
const ulong Path2 = VI2;
const ulong Path3 = VI3;
const ulong Path4 = VI4;
const ulong Path5 = VI5;
const ulong Path6 = VI6;
#else
/// Paths modulating the flows
const ulong V1 = (1L << 1);
const ulong V2 = (1L << 2);
const ulong V3 = (1L << 3);
const ulong V3b = (1L << 4);
const ulong V4 = (1L << 5);
const ulong V4b = (1L << 6);
const ulong V5 = (1L << 7);
const ulong V5b = (1L << 8);
const ulong EmptyValve1 = (1L << 9); // =Ve1
const ulong EmptyValve2 = (1L << 10); // =Ve2
const ulong Path1 = V1 | V3 | V3b | V4 | V4b;
const ulong Path2 = V2 | V3 | V3b | V5 | V5b;
const ulong Path3 = 0;
const ulong Path4 = 0;
const ulong Path5 = 0;
#endif
/// <summary>Run this device</summary>
public static void RunDevice(int regulValveNo, int refFlowmtrNo, StatusP statusP)
{
TestBenchSim.regulValveNo = regulValveNo;
TestBenchSim.refFlowmtrNo = refFlowmtrNo;
double time = 1.0;
#if DN100
if (Div1sim && (SimRoute & Path1) == Path1) Mass1sim += DiffKg(Flow1sim, time);
if (Div1sim && (SimRoute & Path2) == Path2) Mass1sim += DiffKg(Flow2sim, time);
if (Div2sim && (SimRoute & Path3) == Path3) Mass2sim += DiffKg(Flow3sim, time);
if (Div2sim && (SimRoute & Path4) == Path4) Mass2sim += DiffKg(Flow4sim, time);
if (Div1sim && (SimRoute & Path5) == Path5) Mass1sim += DiffKg(Flow5sim, time);
#elif MUNICH || FUZHOU_150 || FUZHOU_300 || IZRAEL_200 || PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER
if (Div1sim && (SimRoute & Path1) == Path1)
{
double diff = DiffKg(Flow1sim, time);
Mass1sim += diff;
log.WarnFormat("Path1: Div1, Mass1+={0}kg, flow={1}m3/h, Mass1={2}kg", diff, Flow1sim, Mass1sim);
}
if (Div1sim && (SimRoute & Path2) == Path2)
{
double diff = DiffKg(Flow2sim, time);
Mass1sim += diff;
log.WarnFormat("Path2: Div1, Mass1 += {0}kg, flow={1}m3/h, Mass1={2}kg", diff, Flow1sim, Mass1sim);
}
if (Div2sim && (SimRoute & Path3) == Path3)
{
double diff = DiffKg(Flow3sim, time);
Mass2sim += diff;
log.WarnFormat("Path3: Div2, Mass2 += {0}kg, flow={1}m3/h, Mass2={2}kg", diff, Flow1sim, Mass2sim);
}
if (Div3sim && (SimRoute & Path4) == Path4)
{
double diff = DiffKg(Flow4sim, time);
Mass2sim += diff;
log.WarnFormat("Path4: Div3, Mass2 += {0}kg, flow={1}m3/h, Mass2={2}kg", diff, Flow1sim, Mass2sim);
}
if (Div3sim && (SimRoute & Path5) == Path5)
{
double diff = DiffKg(Flow5sim, time);
Mass2sim += diff;
log.WarnFormat("Path5: Div3, Mass2 += {0}kg, flow={1}m3/h, Mass2={2}kg", diff, Flow1sim, Mass2sim);
}
#elif BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || BADGER_VELKA_TRAT || TORINO_50 /// Path1,2 do WT1, Path3,4 do WT2
if (Div1sim && (SimRoute & Path1) == Path1) Mass1sim += DiffKg(Flow1sim, time);
if (Div1sim && (SimRoute & Path2) == Path2) Mass1sim += DiffKg(Flow2sim, time);
if (Div2sim && (SimRoute & Path3) == Path3) Mass2sim += DiffKg(Flow3sim, time);
if (Div2sim && (SimRoute & Path4) == Path4) Mass2sim += DiffKg(Flow4sim, time);
#elif CEVAK_40 || MURES_40 || FUZHOU_50 || SLM_50 || MALTA_WSD25 /// Path1,2,3 do WT1
if (Div1sim && (SimRoute & Path1) == Path1) Mass1sim += DiffKg(Flow1sim, time);
if (Div1sim && (SimRoute & Path2) == Path2) Mass1sim += DiffKg(Flow2sim, time);
if (Div1sim && (SimRoute & Path3) == Path3) Mass1sim += DiffKg(Flow3sim, time);
#elif TORUN_50 || TURA_IPERL || TURA_IPERL_NEW /// Path1,2,3,4,5 do WT1, Path6 do WT2
if (Div1sim && (SimRoute & Path1) == Path1) Mass1sim += DiffKg(Flow1sim, time);
if (Div1sim && (SimRoute & Path2) == Path2) Mass1sim += DiffKg(Flow2sim, time);
if (Div1sim && (SimRoute & Path3) == Path3) Mass1sim += DiffKg(Flow3sim, time);
if (Div2sim && (SimRoute & Path4) == Path4) Mass1sim += DiffKg(Flow4sim, time);
if (Div2sim && (SimRoute & Path5) == Path5) Mass1sim += DiffKg(Flow5sim, time);
if (Div3sim && (SimRoute & Path6) == Path6) Mass2sim += DiffKg(Flow4sim, time);
#elif TURA_SPECIAL
if (Div1sim && (SimRoute & Path1) == Path1) Mass1sim += DiffKg(Flow1sim, time);
if (Div1sim && (SimRoute & Path2) == Path2) Mass1sim += DiffKg(Flow2sim, time);
if (Div1sim && (SimRoute & Path3) == Path3) Mass1sim += DiffKg(Flow3sim, time);
if (Div2sim && (SimRoute & Path4) == Path4) Mass1sim += DiffKg(Flow4sim, time);
if (Div2sim && (SimRoute & Path5) == Path5) Mass1sim += DiffKg(Flow5sim, time);
if (Div3sim && (SimRoute & Path6) == Path6) Mass2sim += DiffKg(Flow4sim, time);
#else
if (Div1sim && (SimRoute & Path1) == Path1) Mass1sim += DiffKg(Flow1sim, time);
if (Div2sim && (SimRoute & Path2) == Path2) Mass2sim += DiffKg(Flow2sim, time);
#endif
if ((SimRoute & EmptyValve1) == EmptyValve1) Mass1sim -= DiffKg(100.0, time);
if (Mass1sim < 0) Mass1sim = 0;
if ((SimRoute & EmptyValve2) == EmptyValve2) Mass2sim -= DiffKg(10.0, time);
if (Mass2sim < 0) Mass2sim = 0;
}
/// <summary>
/// Calculate the mass change from the flow and the time period
/// </summary>
/// <param name="flowM3H">Flow in [m3/h]</param>
/// <param name="timeSec">Time period in [s]</param>
/// <returns>Mass change in [kg]</returns>
static double DiffKg(double flowM3H, double timeSec)
{
return timeSec * flowM3H / 3.6;
}
/// <summary>
/// Get the simulated mass. Use the balance range to distinguish the balances.
/// </summary>
/// <param name="balanceNr">0-based balance number</param>
/// <returns>Simulated mass reading [kg]</returns>
public static double GetSimMass(int balanceNr)
{
if (balanceNr == 0) return Mass1sim;
else if (balanceNr == 1) return Mass2sim;
else return 0;
}
/// <summary>
/// Set the simulated diverter. Use the nominal flow to distinguish the path.
/// </summary>
/// <param name="toTank">Diverter state: true = to the tank</param>
/// <param name="nominalFlow">Nominal flow of the path [m3/h]</param>
public static void SetSimDiverter(bool toTank)
{
#if DN100
switch (regulValveNo)
{
case 1:
case 2:
case 5:
Div1sim = toTank;
break;
case 3:
case 4:
Div2sim = toTank;
break;
default:
if (toTank) { throw (new Exception()); }
break;
}
#elif MUNICH || FUZHOU_150 || FUZHOU_300 || IZRAEL_200 || PUCHONG_200 || GENESIS || BERLIN || GELSENWASSER
switch (regulValveNo)
{
case 1:
case 2:
Div1sim = toTank;
break;
case 3:
Div2sim = toTank;
break;
case 4:
case 5:
Div3sim = toTank;
break;
default:
//if (toTank) { throw (new Exception()); }
break;
}
#elif BADGER_MALA_TRAT || BADGER_STREDNA_TRAT || TORINO_50
switch (regulValveNo)
{
case 1:
case 2:
Div1sim = toTank;
break;
case 3:
case 4:
Div2sim = toTank;
break;
default:
if (toTank) { throw (new Exception()); }
break;
}
#elif BADGER_VELKA_TRAT
switch (regulValveNo)
{
case 7:
case 2:
Div1sim = toTank;
break;
case 3:
Div2sim = toTank;
break;
default:
if (toTank) { throw (new Exception()); }
break;
}
#elif CEVAK_40 || MURES_40 || FUZHOU_50 || SLM_50 || MALTA_WSD25
switch (regulValveNo)
{
case 1:
case 2:
case 3:
Div1sim = toTank;
break;
default:
if (toTank) { /* throw (new Exception()); */ }
break;
}
#elif TORUN_50 || TURA_IPERL || TURA_IPERL_NEW
switch (regulValveNo)
{
case 1:
case 2:
case 3:
Div1sim = toTank;
break;
case 4:
case 5:
Div2sim = toTank;
break;
case 6:
Div3sim = toTank;
break;
default:
if (toTank) { throw (new Exception()); }
break;
}
#elif TURA_SPECIAL
switch (regulValveNo)
{
case 1:
case 2:
case 3:
Div1sim = toTank;
break;
case 4:
case 5:
Div2sim = toTank;
break;
case 6:
Div3sim = toTank;
break;
default:
if (toTank) { throw (new Exception()); }
break;
}
#else
switch (regulValveNo)
{
case 1: Div1sim = toTank; break;
case 2: Div2sim = toTank; break;
default:
if (toTank) { throw (new Exception()); }
break;
}
#endif
return;
}
/// <summary>
/// Get the simulated flow in [m3/h].
/// </summary>
/// <returns>Simulated flow [m3/h]</returns>
public static double GetSimFlow()
{
switch (refFlowmtrNo)
{
case 1: return ((SimRoute & Path1) == Path1) ? Flow1sim : 0;
case 2: return ((SimRoute & Path2) == Path2) ? Flow2sim : 0;
case 3: return ((SimRoute & Path3) == Path3) ? Flow3sim : 0;
case 4: return (((SimRoute & Path4) == Path4) ? Flow4sim : 0) +
(((SimRoute & Path5) == Path5) ? Flow5sim : 0);
default: return 0;
}
}
/// <summary>
/// Update simulated flow. Use the nominal flow to distinguish the path.
/// </summary>
/// <param name="targetFlow">Target flow [m3/h]</param>
/// <param name="nominalFlow">Nominal flow of the path [m3/h]</param>
public static void UpdateSimFlow(float targetFlow)
{
float A = 0.7f; /// higher value -> slower regulation
float B = 1.0f - A;
switch (regulValveNo)
{
case 1: Flow1sim = A * Flow1sim + B * targetFlow; break;
case 2: Flow2sim = A * Flow2sim + B * targetFlow; break;
case 3: Flow3sim = A * Flow3sim + B * targetFlow; break;
case 4: Flow4sim = A * Flow4sim + B * targetFlow; break;
case 5: Flow5sim = A * Flow5sim + B * targetFlow; break;
case 6: Flow6sim = A * Flow6sim + B * targetFlow; break;
default: break;
}
}
}
}
+182
View File
@@ -0,0 +1,182 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using Config.Entities;
using Dirichlet.Numerics;
using TBF.BenchControl.Generic;
using TBF.BenchControl.GenericDevices;
namespace TBF.BenchControl.ControlBoard.New
{
public class CBoard : ComponentBase, IControlBoard
{
private static readonly ILog log = LogManager.GetLogger(typeof(CBoard));
public override string ToString() { return string.Format("CBoard({0})", Cfg.ToString(1)); }
readonly CBoardCfg cBoardCfg;
public OutputPath Devices
{
set { devices = value; }
get { return devices; }
}
OutputPath devices;
public CBoard()
{
}
/// <summary>
/// Constructor
/// </summary>
public CBoard(Generic.IComponentCfg cfg)
: base(cfg)
{
cBoardCfg = cfg as CBoardCfg;
}
public void InitializeComponent(object thirdPartyComponent, UInt128 valvesToInvert, double[] tankCapacities)
{
this.valvesToInvert = valvesToInvert;
this.benchModelRoute = valvesToInvert;
}
public void Initialize() { }
public void RunDeviceBefore() { }
public void RunDeviceAfter() { }
public void StopDevice() { }
public void StopDevice2() { }
///
/// IControlBoard interface
///
/// <summary>
/// Route: 128-bit word representing the current state of all valves and pumps
/// </summary>
public UInt128 Route { get { return benchModelRoute; } }
UInt128 benchModelRoute;
UInt128 valvesToInvert; /// Route = valesToInvert ^ RequiredRouteWithoutInvertedVlaves
readonly UInt128 routeMask; /// This mask masks out virtual valves, routeMask is set in the constructor
public UInt64 DigitalInputs { get { return 0; } }
public double RefFrequency { get { return 0; } }
public double RefFlow { get { return 0; } }
///
/// Last test result
///
public int RefPulses { get { return 0; } }
public double TestTime { get { return 1.0; } }
public int RefPulsesWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? 0 : 0; }
public int PulsesWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? 0 : 0; }
public double TestTimeWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? 1.0 : 1.0; }
public void UpdateUI(string sParam, int iParam, double value) { }
public void ClearProcessValues() { }
public UInt128 SetValves(UInt128 valvesToOpen, UInt128 valvesToClose, IList<BuiltIn.ValveEx.Valve> extendedValves)
{
return routeMask;
}
/// <summary>
/// Open one valve and/or close one valve.
/// Events: ValvesSet
/// </summary>
/// <param name="valveOpen">Valve to be opened</param>
/// <param name="valveClose">Valve to be closed</param>
/// <returns>Created operation</returns>
public IOperation SetValvesOp(IValve valveOpen, IValve valveClose)
{
return new BuiltIn.SetValvesOp(this, valveOpen, valveClose);
}
/// <summary>
/// Open and/or close multiple valves.
/// Events: ValvesSet
/// </summary>
/// <param name="valvesOpen">A list of valves to be opened</param>
/// <param name="valvesClose">A list of valves to be closed</param>
/// <returns>Created operation</returns>
public IOperation SetValvesOp(IList<IValve> valvesOpen, IList<IValve> valvesClose)
{
return new BuiltIn.SetValvesOp(this, valvesOpen, valvesClose);
}
/// <summary>
/// Open start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation OpenStartValveOp()
{
return new BuiltIn.SetValvesOp(this, true, devices);
}
/// <summary>
/// Close start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation CloseStartValveOp()
{
return new BuiltIn.SetValvesOp(this, false, devices);
}
public float ValveOpenCloseTime { get { return 0.1f; } }
public IOperation SetFlowOp(double reqrdFlowLo, double reqrdFlowHi, TBF.Boxes.DoubleBox measuredFlow, int timeout)
{
return null; /// TODO
}
public IOperation StopFlowRegulationOp()
{
return null; /// TODO
}
/// <summary>
/// Start a test, Events: MeasurementStarted
/// </summary>
/// <param name="iParam">Int32 parameter 1</param>
/// <param name="dParam1">Double parameter 1 (alternative lower flow limit)</param>
/// <param name="dParam2">Double parameter 2 (alternative upper flow limit)</param>
/// <returns>Operation</returns>
public IOperation StartTestOp(Test test, int iParam = 0, double dParam1 = 0, double dParam2 = 0)
{
return null; /// TODO
}
public IOperation QueryMeasurementEndOp()
{
return null; /// TODO
}
public IOperation StopMeasurementOp()
{
return null; /// TODO
}
public IOperation ReadDiverterTransitionOp(bool isStart, Sequences.Statistics plotter, int batchNr, string testName, int repetition)
{
return null; /// TODO
}
public IOperation StopPreviousOp()
{
return null;
}
}
}
@@ -0,0 +1,46 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.New
{
public class CBoardCfg : ComponentCfgBase, IComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(CBoardCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl() { return new CBoardCfgCtrl(); }
///
/// Serialized parameters
///
public int ComPortNr;
public int VirtualValvesRangeLo;
public int VirtualValvesRangeHi;
/// Private parameterless constructor invoked by all other (public) constructors
CBoardCfg()
{
Name = "CB";
ParentName = string.Empty;
ComPortNr = 1;
VirtualValvesRangeLo = 50;
VirtualValvesRangeHi = 59;
}
public CBoardCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("Name={0}, Com{1}, VVRangeLo={2}, VVRangeHi={3}", Name, ComPortNr, VirtualValvesRangeLo, VirtualValvesRangeHi);
}
}
}
@@ -0,0 +1,155 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
namespace TBF.BenchControl.ControlBoard.New
{
partial class CBoardCfgCtrl
{
/// <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.comPortNrLabel = new System.Windows.Forms.Label();
this.comPortNrTextBox = new System.Windows.Forms.TextBox();
this.classNameLabel = new System.Windows.Forms.Label();
this.nameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeLoTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeLoLabel = new System.Windows.Forms.Label();
this.vValveRangeHiTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeHiLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// comPortNrLabel
//
this.comPortNrLabel.AutoSize = true;
this.comPortNrLabel.Location = new System.Drawing.Point(42, 85);
this.comPortNrLabel.Name = "comPortNrLabel";
this.comPortNrLabel.Size = new System.Drawing.Size(95, 13);
this.comPortNrLabel.TabIndex = 0;
this.comPortNrLabel.Text = "Serial port number:";
//
// comPortNrTextBox
//
this.comPortNrTextBox.Enabled = false;
this.comPortNrTextBox.Location = new System.Drawing.Point(166, 82);
this.comPortNrTextBox.Name = "comPortNrTextBox";
this.comPortNrTextBox.Size = new System.Drawing.Size(80, 20);
this.comPortNrTextBox.TabIndex = 1;
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(163, 26);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 2;
this.classNameLabel.Text = "ComonentName";
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(42, 60);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 3;
this.nameLabel.Text = "Name";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(166, 57);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(80, 20);
this.nameTextBox.TabIndex = 4;
//
// vValveRangeLoTextBox
//
this.vValveRangeLoTextBox.Enabled = false;
this.vValveRangeLoTextBox.Location = new System.Drawing.Point(166, 107);
this.vValveRangeLoTextBox.Name = "vValveRangeLoTextBox";
this.vValveRangeLoTextBox.Size = new System.Drawing.Size(80, 20);
this.vValveRangeLoTextBox.TabIndex = 6;
//
// vValveRangeLoLabel
//
this.vValveRangeLoLabel.AutoSize = true;
this.vValveRangeLoLabel.Location = new System.Drawing.Point(42, 110);
this.vValveRangeLoLabel.Name = "vValveRangeLoLabel";
this.vValveRangeLoLabel.Size = new System.Drawing.Size(118, 13);
this.vValveRangeLoLabel.TabIndex = 5;
this.vValveRangeLoLabel.Text = "Virtual valves range Lo:";
//
// vValveRangeHiTextBox
//
this.vValveRangeHiTextBox.Enabled = false;
this.vValveRangeHiTextBox.Location = new System.Drawing.Point(166, 132);
this.vValveRangeHiTextBox.Name = "vValveRangeHiTextBox";
this.vValveRangeHiTextBox.Size = new System.Drawing.Size(80, 20);
this.vValveRangeHiTextBox.TabIndex = 8;
//
// vValveRangeHiLabel
//
this.vValveRangeHiLabel.AutoSize = true;
this.vValveRangeHiLabel.Location = new System.Drawing.Point(42, 135);
this.vValveRangeHiLabel.Name = "vValveRangeHiLabel";
this.vValveRangeHiLabel.Size = new System.Drawing.Size(116, 13);
this.vValveRangeHiLabel.TabIndex = 7;
this.vValveRangeHiLabel.Text = "Virtual valves range Hi:";
//
// ControlBoardCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.vValveRangeHiTextBox);
this.Controls.Add(this.vValveRangeHiLabel);
this.Controls.Add(this.vValveRangeLoTextBox);
this.Controls.Add(this.vValveRangeLoLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Controls.Add(this.comPortNrTextBox);
this.Controls.Add(this.comPortNrLabel);
this.Name = "ControlBoardCfgCtrl";
this.Size = new System.Drawing.Size(300, 200);
this.Load += new System.EventHandler(this.ControlBoardCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label comPortNrLabel;
private System.Windows.Forms.TextBox comPortNrTextBox;
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.TextBox vValveRangeLoTextBox;
private System.Windows.Forms.Label vValveRangeLoLabel;
private System.Windows.Forms.TextBox vValveRangeHiTextBox;
private System.Windows.Forms.Label vValveRangeHiLabel;
}
}
@@ -0,0 +1,105 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System;
using System.Windows.Forms;
using Config.Entities;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.New
{
public partial class CBoardCfgCtrl : UserControl, IComponentCfgCtrl
{
public bool ShowMore { get { return false; } }
CBoardCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as CBoardCfg;
Redraw();
}
}
public CBoardCfgCtrl()
{
InitializeComponent();
}
private void ControlBoardCfgCtrl_Load(object sender, EventArgs e)
{
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;
comPortNrTextBox.Text = config.ComPortNr.ToString();
vValveRangeLoTextBox.Text = config.VirtualValvesRangeLo.ToString();
vValveRangeHiTextBox.Text = config.VirtualValvesRangeHi.ToString();
}
public void Unlock()
{
nameTextBox.Enabled = true;
comPortNrTextBox.Enabled = true;
vValveRangeLoTextBox.Enabled = true;
vValveRangeHiTextBox.Enabled = true;
}
/// <summary>
/// Verify current user controls for validity.
/// Called when user wants to close/save the configuration.
/// </summary>
/// <returns>See VerifyFlags</returns>
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (!int.TryParse(comPortNrTextBox.Text, out dummy) || dummy <= 0 || dummy > 999)
{
message += "Control board serial port number is invalid";
flags |= CfgUpdateFlags.Error;
}
if (!int.TryParse(vValveRangeLoTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
message += "Virtual valves range Lo is invalid";
flags |= CfgUpdateFlags.Error;
}
if (!int.TryParse(vValveRangeHiTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
message += "Virtual valves range Hi is invalid";
flags |= CfgUpdateFlags.Error;
}
return flags;
}
/// <summary>
/// Save settings to the configuration class.
/// </summary>
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
this.Name = nameTextBox.Text;
this.config.ComPortNr = int.Parse(comPortNrTextBox.Text);
this.config.VirtualValvesRangeLo = int.Parse(vValveRangeLoTextBox.Text);
this.config.VirtualValvesRangeHi = int.Parse(vValveRangeHiTextBox.Text);
return flags;
}
}
}
@@ -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,36 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.New
{
public class CBoardFactory : IComponentFactory
{
public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
/// <summary>Max. number of components of this class in the system</summary>
public int MaxCount { get { return 1; } }
public IComponent DummyComponent() { return new CBoard(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new CBoard(cfg); }
public IComponentCfg DefaultConfig() { return new CBoardCfg(this); }
public IComponentCfgCtrl CreateCfgCtrl() { return new CBoardCfgCtrl(); }
public IComponent ComponentFromCmpntCfg(IComponentCfg config, IList<Generic.IComponent> components)
{
return new CBoard((CBoardCfg)config);
}
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(CBoardCfg.Serializer, component, this);
}
public void ResetStaticProperties() { }
}
}
@@ -0,0 +1,278 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using log4net;
using Config.Entities;
using Dirichlet.Numerics;
using TBF.BenchControl.Generic;
using TBF.BenchControl.GenericDevices;
using TBF.BenchControl.Modbus;
namespace TBF.BenchControl.ControlBoard.Papouch
{
public class CBoard : ComponentBase, IControlBoard
{
private static readonly ILog log = LogManager.GetLogger(typeof(CBoard));
public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(1)); }
readonly CBoardCfg cBoardCfg;
readonly GenericDevices.IModbus modbus;
public QuidoVariant Variant { get { return cBoardCfg.Variant; } }
private ushort currentOutputs;
private DateTime lastOutputsChange;
public OutputPath Devices
{
set { devices = value; }
get { return devices; }
}
OutputPath devices;
public CBoard()
{
}
/// <summary>
/// Constructor
/// </summary>
public CBoard(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
: base(cfg)
{
cBoardCfg = cfg as CBoardCfg;
modbus = (GenericDevices.IModbus)TbfComponents.FindComponent(cfg.ParentName, components);
if (modbus == null) throw new Exception("Cannot find " + Name + " parent");
log.Debug(this.ToString());
}
public void InitializeComponent(object thirdPartyComponent, UInt128 valvesToInvert, double[] tankCapacities)
{
this.valvesToInvert = valvesToInvert;
this.benchModelRoute = valvesToInvert;
}
public void Initialize()
{
}
public void RunDeviceBefore()
{
if (cBoardCfg.DebugLevel == Config.Entities.DebugMode.Simulate) return;
if (modbus.ReceivedTelegrams[cBoardCfg.ModbusAddress].Count > 0)
{
byte[] telegram = modbus.ReceivedTelegrams[cBoardCfg.ModbusAddress].Dequeue();
if ((telegram.Length >= 8) && (telegram[1] == 0x11) && (telegram[2] == telegram.Length - 5))
{
int msgLen = telegram.Length - 7;
byte modbusAddress = telegram[3];
string msg = Encoding.ASCII.GetString(telegram, 5, msgLen);
string s = string.Format("QuidoRS detected: Address={0}, Message={1}", modbusAddress, msg);
Debug.WriteLine(s);
log.Warn(s);
}
}
}
public void RunDeviceAfter() { }
public void StopDevice() { SendOutputs(0); }
public void StopDevice2() { }
/// <summary>
/// Set outputs of QuidoRS board
/// </summary>
/// <param name="outputs">output value</param>
public void SetOutputs(ushort outputs)
{
lock (this)
{
SendOutputs(currentOutputs);
}
}
/// <summary>
/// Set bit(s) specified by a bitMask. Leave other bits unaffeced.
/// </summary>
/// <param name="bitMask">Specifies bit(s) to set</param>
/// <param name="outputs">output value</param>
public void SetBit(ushort bitMask)
{
lock (this)
{
SendOutputs(currentOutputs);
}
}
/// <summary>
/// Reset bit(s) specified by a bitMask. Leave other bits unaffeced.
/// </summary>
/// <param name="bitMask">Specifies bit(s) to reset</param>
/// <param name="outputs">output value</param>
public void ResetBit(ushort bitMask)
{
lock (this)
{
SendOutputs(currentOutputs);
}
}
private void SendOutputs(ushort outputs)
{
byte addr = cBoardCfg.ModbusAddress;
byte cmd = (byte)Function.ForceMultipleCoils;
byte dataLo = (byte)(outputs & 0xFF);
byte dataHi = (byte)(outputs >> 8);
if (cBoardCfg.Variant == QuidoVariant.QuidoRS_2_16)
{
modbus.SendMessage(new byte[] { addr, cmd, 0, 0, 0, 16, 2, dataLo, dataHi, 0, 0 });
}
else if (cBoardCfg.Variant == QuidoVariant.QuidoRS_4_4)
{
modbus.SendMessage(new byte[] { addr, cmd, 0, 0, 0, 4, 1, dataLo, 0, 0 });
}
else if (cBoardCfg.Variant == QuidoVariant.QuidoRS_8_8)
{
modbus.SendMessage(new byte[] { addr, cmd, 0, 0, 0, 8, 1, dataLo, 0, 0 });
}
lastOutputsChange = DateTime.Now;
}
///
/// IControlBoard interface
///
/// <summary>
/// Route: 128-bit word representing the current state of all valves and pumps
/// </summary>
public UInt128 Route { get { return benchModelRoute; } }
UInt128 benchModelRoute;
UInt128 valvesToInvert; /// Route = valesToInvert ^ RequiredRouteWithoutInvertedVlaves
readonly UInt128 routeMask; /// This mask masks out virtual valves, routeMask is set in the constructor
public UInt64 DigitalInputs { get { return 0; } }
public double RefFrequency { get { return 0; } }
public double RefFlow { get { return 0; } }
///
/// Last test result
///
public int RefPulses { get { return 0; } }
public double TestTime { get { return 1.0; } }
public int RefPulsesWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? 0 : 0; }
public int PulsesWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? 0 : 0; }
public double TestTimeWM(int wmNr1) { return (wmNr1 > 0 && wmNr1 <= Config.Data.WMsCount) ? 1.0 : 1.0; }
public void UpdateUI(string sParam, int iParam, double value) { }
public void ClearProcessValues() { }
public UInt128 SetValves(UInt128 valvesToOpen, UInt128 valvesToClose, IList<BuiltIn.ValveEx.Valve> extendedValves)
{
return routeMask;
}
/// <summary>
/// Open one valve and/or close one valve.
/// Events: ValvesSet
/// </summary>
/// <param name="valveOpen">Valve to be opened</param>
/// <param name="valveClose">Valve to be closed</param>
/// <returns>Created operation</returns>
public IOperation SetValvesOp(IValve valveOpen, IValve valveClose)
{
return new BuiltIn.SetValvesOp(this, valveOpen, valveClose);
}
/// <summary>
/// Open and/or close multiple valves.
/// Events: ValvesSet
/// </summary>
/// <param name="valvesOpen">A list of valves to be opened</param>
/// <param name="valvesClose">A list of valves to be closed</param>
/// <returns>Created operation</returns>
public IOperation SetValvesOp(IList<IValve> valvesOpen, IList<IValve> valvesClose)
{
return new BuiltIn.SetValvesOp(this, valvesOpen, valvesClose);
}
/// <summary>
/// Open start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation OpenStartValveOp()
{
return new BuiltIn.SetValvesOp(this, true, devices);
}
/// <summary>
/// Close start/stop valve.
/// Events: ValvesSet, ValvesBusy, Error
/// </summary>
/// <returns>Created operation</returns>
public IOperation CloseStartValveOp()
{
return new BuiltIn.SetValvesOp(this, false, devices);
}
public float ValveOpenCloseTime { get { return 0.1f; } }
public IOperation SetFlowOp(double reqrdFlowLo, double reqrdFlowHi, TBF.Boxes.DoubleBox measuredFlow, int timeout)
{
return null; /// TODO
}
public IOperation StopFlowRegulationOp()
{
return null; /// TODO
}
/// <summary>
/// Start a test, Events: MeasurementStarted
/// </summary>
/// <param name="iParam">Int32 parameter 1</param>
/// <param name="dParam1">Double parameter 1 (alternative lower flow limit)</param>
/// <param name="dParam2">Double parameter 2 (alternative upper flow limit)</param>
/// <returns>Operation</returns>
public IOperation StartTestOp(Test test, int iParam = 0, double dParam1 = 0, double dParam2 = 0)
{
return null; /// TODO
}
public IOperation QueryMeasurementEndOp()
{
return null; /// TODO
}
public IOperation StopMeasurementOp()
{
return null; /// TODO
}
public IOperation ReadDiverterTransitionOp(bool isStart, Sequences.Statistics plotter, int batchNr, string testName, int repetition)
{
return null;
}
public IOperation StopPreviousOp()
{
return null;
}
}
}
@@ -0,0 +1,62 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System.IO;
using System.Collections.Generic;
using System.Xml.Serialization;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.Papouch
{
public enum QuidoVariant
{
QuidoRS_2_16,
QuidoRS_4_4,
QuidoRS_8_8,
Count
}
public class CBoardCfg : ComponentCfgBase, Generic.IChildComponentCfg
{
public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(CBoardCfg) })[0];
public override XmlSerializer GetSerializer() { return Serializer; }
public IComponentCfgCtrl GetControl() { return new CBoardCfgCtrl(); }
///
/// Serialized parameters
///
public byte ModbusAddress; /// 1..254
public QuidoVariant Variant; /// QuidoRS_2_16, QuidoRS_4_4 or QuidoRS_8_8
public int VirtualValvesRangeLo;
public int VirtualValvesRangeHi;
/// Private parameterless constructor invoked by all other (public) constructors
CBoardCfg()
{
Name = "CB";
ParentName = "Modbus";
ModbusAddress = 1;
Variant = QuidoVariant.QuidoRS_2_16;
VirtualValvesRangeLo = 50;
VirtualValvesRangeHi = 59;
}
public CBoardCfg(IComponentFactory factory)
: this()
{
this.Factory = factory;
}
public string ToString(int i)
{
return string.Format("name={0}({1}) address={2} variant={3} vvLo={4} vvHi={5}",
Name,
(string.IsNullOrEmpty(ParentName) ? "-" : ParentName),
ModbusAddress,
Variant,
VirtualValvesRangeLo,
VirtualValvesRangeHi);
}
}
}
@@ -0,0 +1,203 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
namespace TBF.BenchControl.ControlBoard.Papouch
{
partial class CBoardCfgCtrl
{
/// <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.classNameLabel = new System.Windows.Forms.Label();
this.nameLabel = new System.Windows.Forms.Label();
this.nameTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeLoTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeLoLabel = new System.Windows.Forms.Label();
this.vValveRangeHiTextBox = new System.Windows.Forms.TextBox();
this.vValveRangeHiLabel = new System.Windows.Forms.Label();
this.parentNameComboBox = new System.Windows.Forms.ComboBox();
this.modbusAddressTextBox = new System.Windows.Forms.TextBox();
this.modbusAddressLabel = new System.Windows.Forms.Label();
this.parentNameLabel = new System.Windows.Forms.Label();
this.variantComboBox = new System.Windows.Forms.ComboBox();
this.variantLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// classNameLabel
//
this.classNameLabel.AutoSize = true;
this.classNameLabel.Location = new System.Drawing.Point(175, 26);
this.classNameLabel.Name = "classNameLabel";
this.classNameLabel.Size = new System.Drawing.Size(83, 13);
this.classNameLabel.TabIndex = 2;
this.classNameLabel.Text = "ComonentName";
//
// nameLabel
//
this.nameLabel.AutoSize = true;
this.nameLabel.Location = new System.Drawing.Point(42, 60);
this.nameLabel.Name = "nameLabel";
this.nameLabel.Size = new System.Drawing.Size(35, 13);
this.nameLabel.TabIndex = 3;
this.nameLabel.Text = "Name";
//
// nameTextBox
//
this.nameTextBox.Enabled = false;
this.nameTextBox.Location = new System.Drawing.Point(178, 57);
this.nameTextBox.Name = "nameTextBox";
this.nameTextBox.Size = new System.Drawing.Size(130, 20);
this.nameTextBox.TabIndex = 4;
//
// vValveRangeLoTextBox
//
this.vValveRangeLoTextBox.Enabled = false;
this.vValveRangeLoTextBox.Location = new System.Drawing.Point(178, 161);
this.vValveRangeLoTextBox.Name = "vValveRangeLoTextBox";
this.vValveRangeLoTextBox.Size = new System.Drawing.Size(46, 20);
this.vValveRangeLoTextBox.TabIndex = 6;
//
// vValveRangeLoLabel
//
this.vValveRangeLoLabel.AutoSize = true;
this.vValveRangeLoLabel.Location = new System.Drawing.Point(42, 164);
this.vValveRangeLoLabel.Name = "vValveRangeLoLabel";
this.vValveRangeLoLabel.Size = new System.Drawing.Size(118, 13);
this.vValveRangeLoLabel.TabIndex = 5;
this.vValveRangeLoLabel.Text = "Virtual valves range Lo:";
//
// vValveRangeHiTextBox
//
this.vValveRangeHiTextBox.Enabled = false;
this.vValveRangeHiTextBox.Location = new System.Drawing.Point(178, 186);
this.vValveRangeHiTextBox.Name = "vValveRangeHiTextBox";
this.vValveRangeHiTextBox.Size = new System.Drawing.Size(46, 20);
this.vValveRangeHiTextBox.TabIndex = 8;
//
// vValveRangeHiLabel
//
this.vValveRangeHiLabel.AutoSize = true;
this.vValveRangeHiLabel.Location = new System.Drawing.Point(42, 189);
this.vValveRangeHiLabel.Name = "vValveRangeHiLabel";
this.vValveRangeHiLabel.Size = new System.Drawing.Size(116, 13);
this.vValveRangeHiLabel.TabIndex = 7;
this.vValveRangeHiLabel.Text = "Virtual valves range Hi:";
//
// parentNameComboBox
//
this.parentNameComboBox.Enabled = false;
this.parentNameComboBox.FormattingEnabled = true;
this.parentNameComboBox.Location = new System.Drawing.Point(178, 83);
this.parentNameComboBox.Name = "parentNameComboBox";
this.parentNameComboBox.Size = new System.Drawing.Size(130, 21);
this.parentNameComboBox.TabIndex = 10;
//
// modbusAddressTextBox
//
this.modbusAddressTextBox.Enabled = false;
this.modbusAddressTextBox.Location = new System.Drawing.Point(178, 136);
this.modbusAddressTextBox.Name = "modbusAddressTextBox";
this.modbusAddressTextBox.Size = new System.Drawing.Size(46, 20);
this.modbusAddressTextBox.TabIndex = 12;
//
// modbusAddressLabel
//
this.modbusAddressLabel.AutoSize = true;
this.modbusAddressLabel.Location = new System.Drawing.Point(42, 140);
this.modbusAddressLabel.Name = "modbusAddressLabel";
this.modbusAddressLabel.Size = new System.Drawing.Size(86, 13);
this.modbusAddressLabel.TabIndex = 11;
this.modbusAddressLabel.Text = "Modbus Address";
//
// parentNameLabel
//
this.parentNameLabel.AutoSize = true;
this.parentNameLabel.Location = new System.Drawing.Point(42, 86);
this.parentNameLabel.Name = "parentNameLabel";
this.parentNameLabel.Size = new System.Drawing.Size(69, 13);
this.parentNameLabel.TabIndex = 9;
this.parentNameLabel.Text = "Parent Name";
//
// variantComboBox
//
this.variantComboBox.Enabled = false;
this.variantComboBox.FormattingEnabled = true;
this.variantComboBox.Location = new System.Drawing.Point(178, 110);
this.variantComboBox.Name = "variantComboBox";
this.variantComboBox.Size = new System.Drawing.Size(130, 21);
this.variantComboBox.TabIndex = 14;
//
// variantLabel
//
this.variantLabel.AutoSize = true;
this.variantLabel.Location = new System.Drawing.Point(42, 113);
this.variantLabel.Name = "variantLabel";
this.variantLabel.Size = new System.Drawing.Size(40, 13);
this.variantLabel.TabIndex = 13;
this.variantLabel.Text = "Variant";
//
// CBoardCfgCtrl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.variantComboBox);
this.Controls.Add(this.variantLabel);
this.Controls.Add(this.parentNameComboBox);
this.Controls.Add(this.modbusAddressTextBox);
this.Controls.Add(this.modbusAddressLabel);
this.Controls.Add(this.parentNameLabel);
this.Controls.Add(this.vValveRangeHiTextBox);
this.Controls.Add(this.vValveRangeHiLabel);
this.Controls.Add(this.vValveRangeLoTextBox);
this.Controls.Add(this.vValveRangeLoLabel);
this.Controls.Add(this.nameTextBox);
this.Controls.Add(this.nameLabel);
this.Controls.Add(this.classNameLabel);
this.Name = "CBoardCfgCtrl";
this.Size = new System.Drawing.Size(400, 300);
this.Load += new System.EventHandler(this.ControlBoardCfgCtrl_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label classNameLabel;
private System.Windows.Forms.Label nameLabel;
private System.Windows.Forms.TextBox nameTextBox;
private System.Windows.Forms.TextBox vValveRangeLoTextBox;
private System.Windows.Forms.Label vValveRangeLoLabel;
private System.Windows.Forms.TextBox vValveRangeHiTextBox;
private System.Windows.Forms.Label vValveRangeHiLabel;
private System.Windows.Forms.ComboBox parentNameComboBox;
private System.Windows.Forms.TextBox modbusAddressTextBox;
private System.Windows.Forms.Label modbusAddressLabel;
private System.Windows.Forms.Label parentNameLabel;
private System.Windows.Forms.ComboBox variantComboBox;
private System.Windows.Forms.Label variantLabel;
}
}
@@ -0,0 +1,142 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System;
using System.Windows.Forms;
using Config.Entities;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.Papouch
{
public partial class CBoardCfgCtrl : UserControl, IComponentCfgCtrl
{
ComponentParametersDlg parent;
public bool ShowMore { get { return false; } }
CBoardCfg config;
public IComponentCfg Config
{
get { return config as IComponentCfg; }
set
{
config = value as CBoardCfg;
Redraw();
}
}
public CBoardCfgCtrl()
{
InitializeComponent();
}
private void ControlBoardCfgCtrl_Load(object sender, EventArgs e)
{
parent = ParentForm as ComponentParametersDlg;
if (parent == null) return;
if (parent.TbfComponents != null)
{
foreach (var cmpnt in parent.TbfComponents)
{
if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is Modbus.Common.Factory)
{
parentNameComboBox.Items.Add(cmpnt.Name);
}
}
}
for (QuidoVariant v = 0; v < QuidoVariant.Count; v++) variantComboBox.Items.Add(v.ToString());
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;
parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName;
variantComboBox.Text = config.Variant.ToString();
modbusAddressTextBox.Text = config.ModbusAddress.ToString();
vValveRangeLoTextBox.Text = config.VirtualValvesRangeLo.ToString();
vValveRangeHiTextBox.Text = config.VirtualValvesRangeHi.ToString();
}
public void Unlock()
{
nameTextBox.Enabled = true;
parentNameComboBox.Enabled = true;
variantComboBox.Enabled = true;
modbusAddressTextBox.Enabled = true;
vValveRangeLoTextBox.Enabled = true;
vValveRangeHiTextBox.Enabled = true;
}
/// <summary>
/// Verify current user controls for validity.
/// Called when user wants to close/save the configuration.
/// </summary>
/// <returns>See VerifyFlags</returns>
public CfgUpdateFlags VerifyCfg(ref string message)
{
CfgUpdateFlags flags = CfgUpdateFlags.None;
int dummy;
if (!parentNameComboBox.Items.Contains(parentNameComboBox.Text))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Invalid 'Parent Name'";
}
if (!variantComboBox.Items.Contains(variantComboBox.Text))
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "Invalid 'Variant'";
}
if (!int.TryParse(modbusAddressTextBox.Text, out dummy) || dummy < 0 || dummy > 255)
{
flags |= CfgUpdateFlags.Error;
message += Environment.NewLine + "'Modbus Address' should be between 0 and 255";
}
if (!int.TryParse(vValveRangeLoTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
message += "Virtual valves range Lo is invalid";
flags |= CfgUpdateFlags.Error;
}
if (!int.TryParse(vValveRangeHiTextBox.Text, out dummy) || dummy < 0 || dummy > 63)
{
message += "Virtual valves range Hi is invalid";
flags |= CfgUpdateFlags.Error;
}
return flags;
}
/// <summary>
/// Save settings to the configuration class.
/// </summary>
public CfgUpdateFlags UpdateCfg()
{
CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd;
if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed
this.Name = nameTextBox.Text;
config.ParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text;
for (QuidoVariant v = 0; v < QuidoVariant.Count; v++)
{
if (variantComboBox.Text == v.ToString()) { config.Variant = v; break; }
}
config.ModbusAddress = (byte)int.Parse(modbusAddressTextBox.Text);
config.VirtualValvesRangeLo = int.Parse(vValveRangeLoTextBox.Text);
config.VirtualValvesRangeHi = int.Parse(vValveRangeHiTextBox.Text);
return flags;
}
}
}
@@ -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,29 @@
///
/// Copyright (c) 2020 Sensus Slovensko a.s.
///
using System.Collections.Generic;
using TBF.BenchControl.Generic;
namespace TBF.BenchControl.ControlBoard.Papouch
{
public class CBoardFactory : IComponentFactory
{
public string ClassName { get { return this.GetType().Namespace.Substring(17); } }
public void ResetStaticProperties() { CBoard.ResetStaticProperties(); }
/// <summary>Max. number of components of this class in the system</summary>
public int MaxCount { get { return 1; } }
public IComponent DummyComponent() { return new CBoard(); }
public IComponent GetComponent(IComponentCfg cfg, IList<IComponent> components) { return new CBoard(cfg, components); }
public IComponentCfg DefaultConfig() { return new CBoardCfg(this); }
public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component)
{
return ComponentCfgBase.CreateFromDbEntity(CBoardCfg.Serializer, component, this);
}
}
}