358 lines
13 KiB
C#
358 lines
13 KiB
C#
///
|
|
/// Copyright (c) 2017 Sensus Metering Systems
|
|
///
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using log4net;
|
|
using TBF.BenchControl.Generic;
|
|
using TBF.BenchControl.GenericDevices;
|
|
using TBF.Boxes;
|
|
using TBF.Resources;
|
|
|
|
namespace TBF.BenchControl.Modbus.TankSelector
|
|
{
|
|
/// <summary> Enumeration of water tanks </summary>
|
|
public enum TankId
|
|
{
|
|
None,
|
|
Cold, /// Cold water tank
|
|
Warm, /// Warm water tank
|
|
Hot, /// Hot water tank
|
|
}
|
|
|
|
/// <summary> Enumeration of water tank activities</summary>
|
|
public enum Activity
|
|
{
|
|
Unknown,
|
|
Idle, /// Main tank is not interconnected with any other water tank
|
|
Selected_Waiting1min, /// Selected water tank and main tank are interconnected, water is flowing to the main tank
|
|
Selected, /// Selected water tank and main tank are interconnected
|
|
Unselected_WaitingLevel, /// Water is being pumped from the main tank to the (un)selected water tank until main tank is empty
|
|
}
|
|
|
|
/// <summary> TankSelector state </summary>
|
|
public enum State
|
|
{
|
|
Unknown, /// Initial state after the program startup
|
|
NoTankSelected,
|
|
ColdTankSelected_Waiting1min,
|
|
ColdTankSelected,
|
|
ColdTankUnselected_WaitingLevel,
|
|
WarmTankSelected_Waiting1min,
|
|
WarmTankSelected,
|
|
WarmTankUnselected_WaitingLevel,
|
|
HotTankSelected_Waiting1min,
|
|
HotTankSelected,
|
|
HotTankUnselected_WaitingLevel,
|
|
}
|
|
|
|
/// <summary> Quido digital out board outputs assignment </summary>
|
|
public enum QuidoOutputs : ushort
|
|
{
|
|
None = 0,
|
|
SelectColdTank = 0x0001, /// Quido output 1
|
|
SelectWarmTank = 0x0002, /// Quido output 2
|
|
SelectHotTank = 0x0004, /// Quido output 3
|
|
PumpToColdTank = 0x0008, /// Quido output 4
|
|
PumpToWarmTank = 0x0010, /// Quido output 5
|
|
PumpToHotTank = 0x0020, /// Quido output 6
|
|
Mixing = 0x0040, /// Quido output 7
|
|
HeatingHotTank = 0x0080, /// Quido output 8
|
|
HeatingWarmTank = 0x0100, /// Quido output 9
|
|
CoolingHotTank = 0x0200, /// Quido output 10
|
|
CoolingWarmTank = 0x0400, /// Quido output 11
|
|
CoolingColdTank = 0x0800, /// Quido output 12
|
|
}
|
|
|
|
/// <summary> Related control board digital inputs </summary>
|
|
public enum DigitalInputs : ulong
|
|
{
|
|
HotTankSelected = (ulong)(1 << 18),
|
|
WarmTankSelected = (ulong)(1 << 19),
|
|
ColdTankSelected = (ulong)(1 << 20),
|
|
Pumping_WaitingLevel = (ulong)(1 << 21),
|
|
}
|
|
|
|
|
|
public class TankSelector : ComponentBase, ISequenceCondition, IDevice
|
|
{
|
|
private static readonly ILog log = LogManager.GetLogger(typeof(TankSelector));
|
|
public override string ToString() { return string.Format("TankSelector({0})", Cfg.ToString(1)); }
|
|
|
|
readonly TankSelectorCfg tankSelectorCfg;
|
|
|
|
readonly TBF.BenchControl.Modbus.QuidoRS.QuidoRS quidoRS;
|
|
|
|
readonly TBF.BenchControl.Modbus.Easytherm.Easytherm hotHeating;
|
|
readonly TBF.BenchControl.Modbus.Easytherm.Easytherm hotCooling;
|
|
readonly TBF.BenchControl.Modbus.Easytherm.Easytherm warmHeating;
|
|
readonly TBF.BenchControl.Modbus.Easytherm.Easytherm warmCooling;
|
|
readonly TBF.BenchControl.Modbus.Easytherm.Easytherm coldCooling;
|
|
|
|
|
|
public State State;
|
|
public TankId CurrentTank { get { return GetTankIdFromState(State); } }
|
|
public Activity CurrentActivity { get { return GetActivityFromState(State); } }
|
|
|
|
ulong latchedDigitalInputs;
|
|
ulong lastDigitalInputs;
|
|
ulong lastValueRepetitions;
|
|
|
|
|
|
public TankId GetTankIdFromState(State state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case State.ColdTankSelected_Waiting1min:
|
|
case State.ColdTankSelected:
|
|
case State.ColdTankUnselected_WaitingLevel:
|
|
return TankId.Cold;
|
|
|
|
case State.WarmTankSelected_Waiting1min:
|
|
case State.WarmTankSelected:
|
|
case State.WarmTankUnselected_WaitingLevel:
|
|
return TankId.Warm;
|
|
|
|
case State.HotTankSelected_Waiting1min:
|
|
case State.HotTankSelected:
|
|
case State.HotTankUnselected_WaitingLevel:
|
|
return TankId.Hot;
|
|
|
|
default:
|
|
return TankId.None;
|
|
}
|
|
}
|
|
|
|
public Activity GetActivityFromState(State state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case State.NoTankSelected:
|
|
return Activity.Idle;
|
|
|
|
case State.ColdTankSelected_Waiting1min:
|
|
case State.WarmTankSelected_Waiting1min:
|
|
case State.HotTankSelected_Waiting1min:
|
|
return Activity.Selected_Waiting1min;
|
|
|
|
case State.ColdTankSelected:
|
|
case State.WarmTankSelected:
|
|
case State.HotTankSelected:
|
|
return Activity.Selected;
|
|
|
|
case State.ColdTankUnselected_WaitingLevel:
|
|
case State.WarmTankUnselected_WaitingLevel:
|
|
case State.HotTankUnselected_WaitingLevel:
|
|
return Activity.Unselected_WaitingLevel;
|
|
|
|
default:
|
|
return Activity.Unknown;
|
|
}
|
|
}
|
|
|
|
|
|
public TankSelector()
|
|
{
|
|
CreateConditions(false);
|
|
}
|
|
|
|
public TankSelector(Generic.IComponentCfg cfg, IList<Generic.IComponent> components)
|
|
: base(cfg)
|
|
{
|
|
tankSelectorCfg = cfg as TankSelectorCfg;
|
|
|
|
quidoRS = (TBF.BenchControl.Modbus.QuidoRS.QuidoRS)TbfComponents.FindComponent(cfg.ParentName, components);
|
|
if (quidoRS == null) throw new Exception(string.Format("Cannot find parent '{0}' of the component {1}", cfg.ParentName, Name));
|
|
|
|
hotHeating = (TBF.BenchControl.Modbus.Easytherm.Easytherm)TbfComponents.FindComponent(tankSelectorCfg.HotWaterHeating, components);
|
|
hotCooling = (TBF.BenchControl.Modbus.Easytherm.Easytherm)TbfComponents.FindComponent(tankSelectorCfg.HotWaterCooling, components);
|
|
warmHeating = (TBF.BenchControl.Modbus.Easytherm.Easytherm)TbfComponents.FindComponent(tankSelectorCfg.WarmWaterHeating, components);
|
|
warmCooling = (TBF.BenchControl.Modbus.Easytherm.Easytherm)TbfComponents.FindComponent(tankSelectorCfg.WarmWaterCooling, components);
|
|
coldCooling = (TBF.BenchControl.Modbus.Easytherm.Easytherm)TbfComponents.FindComponent(tankSelectorCfg.ColdWaterCooling, components);
|
|
|
|
if (hotHeating == null) throw new Exception(string.Format("Component '{0}' cannot find temperature controller '{1}'", Name, tankSelectorCfg.HotWaterHeating));
|
|
if (hotCooling == null) throw new Exception(string.Format("Component '{0}' cannot find temperature controller '{1}'", Name, tankSelectorCfg.HotWaterCooling));
|
|
if (warmHeating == null) throw new Exception(string.Format("Component '{0}' cannot find temperature controller '{1}'", Name, tankSelectorCfg.WarmWaterHeating));
|
|
if (warmCooling == null) throw new Exception(string.Format("Component '{0}' cannot find temperature controller '{1}'", Name, tankSelectorCfg.WarmWaterCooling));
|
|
if (coldCooling == null) throw new Exception(string.Format("Component '{0}' cannot find temperature controller '{1}'", Name, tankSelectorCfg.ColdWaterCooling));
|
|
|
|
CreateConditions(true);
|
|
|
|
State = State.Unknown;
|
|
|
|
log.Fatal(this.ToString());
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recover the TankSelector state from ContolBoard.DigitalInputs
|
|
/// </summary>
|
|
void RecoverState()
|
|
{
|
|
latchedDigitalInputs = StateMachine.ControlBoard.DigitalInputs;
|
|
if ((latchedDigitalInputs & (ulong)DigitalInputs.ColdTankSelected) != 0)
|
|
{
|
|
if ((latchedDigitalInputs & (ulong)DigitalInputs.Pumping_WaitingLevel) == 0)
|
|
State = State.ColdTankSelected;
|
|
else
|
|
State = State.ColdTankUnselected_WaitingLevel;
|
|
}
|
|
else if ((latchedDigitalInputs & (ulong)DigitalInputs.WarmTankSelected) != 0)
|
|
{
|
|
if ((latchedDigitalInputs & (ulong)DigitalInputs.Pumping_WaitingLevel) == 0)
|
|
State = State.WarmTankSelected;
|
|
else
|
|
State = State.WarmTankUnselected_WaitingLevel;
|
|
}
|
|
else if ((latchedDigitalInputs & (ulong)DigitalInputs.HotTankSelected) != 0)
|
|
{
|
|
if ((latchedDigitalInputs & (ulong)DigitalInputs.Pumping_WaitingLevel) == 0)
|
|
State = State.HotTankSelected;
|
|
else
|
|
State = State.HotTankUnselected_WaitingLevel;
|
|
}
|
|
else
|
|
{
|
|
State = State.NoTankSelected;
|
|
}
|
|
|
|
log.FatalFormat("{0} successfully initialized, DigiInputs={1}, initial state={2}",
|
|
Name, latchedDigitalInputs.ToString("X16"), State);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Detect 'State.NoTankSelected' from ContolBoard.DigitalInputs
|
|
/// </summary>
|
|
void DetectNoTankSelected()
|
|
{
|
|
ulong digiIn = StateMachine.ControlBoard.DigitalInputs;
|
|
|
|
if (digiIn == latchedDigitalInputs)
|
|
{
|
|
return;
|
|
}
|
|
else if (digiIn == lastDigitalInputs)
|
|
{
|
|
if (++lastValueRepetitions == 3)
|
|
{
|
|
latchedDigitalInputs = lastDigitalInputs;
|
|
if ((latchedDigitalInputs & (ulong)DigitalInputs.ColdTankSelected) == 0 &&
|
|
(latchedDigitalInputs & (ulong)DigitalInputs.WarmTankSelected) == 0 &&
|
|
(latchedDigitalInputs & (ulong)DigitalInputs.HotTankSelected) == 0 &&
|
|
(latchedDigitalInputs & (ulong)DigitalInputs.Pumping_WaitingLevel) == 0)
|
|
{
|
|
State = State.NoTankSelected;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lastDigitalInputs = digiIn; /// new value stored
|
|
lastValueRepetitions = 1; /// one repetition so far
|
|
}
|
|
}
|
|
|
|
|
|
///
|
|
/// IDevice interface implementation
|
|
///
|
|
public void Initialize()
|
|
{
|
|
SetOutputs(0); /// Reset all Quido outputs
|
|
}
|
|
public void RunDeviceBefore()
|
|
{
|
|
if (StateMachine.Time == 3)
|
|
{
|
|
RecoverState();
|
|
}
|
|
else if (StateMachine.Time > 3)
|
|
{
|
|
DetectNoTankSelected();
|
|
}
|
|
}
|
|
public void RunDeviceAfter() {}
|
|
public void StopDevice() {}
|
|
|
|
|
|
|
|
///
|
|
/// Quido wrappers
|
|
///
|
|
public void SetOutputs(ushort outputs) { quidoRS.SetOutputs(outputs); }
|
|
public void SetBit(ushort bitMask) { quidoRS.SetBit(bitMask); }
|
|
public void ResetBit(ushort bitMask) { quidoRS.ResetBit(bitMask); }
|
|
|
|
|
|
|
|
///
|
|
/// ISequenceCondition interface implementation (conditions in transition sequences)
|
|
///
|
|
IList<string> sequenceConditionNames;
|
|
IList<IOperation> sequenceConditions;
|
|
|
|
public int ConditionsCount { get { return sequenceConditions != null ? sequenceConditions.Count : 0; } }
|
|
|
|
/// Strings are added to the combo-box for transition sequence condition selection
|
|
public string ConditionName(int i)
|
|
{
|
|
if (sequenceConditionNames != null && i < sequenceConditionNames.Count && i >= 0)
|
|
return sequenceConditionNames[i];
|
|
else
|
|
return string.Empty;
|
|
}
|
|
|
|
/// Operations are executed as a part of a transition sequence
|
|
public IOperation ConditionOp(int i)
|
|
{
|
|
if (sequenceConditions != null && i < sequenceConditions.Count && i >= 0)
|
|
return sequenceConditions[i];
|
|
else
|
|
return null;
|
|
}
|
|
|
|
void ClearConditions()
|
|
{
|
|
sequenceConditionNames = new List<string>();
|
|
sequenceConditions = new List<IOperation>();
|
|
}
|
|
|
|
void AddCondition(string conditionName, bool createOperation, IOperation conditionOperation)
|
|
{
|
|
sequenceConditionNames.Add(conditionName);
|
|
if (createOperation) sequenceConditions.Add(conditionOperation);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a list of conditions
|
|
/// </summary>
|
|
/// <param name="createOps">true = create names and condition operations, false = create names only</param>
|
|
void CreateConditions(bool createOps)
|
|
{
|
|
ClearConditions();
|
|
AddCondition(Strings.Select_COLD_tank, createOps, new SelectTankOp(this, TankId.Cold));
|
|
AddCondition(Strings.Select_WARM_tank, createOps, new SelectTankOp(this, TankId.Warm));
|
|
AddCondition(Strings.Select_HOT_tank, createOps, new SelectTankOp(this, TankId.Hot));
|
|
AddCondition(Strings.Unselect_current_tank, createOps, new UnselectTankOp(this));
|
|
AddCondition(Strings.Set_hot_water_heating_temperature, createOps, hotHeating.SetRequiredTemperatureOp());
|
|
AddCondition(Strings.Set_hot_water_cooling_temperature, createOps, hotCooling.SetRequiredTemperatureOp());
|
|
AddCondition(Strings.Set_warm_water_heating_temperature, createOps, warmHeating.SetRequiredTemperatureOp());
|
|
AddCondition(Strings.Set_warm_water_cooling_temperature, createOps, warmCooling.SetRequiredTemperatureOp());
|
|
AddCondition(Strings.Set_cold_water_cooling_temperature, createOps, coldCooling.SetRequiredTemperatureOp());
|
|
AddCondition("Start heating hot water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.HeatingHotTank, true));
|
|
AddCondition("Stop heating hot water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.HeatingHotTank, false));
|
|
AddCondition("Start cooling hot water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.CoolingHotTank, true));
|
|
AddCondition("Stop cooling hot water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.CoolingHotTank, false));
|
|
AddCondition("Start heating warm water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.HeatingWarmTank, true));
|
|
AddCondition("Stop heating warm water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.HeatingWarmTank, false));
|
|
AddCondition("Start cooling warm water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.CoolingWarmTank, true));
|
|
AddCondition("Stop cooling warm water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.CoolingWarmTank, false));
|
|
AddCondition("Start cooling cold water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.CoolingColdTank, true));
|
|
AddCondition("Stop cooling cold water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.CoolingColdTank, false));
|
|
AddCondition("Start mixing water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.Mixing, true));
|
|
AddCondition("Stop mixing water", createOps, new SetQuidoBitOp(quidoRS, QuidoOutputs.Mixing, false));
|
|
}
|
|
}
|
|
}
|