tbf/TestBenchFramework/BenchControl/Elde/ControlBoardDev.cs

611 lines
24 KiB
C#

///
/// Copyright (c) 2013-2017 Sensus Metering Systems
///
using System;
using System.Text;
using System.IO.Ports;
using System.Collections.Generic;
using Config.Entities;
using Dirichlet.Numerics;
using TBF.BenchControl.Generic;
using TBF.BenchControl.GenericDevices;
using log4net;
namespace TBF.BenchControl.Elde
{
public class ControlBoardDev : ComponentBase, IDevice, IValveControl
{
private static readonly ILog log = LogManager.GetLogger(typeof(ControlBoardDev));
public override string ToString() { return string.Format("ControlBoard({0})", Cfg.ToString(1)); }
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 double ReferenceFreq { get { return controlCom.ReferenceFreq; } } /// In [Hz] 0..2500, nom.=2000
public int EtPulses(int wmNr0) { return (int)controlCom.EtPulses(wmNr0); }
public UInt16 WMeterPulses(int wmNr0) { return controlCom.WMeterPulses(wmNr0); }
public int WMeterReference(int wmNr0) { return controlCom.WMeterReference(wmNr0); }
public uint RegulValveDAC { get { return controlCom.RegulValveDAC; } }
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 { get { return controlCom.DivTime; } }
public float TTime { get { return controlCom.TTime; } }
public uint FmState { get { return controlCom.FmState; } }
public uint BeginState(int wmNr0) { return controlCom.BeginState(wmNr0); }
public double ReferenceFlow { get { return controlCom.ReferenceFlow; } }
public float RValvePosition(int rvNr1) { return controlCom.RValvePosition(rvNr1); }
public float DivSamples(int ms) { return controlCom.DivSamples(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; } }
/// <summary> Extracted from the StatusP in RunDeviceBefore </summary>
public bool EmergencyStop { get { return emergencyStop; } }
bool emergencyStop;
bool previousEmergencyStop;
private int flowmeterNr4DiverterTest;
private bool scheduleDiverterToTank;
private bool scheduleDiverterToSink;
private bool scheduleReadDiverterTransition;
private bool pumpPowerChanged;
///
public void DiverterToTank(int flowmeterNr)
{
if (!scheduleDiverterToSink && !scheduleReadDiverterTransition && !emergencyStop)
{
flowmeterNr4DiverterTest = flowmeterNr;
scheduleDiverterToTank = true;
}
}
///
public void DiverterToSink(int flowmeterNr)
{
if (!scheduleDiverterToTank && !scheduleReadDiverterTransition && !emergencyStop)
{
flowmeterNr4DiverterTest = flowmeterNr;
scheduleDiverterToSink = true;
}
}
///
public void ReadDiverterTransition(int flowmeterNr)
{
if (!scheduleDiverterToTank && !scheduleDiverterToSink && !emergencyStop)
{
flowmeterNr4DiverterTest = flowmeterNr;
scheduleReadDiverterTransition = true;
}
}
/// <summary> State of valves sent out - buffered by the last SendCommand() </summary>
public UInt128 Route { get { return benchModelRoute; } }
UInt128 valvesToInvert; /// Route = valesToInvert ^ RequiredRouteWithoutInvertedVlaves
readonly UInt128 routeMask; /// This mask masks out virtual valves, routeMask is set in the constructor
///
/// Initialization data updated by constructors of children components
/// and sent to 'controlCom2panel' by SendCalibData() method.
///
public float[,] TempCalibData = new float[8, 5];
public uint[] DiverterEdge; /// Created and initialized in this.Initialize()
// Array length is the reference flowmeters count plus one
#if DN100 || FUZHOU150 || FUZHOU300 || PT200IL || PUCHONG_PT200
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 GENESIS || BERLIN || PETERSBURG_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[4];
public float[] EtCalib = new float[7] { 1, 1, 1, 1, 1, 1, 1 };
#elif MUNICH || TORINO50 || BADGER_MALA_TRAT || TORUN_PT50_2015 || CEVAK_PT40_272 || TURA_IPERL || TURA_SPECIAL || MURES_PT40 || FUZHOU_40 || SLM_50 || SLM_150 || PETERSBURG_50 || KEMPNO_50 || BAHRAIN_50
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 SLM_END || 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];
///
/// Private values sent by the last SendCommand()
///
bool sendCommandFlag;
Command cmd;
int refNr;
UInt128 benchModelRoute;
int totalRefPulses;
int massRefPulses;
TestMethods testMethods;
float filterConstant;
float[] FMFreq;
int regConst;
int shortImp;
StopDevs stopDevs;
/// <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)
{
#if DN100 || MUNICH || FUZHOU150
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 && FMFreq[index] != power)
{
FMFreq[index] = power;
pumpPowerChanged = true; /// Forces sending a command using Command.None
log.DebugFormat("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()) : "");
}
}
public void UpdateWeights()
{
for (int i = 0; i < MettlerToledo.Standard.BalanceDev.BalancesCount; i++)
{
controlCom.SetWeight(i, MettlerToledo.Standard.BalanceDev.Masses[i]);
}
for (int i = 0; i < MettlerToledo.Multi.BalanceDev.BalancesCount; i++)
{
controlCom.SetWeight(i, MettlerToledo.Multi.BalanceDev.Masses[i]);
}
}
public void SetReservoirTemp(int nr, float temp)
{
controlCom.SetReservoirTemp(nr, temp);
}
public ControlBoardDev()
{
}
/// <summary>
/// Constructor
/// </summary>
public ControlBoardDev(Generic.IComponentCfg cfg)
: base(cfg)
{
controlBoardCfg = cfg as ControlBoardCfg;
#if DN100 || MUNICH || FUZHOU150
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="ctrlBrdComponent"></param>
public void InitializeComponent(
#if DN100
ControlCom2VB.ControlCom2panel ctrlBrdComponent,
#elif MUNICH || FUZHOU150
ControlComponent3Munich.UserControl1 ctrlBrdComponent,
#elif FUZHOU300
ControlComponent3F300.UserControl1 ctrlBrdComponent,
#elif PT200IL || PUCHONG_PT200 || GENESIS || BERLIN || SLM_150 || PETERSBURG_200
ControlComponent_Izrael2014.UserControl1 ctrlBrdComponent,
#else /// all newer benches
ControlComponent_Torino2015.UserControl1 ctrlBrdComponent,
#endif
UInt128 valvesToInvert,
double[] tankCapacities)
{
this.valvesToInvert = valvesToInvert;
this.benchModelRoute = valvesToInvert;
if (controlBoardCfg.DebugLevel == DebugMode.Simulate)
{
controlCom = new ControlComSim();
}
else
{
/// 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);
}
controlCom = new ControlComWrap(ctrlBrdComponent);
}
}
/// <summary>Initialize this device</summary>
public void Initialize()
{
int divertersCount = BenchControl.Elde.Diverter.Diverter.DivertersCount;
DiverterEdge = new uint[divertersCount * 2];
for (int i = 0; i < divertersCount; i++)
{
DiverterEdge[2 * i] = (uint)BenchControl.Elde.Diverter.Diverter.DivStartingLevelPct[i];
DiverterEdge[2 * i + 1] = (uint)BenchControl.Elde.Diverter.Diverter.DivStoppingLevelPct[i];
}
controlCom.SendCalibData(RegValveCalib,
MeretRS485Address,
controlBoardCfg.ComPortNr,
TempCalibData,
EtCalib,
DiverterEdge,
BalanceRange);
log.FatalFormat("Successfully initialized device {0}", ToString());
}
/// <summary>Run this device</summary>
public void RunDeviceBefore()
{
controlCom.RunDeviceBefore();
previousEmergencyStop = emergencyStop;
emergencyStop = (controlCom.StatusP & StatusP.EmgcyStopActive) != 0;
if (emergencyStop && !previousEmergencyStop)
{
Program.MainWnd.Invoke(new ShowEmergencyStopFormDlgt(ShowEmergencyStopForm));
/// Stop the cycle - Press 'STOP' button in the BenchControlPanel
TBF.UiBridge.Bridge.Ui2Bench(TBF.UiBridge.UI2BenchCmd.Stop);
}
else if (!emergencyStop && previousEmergencyStop)
{
UiBridge.Bridge.OnEmergencyStopChanged(this,UiBridge.EmergencyStopEventArgs.State.CloseForm);
}
if (emergencyStop)
{
/// SendCommand() arguments to clear the emergency stop state
sendCommandFlag = true;
this.refNr = 0;
this.totalRefPulses = 0;
this.massRefPulses = 0;
this.cmd = Command.Stop;
this.testMethods = 0;
this.stopDevs = StopDevs.All;
}
else if (scheduleDiverterToTank)
{
/// Reset all SendCommand() arguments
sendCommandFlag = true;
this.cmd = Command.Start;
this.refNr = flowmeterNr4DiverterTest;
this.totalRefPulses = int.MaxValue;
this.massRefPulses = int.MaxValue;
this.testMethods = TestMethods.Diverter;
this.stopDevs = 0;
scheduleDiverterToTank = false;
}
else if (scheduleDiverterToSink)
{
/// Reset all SendCommand() arguments
sendCommandFlag = true;
this.cmd = Command.Stop;
this.refNr = flowmeterNr4DiverterTest;
this.totalRefPulses = 0;
this.massRefPulses = 0;
this.testMethods = TestMethods.Diverter;
this.stopDevs = StopDevs.Diverter;
scheduleDiverterToSink = false;
}
else if (scheduleReadDiverterTransition)
{
/// Reset all SendCommand() arguments
sendCommandFlag = true;
this.cmd = Command.ReadDivTransition;
this.refNr = flowmeterNr4DiverterTest;
this.totalRefPulses = 0;
this.massRefPulses = 0;
this.testMethods = TestMethods.Diverter;
this.stopDevs = 0;
scheduleReadDiverterTransition = false;
}
else if (pumpPowerChanged)
{
/// Reset all SendCommand() arguments
sendCommandFlag = true;
this.cmd = Command.None;
pumpPowerChanged = false;
}
else
{
/// Reset all SendCommand() arguments
sendCommandFlag = false;
this.cmd = Command.None;
this.refNr = 0;
this.totalRefPulses = 0;
this.massRefPulses = 0;
this.testMethods = 0;
this.stopDevs = 0;
}
this.filterConstant = 0;
this.regConst = 0;
this.shortImp = 0;
}
/// <summary>Run this device</summary>
public void RunDeviceAfter()
{
if (sendCommandFlag)
{
controlCom.SendCommand(cmd, refNr, (benchModelRoute & routeMask),
totalRefPulses, massRefPulses, testMethods,
filterConstant, FMFreq, regConst, shortImp, stopDevs);
}
controlCom.RunDeviceAfter();
}
/// <summary>Stop this device</summary>
public void StopDevice()
{
}
delegate void ShowEmergencyStopFormDlgt();
///
void ShowEmergencyStopForm()
{
(new TBF.Forms.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">Route: 64-bit installation specific number</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, UInt128 route,
int totalRefPulses, int massRefPulses, TestMethods testMethods,
float filterConstant, int regConst, int shortImp, StopDevs stopDevs)
{
this.cmd = cmd;
this.refNr = refFlowmtrNr;
this.benchModelRoute = route;
this.totalRefPulses = totalRefPulses;
this.massRefPulses = massRefPulses;
this.testMethods = testMethods;
this.filterConstant = filterConstant;
this.regConst = regConst;
this.shortImp = shortImp;
this.stopDevs = stopDevs;
sendCommandFlag = true;
}
/// <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<Elde.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));
sendCommandFlag = true;
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 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 SetValvesOp(this, valvesOpen, valvesClose);
}
/// <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 StartMeasurementOp(OutputPath outputPath, TestMethods method,
float requiredFlowLo, float requiredFlowHi,
int refPulses, float filterConstant)
{
if (controlBoardCfg.DebugLevel == DebugMode.Simulate)
{
return new Operations.ReturnGivenEventOp(Event.MeasurementStarted);
}
else
{
return new StartMeasurementOp(this, outputPath, method, requiredFlowLo, requiredFlowHi,
refPulses, filterConstant);
}
}
/// <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 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);
}
/// <summary>
/// Updates the tank drawing and weight value in Elde software component.
/// </summary>
/// <param name="weight">Current mass in [kg] or approx. volume in [l]</param>
/// <returns>Created operation</returns>
public IOperation UpdateTankWeightOp()
{
return new UpdateTankWeightOp(this);
}
}
}