384 lines
13 KiB
C#
384 lines
13 KiB
C#
///
|
|
/// Copyright (c) 2013-2015 Sensus Metering Systems
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using Dirichlet.Numerics;
|
|
using log4net;
|
|
|
|
namespace TBF.BenchControl.Elde
|
|
{
|
|
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; } }
|
|
|
|
float referenceFreq = 650.0f;
|
|
public float ReferenceFreq { get { return referenceFreq; } }
|
|
|
|
int[] etPulses = new int[Config.Data.WMsCount + 1];
|
|
public int EtPulses(int wmNr1and0)
|
|
{
|
|
return etPulses[wmNr1and0];
|
|
}
|
|
|
|
float[] errFactor = new float[Config.Data.WMsCount + 1]; /// Internal
|
|
float[] wMeterPulsesF = new float[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 { get { 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 = 0;
|
|
public float TTime { get { return tTime; } }
|
|
|
|
public uint FmState { get { return 0; } }
|
|
public uint BeginState(int wmNr0) { return 0; }
|
|
|
|
float referenceFlow;
|
|
public float ReferenceFlow { get { return referenceFlow; } }
|
|
|
|
public float RValvePosition(int rvNr1) { return 0; }
|
|
public float DivSamples(int ms) { 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;
|
|
|
|
|
|
uint[] cyclePar;
|
|
uint[] wMeterCont;
|
|
|
|
///
|
|
/// Private values set by SendCommand()
|
|
///
|
|
Command cmd;
|
|
int refFlowmtrNr;
|
|
UInt128 simRoute;
|
|
int totalRefPulses;
|
|
int massRefPulses;
|
|
TestMethods testMethods;
|
|
float filterConstant;
|
|
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)
|
|
{
|
|
log.Info("SendCalibData(...)");
|
|
|
|
this.regValveCalib = rvCalib;
|
|
this.meretRS485Address = meretA;
|
|
this.tempCalibData = tempCalib;
|
|
this.etCalib = etCalib;
|
|
this.diverterEdge = divEdge;
|
|
this.balanceRange = balanceRange;
|
|
}
|
|
|
|
/// <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">Route: 64-bit installation specific number</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(Command cmd, int refFlowmtrNr, UInt128 route,
|
|
int totalRefPulses, int massRefPulses, TestMethods testMethods,
|
|
float filterConstant, float[] fmFreq, int regConst, int shortImp, StopDevs stopDevs)
|
|
{
|
|
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})",
|
|
cmd, 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, filterConstant, /// 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 = cmd;
|
|
this.refFlowmtrNr = refFlowmtrNr;
|
|
|
|
#if FUZHOU300
|
|
///
|
|
/// 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 FUZHOU300 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.ShowRoute(route));
|
|
#endif
|
|
TestBenchSim.SimRoute = this.simRoute = route;
|
|
this.totalRefPulses = totalRefPulses;
|
|
this.massRefPulses = massRefPulses;
|
|
this.testMethods = testMethods;
|
|
this.filterConstant = filterConstant;
|
|
this.FMFreq = fmFreq;
|
|
this.regConst = regConst;
|
|
this.shortImp = shortImp;
|
|
this.stopDevs = stopDevs;
|
|
|
|
#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.0f / Qnom;
|
|
if (referenceFreq < 0) referenceFreq = 0;
|
|
if (referenceFreq > 2500) referenceFreq = 2500;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
referenceFlow = referenceFreq / 2000;
|
|
|
|
bool stopTest = false;
|
|
if (cmd == Command.Start)
|
|
{
|
|
float pwFactor = 1.0f;
|
|
|
|
/// Increment reference flow meters
|
|
for (int i = 0; i <= Config.Data.WMsCount; i++)
|
|
{
|
|
etPulses[i] += (int)referenceFreq;
|
|
if (etPulses[i] > totalRefPulses)
|
|
{
|
|
pwFactor = 1.0f - (float)(etPulses[i] - totalRefPulses) / referenceFreq;
|
|
etPulses[i] = totalRefPulses;
|
|
stopTest = true;
|
|
}
|
|
}
|
|
|
|
/// Increment water meters
|
|
float flowLtrPerSec = TestBenchSim.GetSimFlow() / 1800.0f;
|
|
for (int i = 1; i <= Config.Data.WMsCount; i++)
|
|
{
|
|
float wmPulsesPerLiter = 0;
|
|
if ((BenchControl.Sequences.ProcessData.RegisterReaders[i - 1] != null) &&
|
|
(BenchControl.Sequences.ProcessData.BatchRslts.WaterMeters.Length >= i) &&
|
|
(BenchControl.Sequences.ProcessData.BatchRslts.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;
|
|
}
|
|
}
|
|
}
|
|
}
|