tbf/TestBenchFramework/BenchControl/Modbus/TankSelector/TankSelector.cs
2017-03-27 23:05:02 +02:00

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));
}
}
}