/// /// Copyright (c) 2021 Sensus Slovensko a.s. /// using System; using System.Collections.Generic; using log4net; using SchematicDrawing; using TBF.Rig.ControlBoard.Uni; using TBF.Boxes; using SharedComponents; namespace TBF.Rig.Uni.RegValveLowRegulTimeSaturation { public class RegValve : ComponentBase, Generic.IDevice, GenericDevices.IRegValve, IDrawingItCmpntWithSetpoint { private static readonly ILog log = LogManager.GetLogger(typeof(RegValve)); public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } public readonly RegValveCfg regValveCfg; public IDrawingItem DrawingItem { get { return regValveCfg as IDrawingItem; } } IDictionary dict; public IDictionary Dict { get { return dict; } } public readonly UniCB UniCB; public ValveCategory Category { get { return regValveCfg.Category; } } public int Idx1 { get { return regValveCfg.Idx1; } } /// 1 .. 8 public int DacValueClosed { get { return regValveCfg.AdcValueClosed; } } /// 0 .. 1023 public int DacValueOpen { get { return regValveCfg.AdcValueOpen; } } /// 0 .. 1023 public int StableTime { get { return regValveCfg.StableTime; } } /// 0=200ms, step=50ms, max. 1500ms (max.26) public int FlowStableSec { get { return regValveCfg.FlowStableSec; } } /// 0 .. 60 sec public bool StoredPositionReuse { get { return regValveCfg.StoredPositionReuse; } } public int RegulationMinStep { get { return regValveCfg.RegulationMinStep; } } /// low regulation saturation time ...0=0, 1=50, 2=100, 3=150, 4=200, 5=250, 6=300 [ms] /// public bool IsCoax { get { return false; } } public RegValveState RegValveState { get { switch (UniCB.Data.RegVStatus[Idx1 - 1]) { default: case 0: return ControlBoard.Uni.RegValveState.Idle; case 2: return ControlBoard.Uni.RegValveState.PwOrFreqRegul; case 3: return ControlBoard.Uni.RegValveState.DacValueRegul; } } } /// Position 0.0 .. 100.0 in % public double Position { get { int denominator = DacValueOpen - DacValueClosed; if (denominator == 0) denominator = 1; return 100.0 * Convert.ToDouble(Math.Max(0, Math.Min(denominator, (int)UniCB.AnalogInput(adcChannel) - DacValueClosed))) / Convert.ToDouble(denominator); } } /// public double SetpointVal { get { return Position; } set { TargetRegValvePosition = value; } } /// public void IncreaseSetpoint() { IncrementalMovement(true, 0.5); } /// public void DecreaseSetpoint() { IncrementalMovement(true, -0.5); } int adcChannel; public double TargetRegValvePosition; /// /// Incremental movement of the regulation valve. /// /// Time in seconds, positive value opens the reg. valve public void IncrementalMovement(bool isFromUI, double time) { UniCB.RegVlvIncrMove(isFromUI, Idx1, time, RegulationMinStep); } /// /// Move a regulation valve to specfied position /// /// Reg. valve ID /// Reg. valve position (0 .. 1.0) public void MoveToPosition(bool isFromUI, double posPctLo, double posPctHi = -1) { int adcDiff = Math.Abs(regValveCfg.AdcValueOpen - regValveCfg.AdcValueClosed); int adcLower = Math.Min(regValveCfg.AdcValueOpen, regValveCfg.AdcValueClosed); int targetAdcValLo = Math.Max(0, Convert.ToInt32(Math.Round((posPctLo * adcDiff / 100.0) + adcLower))); int targetAdcValHi = (posPctHi < 0) ? -1 : Math.Min(1023, Convert.ToInt32(Math.Round((posPctHi * adcDiff / 100.0) + adcLower))); UniCB.RegVlvMoveToPos(isFromUI, Idx1, targetAdcValLo, targetAdcValHi, RegulationMinStep); } #region Configuration Change Handling public static void OnCfgChange(object sender, CfgChangeArgs args) { if (CfgChangeHandler == null) return; try { CfgChangeHandler(sender, args); } catch (Exception e) { log.Error("CfgChangeHandler(...) failed", e); } } public static event EventHandler CfgChangeHandler; public override void StartChangeHandler() { CfgChangeHandler += delegate(object sender, CfgChangeArgs args) { RegValveCfg tmpcfg = args.Cfg as RegValveCfg; if (tmpcfg != null && tmpcfg.Name.Equals(Name)) { if (args.Command == CfgChangeCmd.CfgChange) { regValveCfg.StableTimeMs = tmpcfg.StableTimeMs; regValveCfg.FlowStableSec = tmpcfg.FlowStableSec; regValveCfg.RegulationMinStep = tmpcfg.RegulationMinStep; } else if (args.Command == CfgChangeCmd.RVOpenStep) { IncrementalMovement(true, 0.5); /// true = command is comming from the UI } else if (args.Command == CfgChangeCmd.RVCloseStep) { IncrementalMovement(true, -0.5); /// true = command is comming from the UI } else if (args.Command == CfgChangeCmd.GetAdc1 || args.Command == CfgChangeCmd.GetAdc2) { short adcValue = UniCB.AnalogInput(adcChannel); RegValveCfgCtrl.OnCmdResponse(this, new CmdResponseArgs(args.Command, Idx1, adcValue)); } } }; } #endregion Configuration Change Handling public RegValve() { } public RegValve(Generic.IComponentCfg cfg, IList components) : base(cfg) { regValveCfg = cfg as RegValveCfg; this.UniCB = TbfComponents.FindComponent(cfg.ParentName, components) as UniCB; if (this.UniCB == null) throw new Exception(string.Format("Cannot find {0} (a parent of {1})", cfg.ParentName, Name)); } public override void Initialize() { adcChannel = regValveCfg.Idx1 - 1; this.dict = new Dictionary(); TBF.UiBridge.Bridge.SetpointChangeHandler += delegate(object sndr, TBF.UiBridge.SetpointChangeArgs args) { if (args.Name == Name) { if (args.Increase) IncreaseSetpoint(); else DecreaseSetpoint(); } }; log.FatalFormat("{0} initialized: {1}", Name, this); } /// /// IDevice interface implementation /// public void RunDeviceBefore() { Int16 adcValue = UniCB.AnalogInput(adcChannel); Handlers.OnAdcChanged(this, new CmdResponseArgs(CfgChangeCmd.GetAdc, Idx1, adcValue)); } public void RunDeviceAfter() { } public void StopDevice() { } public void StopDevice2() { } /// /// Operation to set the water flow within tolerances /// Events: Event.None, Event.FlowReached, Event.FlowTimeOut /// /// Flowmeter component /// Lower limit of the required flow [m3/h] /// Higher limit of the required flow [m3/h] /// PID coefficient (float) /// Measured flow [m3/h] /// Timeout in [s] /// SetFlowOp instance public IOperation SetFlowOp(GenericDevices.IFlowMeter flowMeter, double requiredFlowLo, double requiredFlowHi, DoubleBox measuredFlow, int timeout, int delay) { return new SetFlowOp(UniCB, this, flowMeter, requiredFlowLo, requiredFlowHi, measuredFlow, timeout, delay); } /// /// Operation to set the water flow within tolerances. Leave the measurement running. /// Events: Event.None, Event.FlowReached, Event.FlowTimeOut /// /// Flowmeter component /// Lower limit of the required flow [m3/h] /// Higher limit of the required flow [m3/h] /// Measured flow [m3/h] /// Timeout in [s] /// SetFlowOp instance public IOperation SetFlowAndMeasureOp(GenericDevices.IFlowMeter flowMeter, double requiredFlowLo, double requiredFlowHi, DoubleBox measuredFlow, int timeout, float filterConstant) { return new SetFlowOp(UniCB, this, flowMeter, requiredFlowLo, requiredFlowHi, measuredFlow, timeout, 0, true); } /// /// Operation to set the regulation valve to a required position /// Events: Event.None /// /// Lower limit of the required valve position in [%] /// Higher limit of the required valve position in [%] /// SetPositionOp instance public IOperation SetRegValvePositionOp(double pctLo, double pctHi, int timeoutMs) { return new SetRegValvePositionOp(UniCB, this, pctLo, pctHi, timeoutMs); } public IOperation ChangeRegValvePositionOp(double timePulseSec) { return new ChangeRegValvePositionOp(UniCB, this, timePulseSec); } } }