/// /// Copyright (c) 2013-2015 Sensus Metering Systems /// using System; using System.Text; using System.IO.Ports; using System.Collections.Generic; 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; /// /// Emergency stop signal state updated by RunDevice(). /// 'modelessDlg' is notified about the changes and uses this information to change its visibility. /// //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 float ReferenceFreq { get { return controlCom.ReferenceFreq; } } /// In [Hz] 0..2500, nom.=2000 public int EtPulses(int wmNr0) { return controlCom.EtPulses(wmNr0); } public int 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 ulong 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 float 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 uint 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.ReferenceVolume; } } public float VyslExt(int wmNr0, int what) { return controlCom.VyslExt(wmNr0, what); } /// Extracted from the StatusP in RunDeviceBefore public bool EmergencyStop { get { return emergencyStop; } } bool emergencyStop; bool previousEmergencyStop; private int flowmeterNr4DiverterTest; private bool scheduleDiverterToTank; private bool scheduleDiverterToSink; private bool scheduleReadDiverterTransition; /// 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; } } /// State of valves sent out - buffered by the last SendCommand() public ulong Route { get { return route; } } ulong valvesToInvert; /// Route = valesToInvert ^ RequiredRouteWithoutInvertedVlaves /// /// Initialization data updated by constructors of children components /// and sent to 'controlCom2panel' by SendCalibData() method. /// public uint[,] RegValveCalib = new uint[8, 2]; public byte[] MeretRS485Address = new byte[4]; public float[,] TempCalibData = new float[8, 5]; // Array length is the reference flowmeters count plus one #if DN100 public float[] EtCalib = new float[6] { 1, 1, 1, 1, 1, 1 }; #elif MUNICH || TORINO50 || BADGER_MALA_TRAT || TORUN_PT50_2015 || CEVAK_PT40_272 public float[] EtCalib = new float[5] { 1, 1, 1, 1, 1 }; #elif FUZHOU150 || FUZHOU300 || PT200IL public float[] EtCalib = new float[6] { 1, 1, 1, 1, 1, 1 }; #endif public uint[] DiverterEdge = new uint[2] { 50, 50 }; public uint[] BalanceRange = new uint[2]; /// /// Private values sent by the last SendCommand() /// bool sendCommandFlag; Command cmd; int refNr; ulong route; int refPulses; TestMethods testMethods; float filterConstant; float[] FMFreq; int regConst; int shortImp; StopDevs stopDevs; /// /// Sets the power of the selected pump /// /// Pump index 0 .. 5, invalid pump index sets all pumps to 0% /// Power (0.0f .. 100.0f) public void SetFMFreq(int index, float power) { #if FUZHOU300 || PT200IL if ((FMFreq == null) || (FMFreq.Length != 6)) FMFreq = new float[6]; #else if ((FMFreq == null) || (FMFreq.Length != 2)) FMFreq = new float[2]; #endif if (index >= 0 && index < FMFreq.Length) FMFreq[index] = power; 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]); } } /// /// Constructor /// public ControlBoardDev(ControlBoardCfg cfg) : base(cfg) { controlBoardCfg = cfg; #if FUZHOU300 || PT200IL FMFreq = new float[6] { 0, 0, 0, 0, 0, 0 }; /// Nr. of pumps: Fuzhou150=2, Fuzhou300,PT200IL=6 #else FMFreq = new float[2] { 0, 0 }; /// Nr. of pumps: Fuzhou150=2, Fuzhou300=6 #endif emergencyStop = false; previousEmergencyStop = false; log.Debug(this.ToString()); } /// /// Specific pre-initialization for control board only /// /// public void InitializeComponent( #if DN100 ControlCom2VB.ControlCom2panel ctrlBrdComponent, #elif MUNICH ControlComponent3Munich.UserControl1 ctrlBrdComponent, #elif FUZHOU150 ControlComponent3Munich.UserControl1 ctrlBrdComponent, #elif FUZHOU300 ControlComponent3F300.UserControl1 ctrlBrdComponent, #elif PT200IL ControlComponent_Izrael2014.UserControl1 ctrlBrdComponent, #elif TORINO50 ControlComponent_Torino2015.UserControl1 ctrlBrdComponent, #elif BADGER_MALA_TRAT ControlComponent_Torino2015.UserControl1 ctrlBrdComponent, #elif TORUN_PT50_2015 || CEVAK_PT40_272 ControlComponent_Torino2015.UserControl1 ctrlBrdComponent, #endif ulong valvesToInvert, float[] tankCapacities) { this.valvesToInvert = valvesToInvert; this.route = valvesToInvert; if (controlBoardCfg.DebugLevel == Entities.DebugMode.Simulate) { controlCom = new ControlComSim(); } else { int cnt = Math.Min(BalanceRange.Length, tankCapacities.Length); for (int i = 0; i < cnt; i++) BalanceRange[i] = (uint)tankCapacities[i]; controlCom = new ControlComWrap(ctrlBrdComponent); } } /// Initialize this device public void Initialize() { controlCom.SendCalibData(RegValveCalib, MeretRS485Address, controlBoardCfg.ComPortNr, TempCalibData, EtCalib, DiverterEdge, BalanceRange); log.FatalFormat("Successfully initialized device {0}", ToString()); } /// Run this device public void RunDeviceBefore() { controlCom.RunDeviceBefore(); previousEmergencyStop = emergencyStop; emergencyStop = (controlCom.StatusP & StatusP.EmgcyStopActive) != 0; if (emergencyStop && !previousEmergencyStop) { Program.MainWnd.Invoke(new ShowEmergencyStopFormDlgt(ShowEmergencyStopForm)); } 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.refPulses = 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.refPulses = 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.refPulses = 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.refPulses = 0; this.testMethods = TestMethods.Diverter; this.stopDevs = 0; scheduleReadDiverterTransition = false; } else { /// Reset all SendCommand() arguments sendCommandFlag = false; this.cmd = Command.None; this.refNr = 0; this.refPulses = 0; this.testMethods = 0; this.stopDevs = 0; } this.filterConstant = 0; this.regConst = 0; this.shortImp = 0; } /// Run this device public void RunDeviceAfter() { if (sendCommandFlag) { controlCom.SendCommand(cmd, refNr, route, refPulses, testMethods, filterConstant, FMFreq, regConst, shortImp, stopDevs); } controlCom.RunDeviceAfter(); } /// Stop this device public void StopDevice() { } delegate void ShowEmergencyStopFormDlgt(); /// void ShowEmergencyStopForm() { (new TBF.Forms.EmergencyStopForm()).Show(); } /// /// Sends a command to the control board. /// /// See ControlBoard.Command enum /// Number of the etalon/reference (1..5) /// Route: 64-bit installation specific number /// kolko impulzov ma trvat skuska /// See ControlBoard.TestMethods enum /// 0 = No filtering (0..255) /// Coefficient used to control reg. valves (1..100) /// 0 = default value, 1 = spec. processing of very short pulses /// What to stop public void SendCommand(Command cmd, int refFlowmtrNr, ulong route, int totalRefPulses, TestMethods testMethods, float filterConstant, int regConst, int shortImp, StopDevs stopDevs) { this.cmd = cmd; this.refNr = refFlowmtrNr; this.route = route; this.refPulses = totalRefPulses; this.testMethods = testMethods; this.filterConstant = filterConstant; this.regConst = regConst; this.shortImp = shortImp; this.stopDevs = stopDevs; sendCommandFlag = true; } /// /// Set valves to new positions. /// /// 'ulong' with bits of valves to open set to '1' /// 'ulong' with bits of valves to close set to '1' /// 'ulong' with bits of valves that have changed set to '1' public ulong SetValves(ulong valvesToOpen, ulong valvesToClose) { ulong oldRoute = route; route = (((route ^ valvesToInvert) | valvesToOpen) & (~valvesToClose)) ^ valvesToInvert; log.DebugFormat("SetValves(x,x), new route = {0}", Utils.ShowRoute(route)); sendCommandFlag = true; return oldRoute ^ route; } /// /// Regulation valves control. /// /// Regulation valve nr. (1..5) /// Valve regulation mode, see the enum /// Target position or target frequency or pulse duration /// Stabilization time when setting the flow: 0=200ms, step 50ms, max. 1.5 sec. public void ValveMove(int valveNo, RegulValveMode valveMode, float[] valveValue, int stableTime) { controlCom.ValveMove(valveNo, valveMode, valveValue, stableTime); } /// /// Open one valve and/or close one valve. /// Events: ValvesSet /// /// Valve to be opened /// Valve to be closed /// Created operation public IOperation SetValvesOp(IValve valveOpen, IValve valveClose) { return new Valve.SetValvesOp(this, valveOpen, valveClose); } /// /// Open and/or close multiple valves. /// Events: ValvesSet /// /// A list of valves to be opened /// A list of valves to be closed /// Created operation public IOperation SetValvesOp(IList valvesOpen, IList valvesClose) { return new Valve.SetValvesOp(this, valvesOpen, valvesClose); } /// /// Starts the measurement. /// Events: MeasurementStarted /// /// Flow meter /// An array of register readers /// Number of reference pulses to complete the test /// Created operation public IOperation StartMeasurementOp(IFlowMeter flowMeter, int refPulses, TestMethods method, float filterConstant) { return new StartMeasurementOp(this, flowMeter, refPulses, method, filterConstant); } /// /// Queries th econtrol board whether the measurement was completed. /// Events: MeasurementCompleted or None /// /// Created operation public IOperation QueryMeasurementEndOp() { return new QueryMeasurementEndOp(this); } /// /// Stop the previous control board operation (diverter, gate, etc.) /// Events: PreviousStopped /// /// Created operation public IOperation StopPreviousOp() { return new StopPreviousOp(this); } /// /// Updates the tank drawing and weight value in Elde software component. /// /// Current mass in [kg] or approx. volume in [l] /// Created operation public IOperation UpdateTankWeightOp() { return new UpdateTankWeightOp(this); } } }