diff --git a/TBF/Rig/BuiltIn/PumpTandem/Pump.cs b/TBF/Rig/BuiltIn/PumpTandem/Pump.cs index b98191176..a6a5ab259 100644 --- a/TBF/Rig/BuiltIn/PumpTandem/Pump.cs +++ b/TBF/Rig/BuiltIn/PumpTandem/Pump.cs @@ -67,11 +67,13 @@ namespace TBF.Rig.BuiltIn.PumpTandem if (pump1 is BuiltIn.Pump.Pump) { mask1 = (pump1 as BuiltIn.Pump.Pump).Mask; } else if (pump1 is Danfoss.VLT2800.Pump) { mask1 = (pump1 as Danfoss.VLT2800.Pump).Mask; } else if (pump1 is Modbus.PumpFM.DanfossVLT.Pump) { mask1 = (pump1 as Modbus.PumpFM.DanfossVLT.Pump).Mask; } + else if (pump1 is Modbus.PumpFM.Grundfoss.Pump) { mask1 = (pump1 as Modbus.PumpFM.Grundfoss.Pump).Mask; } else { mask1 = 0; } if (pump2 is BuiltIn.Pump.Pump) { mask2 = (pump2 as BuiltIn.Pump.Pump).Mask; } else if (pump2 is Danfoss.VLT2800.Pump) { mask2 = (pump2 as Danfoss.VLT2800.Pump).Mask; } else if (pump2 is Modbus.PumpFM.DanfossVLT.Pump) { mask2 = (pump2 as Modbus.PumpFM.DanfossVLT.Pump).Mask; } + else if (pump2 is Modbus.PumpFM.Grundfoss.Pump) { mask2 = (pump2 as Modbus.PumpFM.Grundfoss.Pump).Mask; } else { mask2 = 0; } log.FatalFormat("{0} initialized: {1}", Name, this); diff --git a/TBF/Rig/BuiltIn/PumpTandem/PumpCfgCtrl.cs b/TBF/Rig/BuiltIn/PumpTandem/PumpCfgCtrl.cs index 30e481c33..82e646e1f 100644 --- a/TBF/Rig/BuiltIn/PumpTandem/PumpCfgCtrl.cs +++ b/TBF/Rig/BuiltIn/PumpTandem/PumpCfgCtrl.cs @@ -44,6 +44,7 @@ namespace TBF.Rig.BuiltIn.PumpTandem if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is BuiltIn.Pump.PumpFactory || TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is Danfoss.VLT2800.PumpFactory || TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is Modbus.PumpFM.DanfossVLT.Factory || + TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is Modbus.PumpFM.Grundfoss.Factory || TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is BuiltIn.PumpTandem.PumpFactory) { pump1ComboBox.Items.Add(cmpnt.Name); diff --git a/TBF/Rig/BuiltIn/SetValvesOp.cs b/TBF/Rig/BuiltIn/SetValvesOp.cs index 404440ddd..505fd00cb 100644 --- a/TBF/Rig/BuiltIn/SetValvesOp.cs +++ b/TBF/Rig/BuiltIn/SetValvesOp.cs @@ -120,7 +120,8 @@ namespace TBF.Rig.BuiltIn if (valve is PumpTandem.Pump) point.MasksOpen |= (valve as PumpTandem.Pump).Mask; if (valve is Danfoss.VLT2800.Pump) point.MasksOpen |= (valve as Danfoss.VLT2800.Pump).Mask; if (valve is Modbus.PumpFM.DanfossVLT.Pump) point.MasksOpen |= (valve as Modbus.PumpFM.DanfossVLT.Pump).Mask; - } + if (valve is Modbus.PumpFM.Grundfoss.Pump) point.MasksOpen |= (valve as Modbus.PumpFM.Grundfoss.Pump).Mask; + } point.MasksClose = 0; foreach (var valve in point.VClose) { @@ -129,7 +130,8 @@ namespace TBF.Rig.BuiltIn if (valve is PumpTandem.Pump) point.MasksClose |= (valve as PumpTandem.Pump).Mask; if (valve is Danfoss.VLT2800.Pump) point.MasksClose |= (valve as Danfoss.VLT2800.Pump).Mask; if (valve is Modbus.PumpFM.DanfossVLT.Pump) point.MasksClose |= (valve as Modbus.PumpFM.DanfossVLT.Pump).Mask; - } + if (valve is Modbus.PumpFM.Grundfoss.Pump) point.MasksClose |= (valve as Modbus.PumpFM.Grundfoss.Pump).Mask; + } point.TimeSec += timeShift; } } diff --git a/TBF/Rig/BuiltIn/SwitchPoint.cs b/TBF/Rig/BuiltIn/SwitchPoint.cs index dcd6f4f2b..c8637456d 100644 --- a/TBF/Rig/BuiltIn/SwitchPoint.cs +++ b/TBF/Rig/BuiltIn/SwitchPoint.cs @@ -35,6 +35,7 @@ namespace TBF.Rig.BuiltIn if (vlv is Pump.Pump && ((vlv as Pump.Pump).Mask & changed) == 0) continue; if (vlv is Danfoss.VLT2800.Pump && ((vlv as Danfoss.VLT2800.Pump).Mask & changed) == 0) continue; if (vlv is Modbus.PumpFM.DanfossVLT.Pump && ((vlv as Modbus.PumpFM.DanfossVLT.Pump).Mask & changed) == 0) continue; + if (vlv is Modbus.PumpFM.Grundfoss.Pump && ((vlv as Modbus.PumpFM.Grundfoss.Pump).Mask & changed) == 0) continue; if ((vlv != null) && (vlv.Delay > max)) max = vlv.Delay; } @@ -45,6 +46,7 @@ namespace TBF.Rig.BuiltIn if (vlv is Pump.Pump && ((vlv as Pump.Pump).Mask & changed) == 0) continue; if (vlv is Danfoss.VLT2800.Pump && ((vlv as Danfoss.VLT2800.Pump).Mask & changed) == 0) continue; if (vlv is Modbus.PumpFM.DanfossVLT.Pump && ((vlv as Modbus.PumpFM.DanfossVLT.Pump).Mask & changed) == 0) continue; + if (vlv is Modbus.PumpFM.Grundfoss.Pump && ((vlv as Modbus.PumpFM.Grundfoss.Pump).Mask & changed) == 0) continue; if ((vlv != null) && (vlv.Delay > max)) max = vlv.Delay; } diff --git a/TBF/Rig/Modbus/PumpFM/Grundfoss/Factory.cs b/TBF/Rig/Modbus/PumpFM/Grundfoss/Factory.cs new file mode 100644 index 000000000..b82ffd0dc --- /dev/null +++ b/TBF/Rig/Modbus/PumpFM/Grundfoss/Factory.cs @@ -0,0 +1,25 @@ +/// +/// Copyright (c) 2020 Sensus Slovensko a.s. +/// +using System.Collections.Generic; +using TBF.Rig.Generic; + +namespace TBF.Rig.Modbus.PumpFM.Grundfoss +{ + public class Factory : IComponentFactory + { + public string ClassName { get { return this.GetType().Namespace.Substring(8); } } + public override string ToString() { return ClassName; } + + public IComponent DummyComponent() { return new Pump(); } + + public IComponent GetComponent(IComponentCfg cfg, IList components) { return new Pump(cfg, components); } + + public IComponentCfg DefaultConfig() { return new PumpCfg(this); } + + public IComponentCfg CmpntCfgFromCmpntEntity(Config.Entities.Component component) + { + return ComponentCfgBase.CreateFromDbEntity(PumpCfg.Serializer, component, this); + } + } +} diff --git a/TBF/Rig/Modbus/PumpFM/Grundfoss/Pump.cs b/TBF/Rig/Modbus/PumpFM/Grundfoss/Pump.cs new file mode 100644 index 000000000..ba8792c0b --- /dev/null +++ b/TBF/Rig/Modbus/PumpFM/Grundfoss/Pump.cs @@ -0,0 +1,497 @@ +using Common; +using Dirichlet.Numerics; +using log4net; +using SchematicDrawing; +using SharedComponents; +/// +/// Copyright (c) 2020 Sensus Slovensko a.s. +/// +using System; +using System.Collections.Generic; +using System.Diagnostics; +using TBF.Rig.ControlBoard; +using TBF.Rig.Generic; + +namespace TBF.Rig.Modbus.PumpFM.Grundfoss +{ + /// + /// See page 20 of MG10S202 Operational Instructions Modbus RTU + /// + public enum CW : ushort + { + PresetRefLsb = (1 << 0), + PresetRefMsb = (1 << 1), + Not_DcBraking = (1 << 2), + Not_CoastingStop = (1 << 3), + Not_QuickStop = (1 << 4), + Not_FreezeFreq = (1 << 5), + Start_NotRampStop = (1 << 6), + Reset = (1 << 7), + Jog = (1 << 8), + Ramp2_NotRamp1 = (1 << 9), + DataValid = (1 << 10), + Relay01Activ = (1 << 11), + DOut46Activ = (1 << 12), + SelectSetupLsb = (1 << 13), + SelectSetupMsb = (1 << 14), + Reversing = (1 << 15), + } + + /// + /// See page 22 of MG10S202 Operational Instructions Modbus RTU + /// + public enum SW : ushort + { + ControlReady = (1 << 0), + DriveReady = (1 << 1), + CoastingStop = (1 << 2), + Trip = (1 << 3), + TripLock = (1 << 6), + Warning = (1 << 7), + SpeedEqRef = (1 << 8), + RemoteCtrl = (1 << 9), + FreqLimitOK = (1 << 10), + MotorRunning = (1 << 11), + VoltageWarn = (1 << 13), + CurrentLimit = (1 << 14), + ThermalWarn = (1 << 15), + } + + public enum Function : byte + { + ReadCoilStatus = 0x01, + ForceSingleCoil = 0x05, + ForceMultipleCoils = 0x0F, + ReadHoldingRegisters = 0x03, + PresetSingleRegister = 0x06, + PresetMultipleRegisters = 0x10, + } + + + public class Pump : ComponentBase, IDevice, IOperation, GenericDevices.IPumpFM, GenericDevices.IValve, IDrawingItCmpntWithSetpoint + { + private static readonly ILog log = LogManager.GetLogger(typeof(Pump)); + public override string ToString() { return string.Format("{0}({1})", ClassName, Cfg.ToString(-1)); } + + private readonly PumpCfg pumpCfg; + public IDrawingItem DrawingItem { get { return pumpCfg as IDrawingItem; } } + + public int Delay { get { return pumpCfg.Delay; } } + public ValveCategory Category { get { return ValveCategory.Feeding; } } /// Pump category is Feeding + public GenericDevices.IValve CoupledTo { get { return null; } } + public bool InvertCouple { get { return false; } } + public int LagOpening { get { return 0; } } + public int LagClosing { get { return 0; } } + + /// + /// Pump power in [%] in range 0 .. 100.0 + /// + double power; + public double Power { get { return power; } } + /// + public double SetpointVal + { + get { return power; } + set + { + power = (float)value; + if (power != 0) TurnOn(power); + } + } + /// + public void IncreaseSetpoint() + { + power += 3.0; + if (power > 100.0) power = 100.0; + TurnOn(power); + } + /// + public void DecreaseSetpoint() + { + power -= 3.0; + if (power <= 0) + { + power = 0; + TurnOff(); + } + else + { + TurnOn(power); + } + } + + Common.Modbus modbus; + IControlBoard controlBoard; + int bitNr; /// 0 .. 63 + public UInt128 Mask; /// derived from bitPosition in the constructor + + public bool State { get { return (controlBoard.Route & Mask) != 0; } } + + + public Pump() { } + + /// + /// Ambient temperature / humidity / pressure meter 'Greco' connected via serial interface (RS232) + /// Connection settings: 19200 Bd 8-bits No-parity 1-stop-bit Flow control: none or hardware. + /// + public Pump(Generic.IComponentCfg cfg, IList components) + : base(cfg) + { + pumpCfg = cfg as PumpCfg; + } + public override void Initialize() + { + if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + modbus = TbfComponents.FindComponent(pumpCfg.ParentName) as Common.Modbus; + if (modbus == null) throw new Exception("Cannot find " + Name + " parent"); + + controlBoard = null; + foreach (var name in new string[] { "CB", "UniCB" }) + { + if ((controlBoard = TbfComponents.FindComponent(name) as IControlBoard) != null) + break; + } + if (controlBoard == null) throw new Exception("Cannot find a control board"); + + bitNr = pumpCfg.BitNr; + Mask = (((UInt128)1) << bitNr); + modbus.ComponentNames[pumpCfg.ModbusAddress] = Name; + + 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); + } + else + { + log.FatalFormat("{0} simulated: {1}", Name, this); + } + } + + /// + /// IDevice interface + /// + public void RunDeviceBefore() + { + /*if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + if (modbus.ReceivedTelegrams[pumpCfg.ModbusAddress].Count > 0) + { + byte[] telegram = modbus.ReceivedTelegrams[pumpCfg.ModbusAddress].Dequeue(); + string s = Telegram.LogTelegram(string.Format("Telegram received from {0}: ", Name), telegram); + log.Warn(s); + } + }*/ + + if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + if (modbus.ReceivedTelegrams[pumpCfg.ModbusAddress].Count > 0) + { + try + { + byte[] telegram = modbus.ReceivedTelegrams[pumpCfg.ModbusAddress].Dequeue(); + + string hex = BitConverter.ToString(telegram).Replace("-", " "); + + string msg = $"Telegram received from {Name}: {hex}"; + + log.Warn(msg); + + LiveLogDiag.Log1(msg); + } + catch (Exception ex) + { + log.Error($"RunDeviceBefore() – error processing telegram: {ex}"); + LiveLogDiag.Log1($"RunDeviceBefore() – error processing telegram: {ex.Message}"); + } + } + } + } + + public void RunDeviceAfter() { } + public void StopDevice() { } + public void StopDevice2() { } + + + /*/// + /// Turn the pmp on using Modbus commad, use the variable frequency + /// + /// Frequency in percent (-200.0 .. 200.0) + public void TurnOn(double powerArg) + { + log.DebugFormat("{0}.TurnOn({1})", Name, powerArg); + + UInt16 controlWord = (UInt16)(CW.Not_DcBraking | + CW.Not_CoastingStop | + CW.Not_QuickStop | + CW.Not_FreezeFreq | + CW.Start_NotRampStop | + CW.DataValid); + this.power = powerArg; + UInt16 reference = (UInt16)Math.Round(0x4000 * powerArg / 100.0); + + if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + byte[] msg = new byte[13]; + msg[0] = (byte)pumpCfg.ModbusAddress; + msg[1] = (byte)Function.ForceMultipleCoils; + msg[2] = 0; /// Data Hi + msg[3] = 0; /// Data Lo + msg[4] = 0; /// Nr. of coils Hi + msg[5] = 0x20; /// Nr. of coils Lo + msg[6] = 4; /// Byte count + msg[7] = (byte)(controlWord & 0xFF); /// Control Word Lo + msg[8] = (byte)(controlWord >> 8); /// Control Word Hi + msg[9] = (byte)(reference & 0xFF); /// Serial Com Reference Lo + msg[10] = (byte)(reference >> 8); /// Serial Com Reference Hi + /// + modbus.SendMessage(msg, Name); + + /// --- DIAGNOSTIKA --- + LiveLogDiag.Log1("--------------------------------------------"); + LiveLogDiag.Log1($"TurnOff controlWord=0x{controlWord:X4}"); + LiveLogDiag.Log1($"TurnOff reference={reference}"); + + LiveLogDiag.Log1("TurnOff msg = " + BitConverter.ToString(msg)); + } + }*/ + + public void TurnOn(double powerArg) + { + log.DebugFormat("{0}.TurnOn({1})", Name, powerArg); + + LiveLogDiag.Log1("--------------------------------------------"); + LiveLogDiag.Log1("{0}.TurnOn({1})", Name, powerArg); + LiveLogDiag.Log1($"DebugLevel={DebugLevel}"); + + // clamp 0–100 % + double pct = Math.Max(0, Math.Min(100.0, powerArg)); + UInt16 setpoint = (UInt16)Math.Round(pct * 100); // fH,fL = %*100 + this.power = pct; + + if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + byte[] msg = new byte[17]; + + msg[0] = (byte)pumpCfg.ModbusAddress; // adr + msg[1] = 0x10; // function = write multiple registers + msg[2] = 0x00; // start address hi + msg[3] = 0x64; // start address lo = 100 + msg[4] = 0x00; // register count hi + msg[5] = 0x05; // register count lo = 5 + msg[6] = 0x0A; // byte count = 10 + + // ----- registers ----- + + // reg100 = command = 0x0007 (RUN) + msg[7] = 0x00; + msg[8] = 0x07; + + // reg101 = 0x0001 + msg[9] = 0x00; + msg[10] = 0x01; + + // reg102 = 0x0000 + msg[11] = 0x00; + msg[12] = 0x00; + + // reg103 = setpoint = fH,fL + msg[13] = (byte)(setpoint >> 8); + msg[14] = (byte)(setpoint & 0xFF); + + // reg104 = 0x0001 (RUN) + msg[15] = 0x00; + msg[16] = 0x01; + + //Telegram.UpdateTelegramCRC(msg); + modbus.SendMessage(msg, Name); + modbus.SendMessage(msg, Name); + + // --- DIAGNOSTIKA --- + LiveLogDiag.Log1($"GF TurnOn Setpoint={pct}"); + LiveLogDiag.Log1($"GF TurnOn raw reference={setpoint}"); + LiveLogDiag.Log1("GF TurnOn msg = " + BitConverter.ToString(msg)); + } + } + + /*/// + /// Turn the pmp off using Modbus command + /// + public void TurnOff() + { + log.DebugFormat("{0}.TurnOff()", Name); + + UInt16 controlWord = (UInt16)(CW.Not_DcBraking | + CW.Not_CoastingStop | + CW.Not_QuickStop | + CW.Not_FreezeFreq | + //CW.Start_NotRampStop | + CW.DataValid); + this.power = 0; + UInt16 reference = 0; + + if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + byte[] msg = new byte[13]; + msg[0] = (byte)pumpCfg.ModbusAddress; + msg[1] = (byte)Function.ForceMultipleCoils; + msg[2] = 0; /// Data Hi + msg[3] = 0; /// Data Lo + msg[4] = 0; /// Nr. of coils Hi + msg[5] = 0x20; /// Nr. of coils Lo + msg[6] = 4; /// Byte count + msg[7] = (byte)(controlWord & 0xFF); /// Control Word Lo + msg[8] = (byte)(controlWord >> 8); /// Control Word Hi + msg[9] = (byte)(reference & 0xFF); /// Serial Com Reference Lo + msg[10] = (byte)(reference >> 8); /// Serial Com Reference Hi + Telegram.UpdateTelegramCRC(msg); + + modbus.SendMessage(msg, Name); + + /// --- DIAGNOSTIKA --- + LiveLogDiag.Log1("--------------------------------------------"); + LiveLogDiag.Log1($"TurnOff controlWord=0x{controlWord:X4}"); + LiveLogDiag.Log1($"TurnOff reference={reference}"); + + LiveLogDiag.Log1("TurnOff msg = " + BitConverter.ToString(msg)); + } + }*/ + + /// + /// Turn the pump off using Modbus command (Grundfos version) + /// + public void TurnOff() + { + log.DebugFormat("{0}.TurnOff()", Name); + + LiveLogDiag.Log1("--------------------------------------------"); + LiveLogDiag.Log1("{0}.TurnOff()", Name); + LiveLogDiag.Log1($"DebugLevel={DebugLevel}"); + + this.power = 0; + UInt16 setpoint = 0; // desired = 0% + + if (DebugLevel == DebugMode.Normal || DebugLevel == DebugMode.DetectedOn) + { + byte[] msg = new byte[17]; + + msg[0] = (byte)pumpCfg.ModbusAddress; // adr + msg[1] = 0x10; // function + msg[2] = 0x00; + msg[3] = 0x64; // reg100 + msg[4] = 0x00; + msg[5] = 0x05; // 5 registers + msg[6] = 0x0A; // 10 bytes + + // reg100 = 0x0005 (STOP) + msg[7] = 0x00; + msg[8] = 0x05; + + // reg101 = 0x0001 + msg[9] = 0x00; + msg[10] = 0x01; + + // reg102 = 0x0000 + msg[11] = 0x00; + msg[12] = 0x00; + + // reg103 = setpoint = 0 + msg[13] = 0x00; + msg[14] = 0x00; + + // reg104 = 0x0000 (STOP) + msg[15] = 0x00; + msg[16] = 0x00; + + //Telegram.UpdateTelegramCRC(msg); + modbus.SendMessage(msg, Name); + modbus.SendMessage(msg, Name); + + // --- DIAGNOSTIKA --- + LiveLogDiag.Log1("GF TurnOff msg = " + BitConverter.ToString(msg)); + } + } + + /// + /// The currently running operation. + /// + public enum CurrentOp + { + None, + TurnOn, + TurnOff, + } + /// + CurrentOp currentOp; + + double powerForOp; + + /// + /// Turn the pump frequency inverter on. + /// + public IOperation TurnOnOp(double powerArg) + { + if (currentOp != CurrentOp.None) throw new Exception("Sequence error"); + currentOp = CurrentOp.TurnOn; + powerForOp = powerArg; + return this; + } + + /// + /// Turn the pump frequency inverter on. + /// + public IOperation TurnOffOp() + { + if (currentOp != CurrentOp.None) throw new Exception("Sequence error"); + currentOp = CurrentOp.TurnOff; + return this; + } + + /// Start the operation + public void Start() + { + switch (currentOp) + { + case CurrentOp.TurnOn: + TurnOn(powerForOp); + return; + + case CurrentOp.TurnOff: + default: + TurnOff(); + return; + } + } + + /// Run the operation + public Event Run() + { + return Event.TurnPumpOnOffDone; + } + + /// Stop the operation + public void Stop() + { + currentOp = CurrentOp.None; + } + } + + /// + /// Diagnostic logging + /// Activated by compilation condition: DIAG_ENDURANCE_LOG + /// + static class LiveLogDiag + { + [Conditional("DIAG_LOG_PUMP")] + public static void Log1(string format, params object[] args) + { + LiveLogCache.Instance.AddLog(string.Format(format, args)); + } + } +} diff --git a/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfg.cs b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfg.cs new file mode 100644 index 000000000..c2dd27b04 --- /dev/null +++ b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfg.cs @@ -0,0 +1,84 @@ +/// +/// Copyright (c) 2020 Sensus Slovensko a.s. +/// +using System; +using System.Collections.Generic; +using System.Xml.Serialization; +using Common; +using Config.Entities; +using SchematicDrawing; +using TBF.Rig.Generic; + +namespace TBF.Rig.Modbus.PumpFM.Grundfoss +{ + public class PumpCfg : ComponentCfgBase, IChildComponentCfg, IRouteBasedDrawingItem, IDrawingItemWithSetpoint + { + public static XmlSerializer Serializer = XmlSerializer.FromTypes(new[] { typeof(PumpCfg) })[0]; + public override XmlSerializer GetSerializer() { return Serializer; } + + public IComponentCfgCtrl GetControl(IList cmpntEntities) { return new PumpCfgCtrl(); } + + /// + /// Serialized parameters + /// + public int BitNr { get; set; } + public bool Inverted { get; set; } /// When true the pump function is inverted: 0 = on, 1 = off + public int Delay; /// Delay of the pump function in [s] (affects the duration of transition sequence steps) + public byte ModbusAddress; /// 1..254 + + public Shape Shape { get; set; } + public int X { get; set; } + public int Y { get; set; } + public Sz Sz { get; set; } + public Orient Orient { get; set; } + public bool Flip { get; set; } + public int LblX { get; set; } + public int LblY { get; set; } + public Orient LblOrient { get; set; } + public int SetpX { get; set; } + public int SetpY { get; set; } + public Orient SetpOrient { get; set; } + public string SetpFormat { get; set; } + public Unit SetpUnit { get; set; } + + [XmlIgnore] + public IList GNodes { get; set; } + [XmlIgnore] + public IList EdgesToSet { get; set; } + + /// Private parameterless constructor invoked by all other (public) constructors + PumpCfg() + { + GNodes = new List(); + EdgesToSet = new List(); + } + + public PumpCfg(IComponentFactory factory) + : this() + { + Shape = Shape.PumpFM; + Sz = Sz.M; + SetpFormat = "{0:F0} %"; + SetpUnit = Unit.Pct; + + Name = "P"; + Factory = factory; + ParentName = "Modbus"; + BitNr = 0; + Delay = 1; + ModbusAddress = 49; + } + + + + public string ToString(int i) + { + return string.Format("name={0} bit={1} delay={2} addr={3} parent={4}", + Name, + BitNr, + Delay, + ModbusAddress, + (string.IsNullOrEmpty(ParentName) ? "-" : ParentName)); + } + } +} diff --git a/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.cs b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.cs new file mode 100644 index 000000000..b00514604 --- /dev/null +++ b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.cs @@ -0,0 +1,150 @@ +/// +/// Copyright (c) 2020 Sensus Slovensko a.s. +/// +using System; +using System.Windows.Forms; +using Common; +using Config.Entities; +using TBF.Rig.Generic; +using TBF.UI.Bench.Components; + +namespace TBF.Rig.Modbus.PumpFM.Grundfoss +{ + public partial class PumpCfgCtrl : UserControl, IComponentCfgCtrl + { + ComponentParametersDlg parent; + + public bool ShowMore { get { return false; } } + + PumpCfg config; + public IComponentCfg Config + { + get { return config as IComponentCfg; } + set + { + config = value as PumpCfg; + Redraw(); + } + } + + public PumpCfgCtrl() + { + InitializeComponent(); + } + + private void PressureMeterCfgCtrl_Load(object sender, EventArgs e) + { + parent = ParentForm as ComponentParametersDlg; + if (parent == null) return; + + if (parent.CmpntEntities != null) + { + foreach (var cmpnt in parent.CmpntEntities) + { + if (TbfComponents.CmpntFactoryFromClassName(cmpnt.ClassName) is Modbus.Common.Factory) + { + parentNameComboBox.Items.Add(cmpnt.Name); + } + } + } + + for (Unit units = 0; units < Unit.Count; units++) + { + if (global::Common.Units.IsQuantity(units, Quantity.Error)) + { + unitsComboBox.Items.Add(units.ToString()); + } + } + + Redraw(); + } + + public void Closing() + { + } + + void Redraw() + { + if (config == null) return; /// Control was not loaded, settings were not changed + + componentNameLabel.Text = config.Factory.ClassName; + nameTextBox.Text = config.Name; + parentNameComboBox.Text = string.IsNullOrEmpty(config.ParentName) ? "---" : config.ParentName; + bitPositionTextBox.Text = config.BitNr.ToString(); + delayTextBox.Text = config.Delay.ToString(); + modbusAddressTextBox.Text = config.ModbusAddress.ToString(); + formatTextBox.Text = config.SetpFormat; + unitsComboBox.Text = config.SetpUnit.ToString(); + } + + public void Unlock() + { + nameTextBox.Enabled = true; + parentNameComboBox.Enabled = true; + bitPositionTextBox.Enabled = true; + delayTextBox.Enabled = true; + modbusAddressTextBox.Enabled = true; + formatTextBox.Enabled = true; + unitsComboBox.Enabled = true; + } + + 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 (!int.TryParse(bitPositionTextBox.Text, out dummy) || dummy < 0 || dummy >= 128) + { + flags |= CfgUpdateFlags.Error; + message += Environment.NewLine + "'Bit Position' should be between 0 and 127"; + } + if (!int.TryParse(delayTextBox.Text, out dummy) || dummy < 0 || dummy > 300) + { + flags |= CfgUpdateFlags.Error; + message += Environment.NewLine + "'Delay' is not valid"; + } + if (!int.TryParse(modbusAddressTextBox.Text, out dummy) || dummy < 0 || dummy > 254) + { + flags |= CfgUpdateFlags.Error; + message += Environment.NewLine + "'Modbus Address' should be between 0 and 254"; + } + if (!unitsComboBox.Items.Contains(unitsComboBox.Text)) + { + flags |= CfgUpdateFlags.Error; + message += Environment.NewLine + "Invalid 'Units'"; + } + return flags; + } + + public CfgUpdateFlags UpdateCfg() + { + CfgUpdateFlags flags = CfgUpdateFlags.RestartRqrd; + + if (config == null) return CfgUpdateFlags.Error; /// Control was not loaded, settings were not changed + + config.Name = nameTextBox.Text; + config.ParentName = parentNameComboBox.Text.Equals("---") ? string.Empty : parentNameComboBox.Text; + config.BitNr = int.Parse(bitPositionTextBox.Text); + config.Delay = int.Parse(delayTextBox.Text); + config.ModbusAddress = (byte)int.Parse(modbusAddressTextBox.Text); + + config.SetpFormat = formatTextBox.Text; + config.SetpUnit = global::Common.Unit.None; + for (Unit units = 0; units < Unit.Count; units++) + { + if (unitsComboBox.Text == units.ToString()) + { + config.SetpUnit = units; + break; + } + } + + return flags; + } + } +} diff --git a/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.designer.cs b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.designer.cs new file mode 100644 index 000000000..30b7c5312 --- /dev/null +++ b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.designer.cs @@ -0,0 +1,226 @@ +/// +/// Copyright (c) 2020 Sensus Slovensko a.s. +/// +namespace TBF.Rig.Modbus.PumpFM.Grundfoss +{ + partial class PumpCfgCtrl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.parentNameLabel = new System.Windows.Forms.Label(); + this.nameTextBox = new System.Windows.Forms.TextBox(); + this.nameLabel = new System.Windows.Forms.Label(); + this.componentNameLabel = new System.Windows.Forms.Label(); + this.modbusAddressLabel = new System.Windows.Forms.Label(); + this.modbusAddressTextBox = new System.Windows.Forms.TextBox(); + this.parentNameComboBox = new System.Windows.Forms.ComboBox(); + this.bitPositionTextBox = new System.Windows.Forms.TextBox(); + this.bitPositionLabel = new System.Windows.Forms.Label(); + this.delayTextBox = new System.Windows.Forms.TextBox(); + this.delayLabel = new System.Windows.Forms.Label(); + this.unitsComboBox = new System.Windows.Forms.ComboBox(); + this.unitsLabel = new System.Windows.Forms.Label(); + this.formatTextBox = new System.Windows.Forms.TextBox(); + this.formatLabel = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // parentNameLabel + // + this.parentNameLabel.AutoSize = true; + this.parentNameLabel.Location = new System.Drawing.Point(36, 81); + this.parentNameLabel.Name = "parentNameLabel"; + this.parentNameLabel.Size = new System.Drawing.Size(69, 13); + this.parentNameLabel.TabIndex = 3; + this.parentNameLabel.Text = "Parent Name"; + // + // nameTextBox + // + this.nameTextBox.Enabled = false; + this.nameTextBox.Location = new System.Drawing.Point(146, 52); + this.nameTextBox.Name = "nameTextBox"; + this.nameTextBox.Size = new System.Drawing.Size(130, 20); + this.nameTextBox.TabIndex = 2; + // + // nameLabel + // + this.nameLabel.AutoSize = true; + this.nameLabel.Location = new System.Drawing.Point(36, 55); + this.nameLabel.Name = "nameLabel"; + this.nameLabel.Size = new System.Drawing.Size(35, 13); + this.nameLabel.TabIndex = 1; + this.nameLabel.Text = "Name"; + // + // componentNameLabel + // + this.componentNameLabel.AutoSize = true; + this.componentNameLabel.Location = new System.Drawing.Point(143, 28); + this.componentNameLabel.Name = "componentNameLabel"; + this.componentNameLabel.Size = new System.Drawing.Size(83, 13); + this.componentNameLabel.TabIndex = 0; + this.componentNameLabel.Text = "ComonentName"; + // + // modbusAddressLabel + // + this.modbusAddressLabel.AutoSize = true; + this.modbusAddressLabel.Location = new System.Drawing.Point(36, 157); + this.modbusAddressLabel.Name = "modbusAddressLabel"; + this.modbusAddressLabel.Size = new System.Drawing.Size(86, 13); + this.modbusAddressLabel.TabIndex = 7; + this.modbusAddressLabel.Text = "Modbus Address"; + // + // modbusAddressTextBox + // + this.modbusAddressTextBox.Enabled = false; + this.modbusAddressTextBox.Location = new System.Drawing.Point(146, 154); + this.modbusAddressTextBox.Name = "modbusAddressTextBox"; + this.modbusAddressTextBox.Size = new System.Drawing.Size(46, 20); + this.modbusAddressTextBox.TabIndex = 8; + // + // parentNameComboBox + // + this.parentNameComboBox.Enabled = false; + this.parentNameComboBox.FormattingEnabled = true; + this.parentNameComboBox.Location = new System.Drawing.Point(146, 78); + this.parentNameComboBox.Name = "parentNameComboBox"; + this.parentNameComboBox.Size = new System.Drawing.Size(130, 21); + this.parentNameComboBox.TabIndex = 4; + // + // bitPositionTextBox + // + this.bitPositionTextBox.Enabled = false; + this.bitPositionTextBox.Location = new System.Drawing.Point(146, 104); + this.bitPositionTextBox.Name = "bitPositionTextBox"; + this.bitPositionTextBox.Size = new System.Drawing.Size(46, 20); + this.bitPositionTextBox.TabIndex = 22; + // + // bitPositionLabel + // + this.bitPositionLabel.AutoSize = true; + this.bitPositionLabel.Location = new System.Drawing.Point(36, 107); + this.bitPositionLabel.Name = "bitPositionLabel"; + this.bitPositionLabel.Size = new System.Drawing.Size(89, 13); + this.bitPositionLabel.TabIndex = 21; + this.bitPositionLabel.Text = "Valve Bit Position"; + // + // delayTextBox + // + this.delayTextBox.Enabled = false; + this.delayTextBox.Location = new System.Drawing.Point(146, 129); + this.delayTextBox.Name = "delayTextBox"; + this.delayTextBox.Size = new System.Drawing.Size(46, 20); + this.delayTextBox.TabIndex = 24; + // + // delayLabel + // + this.delayLabel.AutoSize = true; + this.delayLabel.Location = new System.Drawing.Point(35, 131); + this.delayLabel.Name = "delayLabel"; + this.delayLabel.Size = new System.Drawing.Size(45, 13); + this.delayLabel.TabIndex = 23; + this.delayLabel.Text = "Delay[s]"; + // + // unitsComboBox + // + this.unitsComboBox.Enabled = false; + this.unitsComboBox.FormattingEnabled = true; + this.unitsComboBox.Location = new System.Drawing.Point(146, 206); + this.unitsComboBox.Name = "unitsComboBox"; + this.unitsComboBox.Size = new System.Drawing.Size(80, 21); + this.unitsComboBox.TabIndex = 28; + // + // unitsLabel + // + this.unitsLabel.AutoSize = true; + this.unitsLabel.Location = new System.Drawing.Point(36, 209); + this.unitsLabel.Name = "unitsLabel"; + this.unitsLabel.Size = new System.Drawing.Size(71, 13); + this.unitsLabel.TabIndex = 27; + this.unitsLabel.Text = "Setpoint units"; + // + // formatTextBox + // + this.formatTextBox.Enabled = false; + this.formatTextBox.Location = new System.Drawing.Point(146, 180); + this.formatTextBox.Name = "formatTextBox"; + this.formatTextBox.Size = new System.Drawing.Size(130, 20); + this.formatTextBox.TabIndex = 26; + // + // formatLabel + // + this.formatLabel.AutoSize = true; + this.formatLabel.Location = new System.Drawing.Point(36, 183); + this.formatLabel.Name = "formatLabel"; + this.formatLabel.Size = new System.Drawing.Size(78, 13); + this.formatLabel.TabIndex = 25; + this.formatLabel.Text = "Setpoint format"; + // + // PumpCfgCtrl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.unitsComboBox); + this.Controls.Add(this.unitsLabel); + this.Controls.Add(this.formatTextBox); + this.Controls.Add(this.formatLabel); + this.Controls.Add(this.delayTextBox); + this.Controls.Add(this.delayLabel); + this.Controls.Add(this.bitPositionTextBox); + this.Controls.Add(this.bitPositionLabel); + this.Controls.Add(this.parentNameComboBox); + this.Controls.Add(this.modbusAddressTextBox); + this.Controls.Add(this.modbusAddressLabel); + this.Controls.Add(this.parentNameLabel); + this.Controls.Add(this.nameTextBox); + this.Controls.Add(this.nameLabel); + this.Controls.Add(this.componentNameLabel); + this.Name = "PumpCfgCtrl"; + this.Size = new System.Drawing.Size(400, 250); + this.Load += new System.EventHandler(this.PressureMeterCfgCtrl_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label parentNameLabel; + private System.Windows.Forms.TextBox nameTextBox; + private System.Windows.Forms.Label nameLabel; + private System.Windows.Forms.Label componentNameLabel; + private System.Windows.Forms.Label modbusAddressLabel; + private System.Windows.Forms.TextBox modbusAddressTextBox; + private System.Windows.Forms.ComboBox parentNameComboBox; + private System.Windows.Forms.TextBox bitPositionTextBox; + private System.Windows.Forms.Label bitPositionLabel; + private System.Windows.Forms.TextBox delayTextBox; + private System.Windows.Forms.Label delayLabel; + private System.Windows.Forms.ComboBox unitsComboBox; + private System.Windows.Forms.Label unitsLabel; + private System.Windows.Forms.TextBox formatTextBox; + private System.Windows.Forms.Label formatLabel; + } +} diff --git a/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.resx b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/TBF/Rig/Modbus/PumpFM/Grundfoss/PumpCfgCtrl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/TBF/Rig/TbfComponents.cs b/TBF/Rig/TbfComponents.cs index ab3001332..5d99ef2f4 100644 --- a/TBF/Rig/TbfComponents.cs +++ b/TBF/Rig/TbfComponents.cs @@ -70,6 +70,7 @@ namespace TBF.Rig new Modbus.PressureMeter.Meret.Factory(), /// Meret pressure meter connected via a modbus on a PC new Modbus.Meret.AdjustableScale.Factory(), /// Adjustable meter connected via a modbus on a PC new Modbus.PumpFM.DanfossVLT.Factory(), /// Pump controlled by Danfoss VLT connected via Modbus + new Modbus.PumpFM.Grundfoss.Factory(), /// Pump controlled by Grundfoss VLT connected via Modbus new Modbus.QuidoRS.Factory(), /// Modbus.QuidoRS new Modbus.TankSelector.Factory(), /// Modbus.TankSelector new Modbus.TempControl.Easytherm.Factory(), diff --git a/TBF/TBF.csproj b/TBF/TBF.csproj index 7d8ac14f6..d369ece6f 100644 --- a/TBF/TBF.csproj +++ b/TBF/TBF.csproj @@ -765,6 +765,15 @@ PumpCfgCtrl.cs + + + + + UserControl + + + PumpCfgCtrl.cs + Form @@ -3212,6 +3221,9 @@ PumpCfgCtrl.cs + + PumpCfgCtrl.cs + ConfirmationForm.cs diff --git a/TBF/UI/Process/ProcessTabPageCtrl.cs b/TBF/UI/Process/ProcessTabPageCtrl.cs index aeb391a81..a7e609ea3 100644 --- a/TBF/UI/Process/ProcessTabPageCtrl.cs +++ b/TBF/UI/Process/ProcessTabPageCtrl.cs @@ -370,6 +370,7 @@ namespace TBF.UI.Process /// water Rig.Various.Drawing.Tank.Configuration tankCfg; /// | Rig.Modbus.PumpFM.DanfossVLT.PumpCfg pumpCfg; /// | setpoint, route based (initially bit nr. = 124) + //Rig.Modbus.PumpFM.Grundfoss.PumpCfg pump2Cfg; /// | setpoint, route based (initially bit nr. = 124)// Rig.Modbus.TempMeter.Groch.TempMeterCfg tempInCfg; /// | measured value Rig.Modbus.PressureMeter.Meret.PressureMeterCfg pressUpCfg; /// | measured value Rig.Modbus.Meret.AdjustableScale.AdjustableMeterCfg electricUpCfg; /// | measured value @@ -438,6 +439,31 @@ namespace TBF.UI.Process Inverted = false, }); + /*/// Pump + pumpSetpIx = setpIx++; + processDrawingCtrl.AddItem( + pump2Cfg = new Rig.Modbus.PumpFM.Grundfoss.PumpCfg(new Rig.Modbus.PumpFM.Grundfoss.Factory()) + { + Id = pumpId = id++, + Name = "P Grundfoss", + X = 450, + Y = 480, + Shape = Shape.PumpFM, + Sz = Sz.L, + Orient = Orient.L, + Flip = false, + LblX = 0, + LblY = -46, + LblOrient = Orient.R, + SetpX = 0, + SetpY = -24, + SetpOrient = Orient.R, + SetpFormat = "{0:F0} %", + SetpUnit = Unit.Pct, + BitNr = DfltPumpBitNr, + Inverted = false, + });*/ + /// Junctions processDrawingCtrl.AddItem(new Configuration("J1", new Factory()) { Id=j1=id++, Shape=Shape.Junction, X=50, Y=490, Sz=Sz.L, }); processDrawingCtrl.AddItem(new Configuration("J2", new Factory()) { Id=j2=id++, Shape=Shape.Junction, X=50, Y=370, Sz=Sz.L, }); @@ -856,6 +882,12 @@ namespace TBF.UI.Process pumpCfg.BitNr = (args.FeedingPath.Pump.Cfg as Rig.Modbus.PumpFM.DanfossVLT.PumpCfg).BitNr; pumpCfg.Inverted = (args.FeedingPath.Pump.Cfg as Rig.Modbus.PumpFM.DanfossVLT.PumpCfg).Inverted; } + else if (args.FeedingPath != null && args.FeedingPath.Pump != null && + args.FeedingPath.Pump.Cfg is Rig.Modbus.PumpFM.Grundfoss.PumpCfg) + { + pumpCfg.BitNr = (args.FeedingPath.Pump.Cfg as Rig.Modbus.PumpFM.Grundfoss.PumpCfg).BitNr; + pumpCfg.Inverted = (args.FeedingPath.Pump.Cfg as Rig.Modbus.PumpFM.Grundfoss.PumpCfg).Inverted; + } else { pumpCfg.BitNr = DfltPumpBitNr;